Commit c9e2bc06833 for php.net

commit c9e2bc0683306574c2c18d9f2055782918d5657e
Author: Weilin Du <weilindu@php.net>
Date:   Fri Sep 18 10:45:09 2026 +0800

    ext/standard: Optimize & Refactor pathinfo() (#23726)

    - Reuse the basename string for PATHINFO_BASENAME
    and extensionless PATHINFO_FILENAME results.
    - Share the last-dot lookup for extension and filename
    when returning PATHINFO_ALL.
    - (From my perspective) cleaner code.

diff --git a/UPGRADING b/UPGRADING
index 835e4886887..8762c7602ae 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -1125,6 +1125,7 @@ PHP 8.6 UPGRADE NOTES
   . Improved performance of array_unshift().
   . Improved performance of array_walk().
   . Improved performance of intval('+0b...', 2) and intval('0b...', 2).
+  . Improved performance of pathinfo() when requesting a single component.
   . Improved performance of str_split().
   . Improved performance of str_pad().
   . Improved performance of str_repeat() when the multiplier is 1.
diff --git a/ext/standard/string.c b/ext/standard/string.c
index 2c5990a6a8c..441890ea290 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -1637,12 +1637,9 @@ ZEND_FRAMELESS_FUNCTION(dirname, 2)
 /* {{{ Returns information about a certain string */
 PHP_FUNCTION(pathinfo)
 {
-	zval tmp;
-	char *path, *dirname;
+	char *path;
 	size_t path_len;
-	bool have_basename;
 	zend_long opt = PHP_PATHINFO_ALL;
-	zend_string *ret = NULL;

 	ZEND_PARSE_PARAMETERS_START(1, 2)
 		Z_PARAM_STRING(path, path_len)
@@ -1660,70 +1657,54 @@ PHP_FUNCTION(pathinfo)
 		RETURN_THROWS();
 	}

-	have_basename = (opt & PHP_PATHINFO_BASENAME);
-
-	array_init(&tmp);
-
-	if (opt & PHP_PATHINFO_DIRNAME) {
-		dirname = estrndup(path, path_len);
+	if (opt == PHP_PATHINFO_DIRNAME) {
+		char *dirname = estrndup(path, path_len);
 		php_dirname(dirname, path_len);
-		if (*dirname) {
-			add_assoc_string(&tmp, "dirname", dirname);
-		}
+		RETVAL_STRING_FAST(dirname);
 		efree(dirname);
+		return;
 	}

-	if (have_basename) {
-		ret = php_basename(path, path_len, NULL, 0);
-		add_assoc_str(&tmp, "basename", zend_string_copy(ret));
+	zend_string *basename = php_basename(path, path_len, NULL, 0);
+	if (opt == PHP_PATHINFO_BASENAME) {
+		RETURN_STR(basename);
 	}

-	if (opt & PHP_PATHINFO_EXTENSION) {
-		const char *p;
-		ptrdiff_t idx;
-
-		if (!have_basename) {
-			ret = php_basename(path, path_len, NULL, 0);
-		}
-
-		p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
-
+	const char *p = zend_memrchr(ZSTR_VAL(basename), '.', ZSTR_LEN(basename));
+	size_t extension_len = p ? ZSTR_LEN(basename) - (p - ZSTR_VAL(basename)) - 1 : 0;
+	if (opt == PHP_PATHINFO_EXTENSION) {
 		if (p) {
-			idx = p - ZSTR_VAL(ret);
-			add_assoc_stringl(&tmp, "extension", ZSTR_VAL(ret) + idx + 1, ZSTR_LEN(ret) - idx - 1);
+			RETVAL_STRINGL(p + 1, extension_len);
+		} else {
+			RETVAL_EMPTY_STRING();
 		}
+		zend_string_release_ex(basename, 0);
+		return;
 	}

-	if (opt & PHP_PATHINFO_FILENAME) {
-		const char *p;
-		ptrdiff_t idx;
-
-		/* Have we already looked up the basename? */
-		if (!have_basename && !ret) {
-			ret = php_basename(path, path_len, NULL, 0);
-		}
-
-		p = zend_memrchr(ZSTR_VAL(ret), '.', ZSTR_LEN(ret));
-
-		idx = p ? (p - ZSTR_VAL(ret)) : (ptrdiff_t)ZSTR_LEN(ret);
-		add_assoc_stringl(&tmp, "filename", ZSTR_VAL(ret), idx);
+	zend_string *filename = p
+		? zend_string_init(ZSTR_VAL(basename), p - ZSTR_VAL(basename), 0)
+		: zend_string_copy(basename);
+	if (opt == PHP_PATHINFO_FILENAME) {
+		zend_string_release_ex(basename, 0);
+		RETURN_STR(filename);
 	}

-	if (ret) {
-		zend_string_release_ex(ret, 0);
+	ZEND_ASSERT(opt == PHP_PATHINFO_ALL);
+	array_init(return_value);
+
+	char *dirname = estrndup(path, path_len);
+	php_dirname(dirname, path_len);
+	if (*dirname) {
+		add_assoc_string(return_value, "dirname", dirname);
 	}
+	efree(dirname);

-	if (opt == PHP_PATHINFO_ALL) {
-		RETURN_COPY_VALUE(&tmp);
-	} else {
-		zval *element;
-		if ((element = zend_hash_get_current_data(Z_ARRVAL(tmp))) != NULL) {
-			RETVAL_COPY_DEREF(element);
-		} else {
-			RETVAL_EMPTY_STRING();
-		}
-		zval_ptr_dtor(&tmp);
+	add_assoc_str(return_value, "basename", basename);
+	if (p) {
+		add_assoc_stringl(return_value, "extension", p + 1, extension_len);
 	}
+	add_assoc_str(return_value, "filename", filename);
 }
 /* }}} */