Commit 93fd714daa6 for php.net
commit 93fd714daa6b32372d6b7c913433c386054455c7
Author: Weilin Du <weilindu@php.net>
Date: Tue Sep 15 00:03:28 2026 +0800
ext/standard: Optimize str_repeat() when the multiplier is one (#23681)
Just reuse the input string when the multiplier is one :)
Co-authored-by: Nora Dossche <7771979+ndossche@users.noreply.github.com>
diff --git a/UPGRADING b/UPGRADING
index 07223cd7a21..ee7f4c3ab73 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -1110,6 +1110,7 @@ PHP 8.6 UPGRADE NOTES
. Improved performance of intval('+0b...', 2) and intval('0b...', 2).
. Improved performance of str_split().
. Improved performance of str_pad().
+ . Improved performance of str_repeat() when the multiplier is 1.
- URI:
. Improved performance of Uri\WhatWg\Url::parse() when collecting
diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h
index b14de7888a9..0add5a32f91 100644
--- a/Zend/Optimizer/zend_func_infos.h
+++ b/Zend/Optimizer/zend_func_infos.h
@@ -493,7 +493,6 @@ static const func_info_t func_infos[] = {
F1("hebrev", MAY_BE_STRING),
F1("strip_tags", MAY_BE_STRING),
F1("str_getcsv", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_NULL),
- F1("str_repeat", MAY_BE_STRING),
F1("count_chars", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_LONG|MAY_BE_STRING),
F1("localeconv", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY),
F1("sscanf", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY|MAY_BE_LONG|MAY_BE_NULL),
diff --git a/ext/opcache/tests/jit/str_repeat_refcount.phpt b/ext/opcache/tests/jit/str_repeat_refcount.phpt
new file mode 100644
index 00000000000..9a9b83cb929
--- /dev/null
+++ b/ext/opcache/tests/jit/str_repeat_refcount.phpt
@@ -0,0 +1,23 @@
+--TEST--
+str_repeat() may return a shared string
+--EXTENSIONS--
+opcache
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.file_update_protection=0
+opcache.jit=1205
+opcache.jit_buffer_size=32M
+--FILE--
+<?php
+function test(int $length): void {
+ $input = str_repeat('a', $length);
+ str_repeat($input, 1);
+ $overwrite = str_repeat('b', $length);
+ var_dump($input, $overwrite);
+}
+test(16);
+?>
+--EXPECT--
+string(16) "aaaaaaaaaaaaaaaa"
+string(16) "bbbbbbbbbbbbbbbb"
diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php
index 6b3b11a5fa3..4b455ec331b 100644
--- a/ext/standard/basic_functions.stub.php
+++ b/ext/standard/basic_functions.stub.php
@@ -2593,7 +2593,6 @@ function parse_str(string $string, &$result): void {}
*/
function str_getcsv(string $string, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): array {}
-/** @refcount 1 */
function str_repeat(string $string, int $times): string {}
/**
diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h
index 677c5e66860..b770a1e8684 100644
Binary files a/ext/standard/basic_functions_arginfo.h and b/ext/standard/basic_functions_arginfo.h differ
diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h
index d6031776699..aab4e4b5f09 100644
Binary files a/ext/standard/basic_functions_decl.h and b/ext/standard/basic_functions_decl.h differ
diff --git a/ext/standard/string.c b/ext/standard/string.c
index f7c2ed37cf6..2c5990a6a8c 100644
--- a/ext/standard/string.c
+++ b/ext/standard/string.c
@@ -5638,6 +5638,10 @@ PHP_FUNCTION(str_repeat)
if (ZSTR_LEN(input_str) == 0 || mult == 0)
RETURN_EMPTY_STRING();
+ if (mult == 1) {
+ RETURN_STR_COPY(input_str);
+ }
+
/* Initialize the result string */
result = zend_string_safe_alloc(ZSTR_LEN(input_str), mult, 0, 0);
result_len = ZSTR_LEN(input_str) * mult;