Skip to content

Commit 5bddc39

Browse files
committed
Use consumed_args for array_reduce and some other callback-using functions
1 parent ac620fd commit 5bddc39

File tree

6 files changed

+121
-0
lines changed

6 files changed

+121
-0
lines changed

ext/pcre/php_pcre.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,7 @@ static zend_string *preg_do_repl_func(zend_fcall_info *fci, zend_fcall_info_cach
15701570
fci->retval = &retval;
15711571
fci->param_count = 1;
15721572
fci->params = &arg;
1573+
fci->consumed_args = zend_fci_consumed_arg(0);
15731574
zend_call_function(fci, fcc);
15741575
zval_ptr_dtor(&arg);
15751576
if (EXPECTED(Z_TYPE(retval) == IS_STRING)) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
preg_replace_callback(): capture match array refcount stays low during callback
3+
--FILE--
4+
<?php
5+
var_dump(preg_replace_callback('/(.)(.)(.)/', static function ($matches) {
6+
debug_zval_dump($matches);
7+
return '';
8+
}, 'abc'));
9+
10+
var_dump(preg_replace_callback_array(['/(.)(.)(.)/' => static function ($matches) {
11+
debug_zval_dump($matches);
12+
return '';
13+
}], 'abc'));
14+
?>
15+
--EXPECTF--
16+
array(4) packed refcount(2){
17+
[0]=>
18+
string(3) "abc"%s
19+
[1]=>
20+
string(1) "a" interned
21+
[2]=>
22+
string(1) "b" interned
23+
[3]=>
24+
string(1) "c" interned
25+
}
26+
string(0) ""
27+
array(4) packed refcount(2){
28+
[0]=>
29+
string(3) "abc"%s
30+
[1]=>
31+
string(1) "a" interned
32+
[2]=>
33+
string(1) "b" interned
34+
[3]=>
35+
string(1) "c" interned
36+
}
37+
string(0) ""

ext/standard/array.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6414,6 +6414,7 @@ PHP_FUNCTION(array_reduce)
64146414
fci.retval = return_value;
64156415
fci.param_count = 2;
64166416
fci.params = args;
6417+
fci.consumed_args = zend_fci_consumed_arg(0);
64176418

64186419
ZEND_HASH_FOREACH_VAL(htbl, operand) {
64196420
ZVAL_COPY_VALUE(&args[0], return_value);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
array_reduce(): accumulator refcount stays low during callback
3+
--FILE--
4+
<?php
5+
6+
$result = array_reduce([1, 2, 3], function ($acc, $val) {
7+
debug_zval_dump($acc);
8+
$acc[] = $val;
9+
return $acc;
10+
}, []);
11+
12+
debug_zval_dump($result);
13+
14+
?>
15+
--EXPECT--
16+
array(0) interned {
17+
}
18+
array(1) packed refcount(2){
19+
[0]=>
20+
int(1)
21+
}
22+
array(2) packed refcount(2){
23+
[0]=>
24+
int(1)
25+
[1]=>
26+
int(2)
27+
}
28+
array(3) packed refcount(2){
29+
[0]=>
30+
int(1)
31+
[1]=>
32+
int(2)
33+
[2]=>
34+
int(3)
35+
}

main/output.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,7 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
972972
handler->func.user->fci.param_count = 2;
973973
handler->func.user->fci.params = ob_args;
974974
handler->func.user->fci.retval = &retval;
975+
handler->func.user->fci.consumed_args = zend_fci_consumed_arg(0);
975976

976977
if (SUCCESS == zend_call_function(&handler->func.user->fci, &handler->func.user->fcc) && Z_TYPE(retval) != IS_UNDEF) {
977978
if (handler->flags & PHP_OUTPUT_HANDLER_PRODUCED_OUTPUT) {
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
ob_start(): callback buffer is consumed to avoid unnecessary COW
3+
--SKIPIF--
4+
<?php
5+
if (getenv('USE_ZEND_ALLOC') === '0') {
6+
die('skip Zend MM disabled');
7+
}
8+
?>
9+
--INI--
10+
memory_limit=4M
11+
--FILE--
12+
<?php
13+
14+
const PAYLOAD_SIZE = 256 * 1024;
15+
16+
function run_handler(bool $force_copy): int {
17+
$callback = static function ($buffer) use ($force_copy) {
18+
if ($force_copy) {
19+
$copy = $buffer;
20+
}
21+
$buffer .= 'x';
22+
return '';
23+
};
24+
$payload = str_repeat('A', PAYLOAD_SIZE);
25+
26+
gc_collect_cycles();
27+
gc_mem_caches();
28+
memory_reset_peak_usage();
29+
$start = memory_get_peak_usage();
30+
31+
ob_start($callback);
32+
33+
echo $payload;
34+
$output = ob_get_flush();
35+
36+
return memory_get_peak_usage() - $start;
37+
}
38+
39+
$without_copy = run_handler(false);
40+
$with_copy = run_handler(true);
41+
42+
var_dump($with_copy >= $without_copy + PAYLOAD_SIZE / 2);
43+
44+
?>
45+
--EXPECT--
46+
bool(true)

0 commit comments

Comments
 (0)