Commit 00f4cd6b800 for php.net

commit 00f4cd6b800872c6220257f9463f683578cfb624
Author: Weilin Du <weilindu@php.net>
Date:   Sat Jul 25 01:06:29 2026 +0800

    Refactor various raw C-string concatenation to use existing API (#22879)

    In #21607 we introduce several inline helpers for raw C-string concatenation.
    This PR use them to refactor several manual C string construction.

diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 0a7a62eae9a..6f7cc8a6552 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -1943,10 +1943,10 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
 					/* ext/phar has to load phar's metadata into memory */
 					if (persistent_script->is_phar) {
 						php_stream_statbuf ssb;
-						char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
-
-						memcpy(fname, "phar://", sizeof("phar://") - 1);
-						memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
+						char *fname = zend_cstr_concat(
+							"phar://", sizeof("phar://") - 1,
+							ZSTR_VAL(persistent_script->script.filename),
+							ZSTR_LEN(persistent_script->script.filename));
 						php_stream_stat_path(fname, &ssb);
 						efree(fname);
 					}
@@ -2457,10 +2457,10 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
 					/* ext/phar has to load phar's metadata into memory */
 					if (persistent_script->is_phar) {
 						php_stream_statbuf ssb;
-						char *fname = emalloc(sizeof("phar://") + ZSTR_LEN(persistent_script->script.filename));
-
-						memcpy(fname, "phar://", sizeof("phar://") - 1);
-						memcpy(fname + sizeof("phar://") - 1, ZSTR_VAL(persistent_script->script.filename), ZSTR_LEN(persistent_script->script.filename) + 1);
+						char *fname = zend_cstr_concat(
+							"phar://", sizeof("phar://") - 1,
+							ZSTR_VAL(persistent_script->script.filename),
+							ZSTR_LEN(persistent_script->script.filename));
 						php_stream_stat_path(fname, &ssb);
 						efree(fname);
 					}
diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c
index 794e75a1716..265b47e52dc 100644
--- a/ext/pcntl/pcntl.c
+++ b/ext/pcntl/pcntl.c
@@ -746,13 +746,10 @@ PHP_FUNCTION(pcntl_exec)
 				zend_string_addref(key);
 			}

-			/* Length of element + equal sign + length of key + null */
-			*pair = safe_emalloc(ZSTR_LEN(element_str) + 1, sizeof(char), ZSTR_LEN(key) + 1);
-			/* Copy key=element + final null byte into buffer */
-			memcpy(*pair, ZSTR_VAL(key), ZSTR_LEN(key));
-			(*pair)[ZSTR_LEN(key)] = '=';
-			/* Copy null byte */
-			memcpy(*pair + ZSTR_LEN(key) + 1, ZSTR_VAL(element_str), ZSTR_LEN(element_str) + 1);
+			*pair = zend_cstr_concat3(
+				ZSTR_VAL(key), ZSTR_LEN(key),
+				"=", 1,
+				ZSTR_VAL(element_str), ZSTR_LEN(element_str));

 			/* Cleanup */
 			zend_string_release_ex(key, false);
diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c
index 8f5e22640cc..52aae986dee 100644
--- a/ext/pdo_pgsql/pgsql_driver.c
+++ b/ext/pdo_pgsql/pgsql_driver.c
@@ -392,11 +392,10 @@ static zend_string* pgsql_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquo
 				return NULL;
 			}
 			quotedlen = tmp_len + 1;
-			quoted = emalloc(quotedlen + 1);
-			memcpy(quoted+1, escaped, quotedlen-2);
-			quoted[0] = '\'';
-			quoted[quotedlen-1] = '\'';
-			quoted[quotedlen] = '\0';
+			quoted = zend_cstr_concat3(
+				"'", 1,
+				(const char *) escaped, quotedlen - 2,
+				"'", 1);
 			PQfreemem(escaped);
 			break;
 		default:
diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index 0ea766c12b4..87ccbe3424a 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -3433,10 +3433,8 @@ static zend_result pgsql_copy_from_query(PGconn *pgsql, PGresult *pgsql_result,

 	int result;
 	if (ZSTR_LEN(tmp) > 0 && ZSTR_VAL(tmp)[ZSTR_LEN(tmp) - 1] != '\n') {
-		char *zquery = emalloc(ZSTR_LEN(tmp) + 2);
-		memcpy(zquery, ZSTR_VAL(tmp), ZSTR_LEN(tmp));
-		zquery[ZSTR_LEN(tmp)] = '\n';
-		zquery[ZSTR_LEN(tmp) + 1] = '\0';
+		char *zquery = zend_cstr_append_char(
+			ZSTR_VAL(tmp), ZSTR_LEN(tmp), '\n');
 		result = PQputCopyData(pgsql, zquery, ZSTR_LEN(tmp) + 1);
 		efree(zquery);
 	} else {
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index 0139f0889a8..8a330a954b8 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -633,9 +633,9 @@ PHP_METHOD(Phar, webPhar)
 				IS_STRING == Z_TYPE_P(z_path_info)) {
 				entry_len = Z_STRLEN_P(z_path_info);
 				entry = estrndup(Z_STRVAL_P(z_path_info), entry_len);
-				path_info = emalloc(Z_STRLEN_P(z_script_name) + entry_len + 1);
-				memcpy(path_info, Z_STRVAL_P(z_script_name), Z_STRLEN_P(z_script_name));
-				memcpy(path_info + Z_STRLEN_P(z_script_name), entry, entry_len + 1);
+				path_info = zend_cstr_concat(
+					Z_STRVAL_P(z_script_name), Z_STRLEN_P(z_script_name),
+					entry, entry_len);
 				free_pathinfo = 1;
 			} else {
 				entry_len = 0;
diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c
index 73886af5a56..77d73a2326d 100644
--- a/ext/soap/php_sdl.c
+++ b/ext/soap/php_sdl.c
@@ -85,12 +85,10 @@ static sdlTypePtr get_element(sdlPtr sdl, xmlNodePtr node, const xmlChar *type)
 			size_t ns_len = xmlStrlen(nsptr->href);
 			size_t type_len = strlen(cptype);
 			size_t len = ns_len + type_len + 1;
-			char *nscat = emalloc(len + 1);
-
-			memcpy(nscat, nsptr->href, ns_len);
-			nscat[ns_len] = ':';
-			memcpy(nscat+ns_len+1, cptype, type_len);
-			nscat[len] = '\0';
+			char *nscat = zend_cstr_concat3(
+				(const char *) nsptr->href, ns_len,
+				":", 1,
+				cptype, type_len);

 			if ((sdl_type = zend_hash_str_find_ptr(sdl->elements, nscat, len)) != NULL) {
 				ret = sdl_type;
@@ -117,13 +115,10 @@ encodePtr get_encoder(sdlPtr sdl, const char *ns, const char *type)
 	size_t type_len = strlen(type);
 	size_t len = ns_len + type_len + 1;

-	nscat = emalloc(len + 1);
-	if (ns) {
-		memcpy(nscat, ns, ns_len);
-	}
-	nscat[ns_len] = ':';
-	memcpy(nscat+ns_len+1, type, type_len);
-	nscat[len] = '\0';
+	nscat = zend_cstr_concat3(
+		ns, ns_len,
+		":", 1,
+		type, type_len);

 	enc = get_encoder_ex(sdl, nscat, len);

@@ -138,11 +133,10 @@ encodePtr get_encoder(sdlPtr sdl, const char *ns, const char *type)

 		enc_ns_len = sizeof(XSD_NAMESPACE)-1;
 		enc_len = enc_ns_len + type_len + 1;
-		enc_nscat = emalloc(enc_len + 1);
-		memcpy(enc_nscat, XSD_NAMESPACE, sizeof(XSD_NAMESPACE)-1);
-		enc_nscat[enc_ns_len] = ':';
-		memcpy(enc_nscat+enc_ns_len+1, type, type_len);
-		enc_nscat[enc_len] = '\0';
+		enc_nscat = zend_cstr_concat3(
+			XSD_NAMESPACE, enc_ns_len,
+			":", 1,
+			type, type_len);

 		enc = get_encoder_ex(NULL, enc_nscat, enc_len);
 		efree(enc_nscat);
@@ -1407,11 +1401,10 @@ static void sdl_deserialize_encoder(encodePtr enc, sdlTypePtr *types, char **in)

 			enc_ns_len = sizeof(XSD_NAMESPACE)-1;
 			enc_len = enc_ns_len + type_len + 1;
-			enc_nscat = emalloc(enc_len + 1);
-			memcpy(enc_nscat, XSD_NAMESPACE, sizeof(XSD_NAMESPACE)-1);
-			enc_nscat[enc_ns_len] = ':';
-			memcpy(enc_nscat+enc_ns_len+1, enc->details.type_str, type_len);
-			enc_nscat[enc_len] = '\0';
+			enc_nscat = zend_cstr_concat3(
+				XSD_NAMESPACE, enc_ns_len,
+				":", 1,
+				enc->details.type_str, type_len);

 			real_enc = get_encoder_ex(NULL, enc_nscat, enc_len);
 			efree(enc_nscat);
diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c
index e1a8c1868b9..79d45b76396 100644
--- a/ext/standard/fsock.c
+++ b/ext/standard/fsock.c
@@ -29,17 +29,10 @@ static size_t php_fsockopen_format_host_port(char **message, const char *prefix,
     int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port);
     size_t total_len = prefix_len + host_len + portlen;

-    char *result = emalloc(total_len + 1);
-
-	if (prefix_len > 0) {
-    	memcpy(result, prefix, prefix_len);
-	}
-    memcpy(result + prefix_len, host, host_len);
-    memcpy(result + prefix_len + host_len, portbuf, portlen);
-
-    result[total_len] = '\0';
-
-    *message = result;
+    *message = zend_cstr_concat3(
+        prefix, prefix_len,
+        host, host_len,
+        portbuf, portlen);

 	return total_len;
 }
diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c
index a41007b7aa8..33b0dadb900 100644
--- a/ext/zip/php_zip.c
+++ b/ext/zip/php_zip.c
@@ -1765,10 +1765,7 @@ PHP_METHOD(ZipArchive, addEmptyDir)
 	}

 	if (dirname[dirname_len-1] != '/') {
-		s=(char *)safe_emalloc(dirname_len, 1, 2);
-		strcpy(s, dirname);
-		s[dirname_len] = '/';
-		s[dirname_len+1] = '\0';
+		s = zend_cstr_append_char(dirname, dirname_len, '/');
 	} else {
 		s = dirname;
 	}
diff --git a/main/SAPI.c b/main/SAPI.c
index 62e1c89e4bb..58e85539d54 100644
--- a/main/SAPI.c
+++ b/main/SAPI.c
@@ -369,10 +369,10 @@ SAPI_API size_t sapi_apply_default_charset(char **mimetype, size_t len)
 	if (*mimetype != NULL) {
 		if (*charset && strncmp(*mimetype, "text/", 5) == 0 && strstr(*mimetype, "charset=") == NULL) {
 			newlen = len + (sizeof(";charset=")-1) + strlen(charset);
-			newtype = emalloc(newlen + 1);
-	 		PHP_STRLCPY(newtype, *mimetype, newlen + 1, len);
-			strlcat(newtype, ";charset=", newlen + 1);
-			strlcat(newtype, charset, newlen + 1);
+			newtype = zend_cstr_concat3(
+				*mimetype, len,
+				";charset=", sizeof(";charset=")-1,
+				charset, strlen(charset));
 			efree(*mimetype);
 			*mimetype = newtype;
 			return newlen;
@@ -878,10 +878,9 @@ SAPI_API int sapi_send_headers(void)
 			SG(sapi_headers).mimetype = default_mimetype;

 			default_header.header_len = sizeof("Content-type: ") - 1 + len;
-			default_header.header = emalloc(default_header.header_len + 1);
-
-			memcpy(default_header.header, "Content-type: ", sizeof("Content-type: ") - 1);
-			memcpy(default_header.header + sizeof("Content-type: ") - 1, SG(sapi_headers).mimetype, len + 1);
+			default_header.header = zend_cstr_concat(
+				"Content-type: ", sizeof("Content-type: ") - 1,
+				SG(sapi_headers).mimetype, len);

 			sapi_header_add_op(SAPI_HEADER_ADD, &default_header);
 		} else {
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
index 3a8435f9e6c..82722538748 100644
--- a/sapi/fpm/fpm/fpm_main.c
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -568,15 +568,10 @@ static void sapi_cgi_register_variables(zval *track_vars_array) /* {{{ */
 		unsigned int path_info_len = path_info ? strlen(path_info) : 0;

 		php_self_len = script_name_len + path_info_len;
-		php_self = emalloc(php_self_len + 1);
-
 		/* Concat script_name and path_info into php_self */
-		if (script_name) {
-			memcpy(php_self, script_name, script_name_len + 1);
-		}
-		if (path_info) {
-			memcpy(php_self + script_name_len, path_info, path_info_len + 1);
-		}
+		php_self = zend_cstr_concat(
+			script_name, script_name_len,
+			path_info, path_info_len);

 		/* Build the special-case PHP_SELF variable for the CGI version */
 		if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len)) {
@@ -1235,12 +1230,9 @@ static void init_request_info(void)

 								/* PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO */
 								path_translated_len = l + (env_path_info ? strlen(env_path_info) : 0);
-								path_translated = (char *) emalloc(path_translated_len + 1);
-								memcpy(path_translated, env_document_root, l);
-								if (env_path_info) {
-									memcpy(path_translated + l, env_path_info, (path_translated_len - l));
-								}
-								path_translated[path_translated_len] = '\0';
+								path_translated = zend_cstr_concat(
+									env_document_root, l,
+									env_path_info, path_translated_len - l);
 								if (orig_path_translated) {
 									FCGI_PUTENV(request, "ORIG_PATH_TRANSLATED", orig_path_translated);
 								}
@@ -1254,12 +1246,9 @@ static void init_request_info(void)
 								int path_translated_len = ptlen + (env_path_info ? strlen(env_path_info) : 0);
 								char *path_translated = NULL;

-								path_translated = (char *) emalloc(path_translated_len + 1);
-								memcpy(path_translated, pt, ptlen);
-								if (env_path_info) {
-									memcpy(path_translated + ptlen, env_path_info, path_translated_len - ptlen);
-								}
-								path_translated[path_translated_len] = '\0';
+								path_translated = zend_cstr_concat(
+									pt, ptlen,
+									env_path_info, path_translated_len - ptlen);
 								if (orig_path_translated) {
 									FCGI_PUTENV(request, "ORIG_PATH_TRANSLATED", orig_path_translated);
 								}
diff --git a/sapi/phpdbg/phpdbg_utils.c b/sapi/phpdbg/phpdbg_utils.c
index 831595939f6..c259a0d1ace 100644
--- a/sapi/phpdbg/phpdbg_utils.c
+++ b/sapi/phpdbg/phpdbg_utils.c
@@ -672,11 +672,7 @@ char *phpdbg_short_zval_print(zval *zv, int maxlen) /* {{{ */
 			/* Make sure it looks like a float */
 			if (zend_finite(Z_DVAL_P(zv)) && !strchr(decode, '.')) {
 				size_t len = strlen(decode);
-				char *decode2 = emalloc(len + strlen(".0") + 1);
-				memcpy(decode2, decode, len);
-				decode2[len] = '.';
-				decode2[len+1] = '0';
-				decode2[len+2] = '\0';
+				char *decode2 = zend_cstr_concat(decode, len, ".0", strlen(".0"));
 				efree(decode);
 				decode = decode2;
 			}
diff --git a/win32/registry.c b/win32/registry.c
index 3597d57c85a..8445fffd11b 100644
--- a/win32/registry.c
+++ b/win32/registry.c
@@ -49,9 +49,9 @@ static int OpenPhpRegistryKey(char* sub_key, HKEY *hKey)
 			LONG ret;

 			main_key_len = strlen(*key_name);
-			reg_key = emalloc(main_key_len + sub_key_len + 1);
-			memcpy(reg_key, *key_name, main_key_len);
-			memcpy(reg_key + main_key_len, sub_key, sub_key_len + 1);
+			reg_key = zend_cstr_concat(
+				*key_name, main_key_len,
+				sub_key, sub_key_len);
 			ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_key, 0, KEY_READ, hKey);
 			efree(reg_key);