From 8629256dc76fc77b013b00caac5fdaab972ef7a2 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:45:09 +0200 Subject: [PATCH 1/8] Deduplicate error-handling code in finfo_open (#19149) --- ext/fileinfo/fileinfo.c | 42 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/ext/fileinfo/fileinfo.c b/ext/fileinfo/fileinfo.c index 143b8ba080fc5..e99c793597678 100644 --- a/ext/fileinfo/fileinfo.c +++ b/ext/fileinfo/fileinfo.c @@ -153,22 +153,10 @@ PHP_FUNCTION(finfo_open) } else if (file && *file) { /* user specified file, perform open_basedir checks */ if (php_check_open_basedir(file)) { - if (object) { - zend_restore_error_handling(&zeh); - if (!EG(exception)) { - zend_throw_exception(NULL, "Constructor failed", 0); - } - } - RETURN_FALSE; + goto err; } if (!expand_filepath_with_mode(file, resolved_path, NULL, 0, CWD_EXPAND)) { - if (object) { - zend_restore_error_handling(&zeh); - if (!EG(exception)) { - zend_throw_exception(NULL, "Constructor failed", 0); - } - } - RETURN_FALSE; + goto err; } file = resolved_path; } @@ -177,37 +165,35 @@ PHP_FUNCTION(finfo_open) if (magic == NULL) { php_error_docref(NULL, E_WARNING, "Invalid mode '" ZEND_LONG_FMT "'.", options); - if (object) { - zend_restore_error_handling(&zeh); - if (!EG(exception)) { - zend_throw_exception(NULL, "Constructor failed", 0); - } - } - RETURN_FALSE; + goto err; } if (magic_load(magic, file) == -1) { php_error_docref(NULL, E_WARNING, "Failed to load magic database at \"%s\"", file); magic_close(magic); - if (object) { - zend_restore_error_handling(&zeh); - if (!EG(exception)) { - zend_throw_exception(NULL, "Constructor failed", 0); - } - } - RETURN_FALSE; + goto err; } if (object) { zend_restore_error_handling(&zeh); finfo_object *obj = Z_FINFO_P(object); obj->magic = magic; + return; } else { zend_object *zobj = finfo_objects_new(finfo_class_entry); finfo_object *obj = php_finfo_fetch_object(zobj); obj->magic = magic; RETURN_OBJ(zobj); } + +err: + if (object) { + zend_restore_error_handling(&zeh); + if (!EG(exception)) { + zend_throw_exception(NULL, "Constructor failed", 0); + } + } + RETURN_FALSE; } /* }}} */ From 7f4076bae0cf91c5a24f4f908525e739e6bca8fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 17 Jul 2025 21:13:42 +0200 Subject: [PATCH 2/8] RFC: Clone with v2 (#18747) RFC: https://wiki.php.net/rfc/clone_with_v2 Co-authored-by: Volker Dusch --- NEWS | 3 + UPGRADING | 3 +- Zend/tests/clone/clone_with_001.phpt | 71 ++++++++++++++++ Zend/tests/clone/clone_with_002.phpt | 114 ++++++++++++++++++++++++++ Zend/tests/clone/clone_with_003.phpt | 23 ++++++ Zend/tests/clone/clone_with_004.phpt | 82 ++++++++++++++++++ Zend/tests/clone/clone_with_005.phpt | 64 +++++++++++++++ Zend/tests/clone/clone_with_006.phpt | 16 ++++ Zend/tests/clone/clone_with_007.phpt | 29 +++++++ Zend/tests/clone/clone_with_008.phpt | 40 +++++++++ Zend/tests/clone/clone_with_009.phpt | 72 ++++++++++++++++ Zend/tests/clone/clone_with_010.phpt | 21 +++++ Zend/tests/clone/clone_with_011.phpt | 18 ++++ Zend/tests/clone/clone_with_012.phpt | 35 ++++++++ Zend/tests/clone/clone_with_013.phpt | 31 +++++++ Zend/zend_builtin_functions.c | 16 +++- Zend/zend_builtin_functions.stub.php | 2 +- Zend/zend_builtin_functions_arginfo.h | 3 +- Zend/zend_iterators.c | 1 + Zend/zend_object_handlers.c | 1 + Zend/zend_object_handlers.h | 2 + Zend/zend_objects.c | 46 +++++++++++ Zend/zend_objects.h | 1 + Zend/zend_vm_def.h | 3 +- Zend/zend_vm_execute.h | 12 ++- ext/com_dotnet/com_handlers.c | 1 + ext/com_dotnet/com_saproxy.c | 1 + 27 files changed, 701 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/clone/clone_with_001.phpt create mode 100644 Zend/tests/clone/clone_with_002.phpt create mode 100644 Zend/tests/clone/clone_with_003.phpt create mode 100644 Zend/tests/clone/clone_with_004.phpt create mode 100644 Zend/tests/clone/clone_with_005.phpt create mode 100644 Zend/tests/clone/clone_with_006.phpt create mode 100644 Zend/tests/clone/clone_with_007.phpt create mode 100644 Zend/tests/clone/clone_with_008.phpt create mode 100644 Zend/tests/clone/clone_with_009.phpt create mode 100644 Zend/tests/clone/clone_with_010.phpt create mode 100644 Zend/tests/clone/clone_with_011.phpt create mode 100644 Zend/tests/clone/clone_with_012.phpt create mode 100644 Zend/tests/clone/clone_with_013.phpt diff --git a/NEWS b/NEWS index 0c925ea0b1179..e317e0c234a3e 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.5.0alpha3 +- Core: + . Add clone-with support to the clone() function. (timwolla, edorian) + - Curl: . Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan) . Add support for CURLINFO_QUEUE_TIME_T in curl_getinfo() (thecaliskan) diff --git a/UPGRADING b/UPGRADING index dba8d29755c96..2cf8a4a4322ac 100644 --- a/UPGRADING +++ b/UPGRADING @@ -408,7 +408,8 @@ PHP 8.5 UPGRADE NOTES . get_exception_handler() allows retrieving the current user-defined exception handler function. RFC: https://wiki.php.net/rfc/get-error-exception-handler - . The clone language construct is now a function. + . The clone language construct is now a function and supports reassigning + (readonly) properties during cloning via the new $withProperties parameter. RFC: https://wiki.php.net/rfc/clone_with_v2 - Curl: diff --git a/Zend/tests/clone/clone_with_001.phpt b/Zend/tests/clone/clone_with_001.phpt new file mode 100644 index 0000000000000..3623706a980c8 --- /dev/null +++ b/Zend/tests/clone/clone_with_001.phpt @@ -0,0 +1,71 @@ +--TEST-- +Clone with basic +--FILE-- + 'BAZ', + 'array' => [1, 2, 3], +]; + +var_dump(clone $x); +var_dump(clone($x)); +var_dump(clone($x, [ 'foo' => $foo, 'bar' => $bar ])); +var_dump(clone($x, $array)); +var_dump(clone($x, [ 'obj' => $x ])); + +var_dump(clone($x, [ + 'abc', + 'def', + new Dummy(), + 'named' => 'value', +])); + +?> +--EXPECTF-- +object(stdClass)#%d (0) { +} +object(stdClass)#%d (0) { +} +object(stdClass)#%d (2) { + ["foo"]=> + string(3) "FOO" + ["bar"]=> + object(Dummy)#%d (0) { + } +} +object(stdClass)#%d (2) { + ["baz"]=> + string(3) "BAZ" + ["array"]=> + array(3) { + [0]=> + int(1) + [1]=> + int(2) + [2]=> + int(3) + } +} +object(stdClass)#%d (1) { + ["obj"]=> + object(stdClass)#%d (0) { + } +} +object(stdClass)#%d (4) { + ["0"]=> + string(3) "abc" + ["1"]=> + string(3) "def" + ["2"]=> + object(Dummy)#%d (0) { + } + ["named"]=> + string(5) "value" +} diff --git a/Zend/tests/clone/clone_with_002.phpt b/Zend/tests/clone/clone_with_002.phpt new file mode 100644 index 0000000000000..8b86e64c76aa8 --- /dev/null +++ b/Zend/tests/clone/clone_with_002.phpt @@ -0,0 +1,114 @@ +--TEST-- +Clone with respects visiblity +--FILE-- + 'updated A', 'b' => 'updated B', 'c' => 'updated C', 'd' => 'updated D' ]); + } +} + +class C extends P { + public function m2() { + return clone($this, [ 'a' => 'updated A', 'b' => 'updated B', 'c' => 'dynamic C' ]); + } + + public function m3() { + return clone($this, [ 'd' => 'inaccessible' ]); + } +} + +class Unrelated { + public function m3(P $p) { + return clone($p, [ 'b' => 'inaccessible' ]); + } +} + +$p = new P(); + +var_dump(clone($p, [ 'a' => 'updated A' ])); +var_dump($p->m1()); + +$c = new C(); +var_dump($c->m1()); +var_dump($c->m2()); +try { + var_dump($c->m3()); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +try { + var_dump(clone($p, [ 'b' => 'inaccessible' ])); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +try { + var_dump(clone($p, [ 'd' => 'inaccessible' ])); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +try { + var_dump((new Unrelated())->m3($p)); +} catch (Error $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +object(P)#%d (4) { + ["a"]=> + string(9) "updated A" + ["b":protected]=> + string(7) "default" + ["c":"P":private]=> + string(7) "default" + ["d"]=> + string(7) "default" +} +object(P)#%d (4) { + ["a"]=> + string(9) "updated A" + ["b":protected]=> + string(9) "updated B" + ["c":"P":private]=> + string(9) "updated C" + ["d"]=> + string(9) "updated D" +} +object(C)#%d (4) { + ["a"]=> + string(9) "updated A" + ["b":protected]=> + string(9) "updated B" + ["c":"P":private]=> + string(9) "updated C" + ["d"]=> + string(9) "updated D" +} + +Deprecated: Creation of dynamic property C::$c is deprecated in %s on line %d +object(C)#%d (5) { + ["a"]=> + string(9) "updated A" + ["b":protected]=> + string(9) "updated B" + ["c":"P":private]=> + string(7) "default" + ["d"]=> + string(7) "default" + ["c"]=> + string(9) "dynamic C" +} +Error: Cannot modify private(set) property P::$d from scope C +Error: Cannot access protected property P::$b +Error: Cannot modify private(set) property P::$d from global scope +Error: Cannot access protected property P::$b diff --git a/Zend/tests/clone/clone_with_003.phpt b/Zend/tests/clone/clone_with_003.phpt new file mode 100644 index 0000000000000..48fb2b1f6a3d6 --- /dev/null +++ b/Zend/tests/clone/clone_with_003.phpt @@ -0,0 +1,23 @@ +--TEST-- +Clone with supports property hooks +--FILE-- +hooked = strtoupper($value); + } + } +} + +$c = new Clazz(); + +var_dump(clone($c, [ 'hooked' => 'updated' ])); + +?> +--EXPECTF-- +object(Clazz)#%d (1) { + ["hooked"]=> + string(7) "UPDATED" +} diff --git a/Zend/tests/clone/clone_with_004.phpt b/Zend/tests/clone/clone_with_004.phpt new file mode 100644 index 0000000000000..e9985d62bd04b --- /dev/null +++ b/Zend/tests/clone/clone_with_004.phpt @@ -0,0 +1,82 @@ +--TEST-- +Clone with evaluation order +--FILE-- +hooked = strtoupper($value); + } + } + + public string $maxLength { + set { + echo __FUNCTION__, PHP_EOL; + + if (strlen($value) > 5) { + throw new \Exception('Length exceeded'); + } + + $this->maxLength = $value; + } + } + + public string $minLength { + set { + echo __FUNCTION__, PHP_EOL; + + if (strlen($value) < 5) { + throw new \Exception('Length unsufficient'); + } + + $this->minLength = $value; + } + } +} + +$c = new Clazz(); + +var_dump(clone($c, [ 'hooked' => 'updated' ])); +echo PHP_EOL; +var_dump(clone($c, [ 'hooked' => 'updated', 'maxLength' => 'abc', 'minLength' => 'abcdef' ])); +echo PHP_EOL; +var_dump(clone($c, [ 'minLength' => 'abcdef', 'hooked' => 'updated', 'maxLength' => 'abc' ])); + +?> +--EXPECTF-- +$hooked::set +object(Clazz)#%d (1) { + ["hooked"]=> + string(7) "UPDATED" + ["maxLength"]=> + uninitialized(string) + ["minLength"]=> + uninitialized(string) +} + +$hooked::set +$maxLength::set +$minLength::set +object(Clazz)#%d (3) { + ["hooked"]=> + string(7) "UPDATED" + ["maxLength"]=> + string(3) "abc" + ["minLength"]=> + string(6) "abcdef" +} + +$minLength::set +$hooked::set +$maxLength::set +object(Clazz)#%d (3) { + ["hooked"]=> + string(7) "UPDATED" + ["maxLength"]=> + string(3) "abc" + ["minLength"]=> + string(6) "abcdef" +} diff --git a/Zend/tests/clone/clone_with_005.phpt b/Zend/tests/clone/clone_with_005.phpt new file mode 100644 index 0000000000000..55ffb2423d7e3 --- /dev/null +++ b/Zend/tests/clone/clone_with_005.phpt @@ -0,0 +1,64 @@ +--TEST-- +Clone with error handling +--FILE-- +hooked = strtoupper($value); + } + } + + public string $maxLength { + set { + echo __FUNCTION__, PHP_EOL; + + if (strlen($value) > 5) { + throw new \Exception('Length exceeded'); + } + + $this->maxLength = $value; + } + } + + public string $minLength { + set { + echo __FUNCTION__, PHP_EOL; + + if (strlen($value) < 5) { + throw new \Exception('Length insufficient'); + } + + $this->minLength = $value; + } + } +} + +$c = new Clazz(); + +try { + var_dump(clone($c, [ 'hooked' => 'updated', 'maxLength' => 'abcdef', 'minLength' => 'abc' ])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +echo PHP_EOL; + +try { + var_dump(clone($c, [ 'hooked' => 'updated', 'minLength' => 'abc', 'maxLength' => 'abcdef' ])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +$hooked::set +$maxLength::set +Exception: Length exceeded + +$hooked::set +$minLength::set +Exception: Length insufficient diff --git a/Zend/tests/clone/clone_with_006.phpt b/Zend/tests/clone/clone_with_006.phpt new file mode 100644 index 0000000000000..7b0b8520b8a82 --- /dev/null +++ b/Zend/tests/clone/clone_with_006.phpt @@ -0,0 +1,16 @@ +--TEST-- +Clone with error cases +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +TypeError: clone(): Argument #2 ($withProperties) must be of type array, int given diff --git a/Zend/tests/clone/clone_with_007.phpt b/Zend/tests/clone/clone_with_007.phpt new file mode 100644 index 0000000000000..08cefd7f8cbe3 --- /dev/null +++ b/Zend/tests/clone/clone_with_007.phpt @@ -0,0 +1,29 @@ +--TEST-- +Clone with supports __clone +--FILE-- +foo = 'foo updated in __clone'; + $this->bar = 'bar updated in __clone'; + } +} + +$c = new Clazz('foo', 'bar'); + +var_dump(clone($c, [ 'foo' => 'foo updated in clone-with' ])); + +?> +--EXPECTF-- +object(Clazz)#%d (2) { + ["foo"]=> + string(25) "foo updated in clone-with" + ["bar"]=> + string(22) "bar updated in __clone" +} diff --git a/Zend/tests/clone/clone_with_008.phpt b/Zend/tests/clone/clone_with_008.phpt new file mode 100644 index 0000000000000..aa2c639fb7f1c --- /dev/null +++ b/Zend/tests/clone/clone_with_008.phpt @@ -0,0 +1,40 @@ +--TEST-- +Clone with readonly +--FILE-- +b = '__clone'; + } +} + +$c = new Clazz('default', 'default'); + +var_dump(clone($c, [ 'a' => "with" ])); + +try { + var_dump(clone($c, [ 'b' => "with" ])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +object(Clazz)#%d (2) { + ["a"]=> + string(4) "with" + ["b"]=> + string(7) "__clone" +} +object(Clazz)#%d (2) { + ["a"]=> + string(7) "default" + ["b"]=> + string(4) "with" +} diff --git a/Zend/tests/clone/clone_with_009.phpt b/Zend/tests/clone/clone_with_009.phpt new file mode 100644 index 0000000000000..c6a7d2d18b982 --- /dev/null +++ b/Zend/tests/clone/clone_with_009.phpt @@ -0,0 +1,72 @@ +--TEST-- +Clone with lazy objects +--FILE-- + 2 ]); + + var_dump($reflector->isUninitializedLazyObject($obj)); + var_dump($obj); + var_dump($reflector->isUninitializedLazyObject($clone)); + var_dump($clone); +} + +$reflector = new ReflectionClass(C::class); + +$obj = $reflector->newLazyGhost(function ($obj) { + var_dump("initializer"); + $obj->__construct(); +}); + +test('Ghost', $obj); + +$obj = $reflector->newLazyProxy(function ($obj) { + var_dump("initializer"); + return new C(); +}); + +test('Proxy', $obj); + +?> +--EXPECTF-- +# Ghost: +string(11) "initializer" +bool(false) +object(C)#%d (1) { + ["a"]=> + int(1) +} +bool(false) +object(C)#%d (1) { + ["a"]=> + int(2) +} +# Proxy: +string(11) "initializer" +bool(false) +lazy proxy object(C)#%d (1) { + ["instance"]=> + object(C)#%d (1) { + ["a"]=> + int(1) + } +} +bool(false) +lazy proxy object(C)#%d (1) { + ["instance"]=> + object(C)#%d (1) { + ["a"]=> + int(2) + } +} diff --git a/Zend/tests/clone/clone_with_010.phpt b/Zend/tests/clone/clone_with_010.phpt new file mode 100644 index 0000000000000..29ecf714fc498 --- /dev/null +++ b/Zend/tests/clone/clone_with_010.phpt @@ -0,0 +1,21 @@ +--TEST-- +Clone with native classes +--FILE-- + "something" ])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +try { + var_dump(clone(new \Random\Engine\Xoshiro256StarStar(), [ 'with' => "something" ])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Error: Trying to clone an uncloneable object of class Random\Engine\Secure +Error: Cannot create dynamic property Random\Engine\Xoshiro256StarStar::$with diff --git a/Zend/tests/clone/clone_with_011.phpt b/Zend/tests/clone/clone_with_011.phpt new file mode 100644 index 0000000000000..5f8e99bb65f2f --- /dev/null +++ b/Zend/tests/clone/clone_with_011.phpt @@ -0,0 +1,18 @@ +--TEST-- +Clone with name mangling +--FILE-- + 'updated'])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +Error: Cannot access property starting with "\0" diff --git a/Zend/tests/clone/clone_with_012.phpt b/Zend/tests/clone/clone_with_012.phpt new file mode 100644 index 0000000000000..e24f0adad7c40 --- /dev/null +++ b/Zend/tests/clone/clone_with_012.phpt @@ -0,0 +1,35 @@ +--TEST-- +Clone with property hook updating readonly property +--FILE-- +foo = $value; + $this->bar = 'bar updated in hook'; + } + } + + public public(set) readonly string $bar; +} + +$f = new Clazz(); + +var_dump(clone($f, ['foo' => 'foo updated in clone-with'])); + +try { + var_dump(clone($f, ['foo' => 'foo updated in clone-with', 'bar' => 'bar updated in clone-with'])); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +object(Clazz)#%d (2) { + ["foo"]=> + string(25) "foo updated in clone-with" + ["bar"]=> + string(19) "bar updated in hook" +} +Error: Cannot modify readonly property Clazz::$bar diff --git a/Zend/tests/clone/clone_with_013.phpt b/Zend/tests/clone/clone_with_013.phpt new file mode 100644 index 0000000000000..13f2463255851 --- /dev/null +++ b/Zend/tests/clone/clone_with_013.phpt @@ -0,0 +1,31 @@ +--TEST-- +Clone with references +--FILE-- + &$ref]; + +try { + var_dump(clone($x, $with)); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +unset($ref); + +try { + var_dump(clone($x, $with)); +} catch (Throwable $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Error: Cannot assign by reference when cloning with updated properties +object(stdClass)#%d (1) { + ["x"]=> + string(9) "reference" +} diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 256f08e05d6a6..1abdfb0ddf2ec 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -72,9 +72,12 @@ zend_result zend_startup_builtin_functions(void) /* {{{ */ ZEND_FUNCTION(clone) { zend_object *zobj; + HashTable *with = (HashTable*)&zend_empty_array; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_OBJ(zobj) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT(with) ZEND_PARSE_PARAMETERS_END(); /* clone() also exists as the ZEND_CLONE OPcode and both implementations must be kept in sync. */ @@ -95,7 +98,16 @@ ZEND_FUNCTION(clone) } zend_object *cloned; - cloned = zobj->handlers->clone_obj(zobj); + if (zend_hash_num_elements(with) > 0) { + if (UNEXPECTED(!zobj->handlers->clone_obj_with)) { + zend_throw_error(NULL, "Cloning objects of class %s with updated properties is not supported", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + cloned = zobj->handlers->clone_obj_with(zobj, scope, with); + } else { + cloned = zobj->handlers->clone_obj(zobj); + } ZEND_ASSERT(cloned || EG(exception)); if (EXPECTED(cloned)) { diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index 256c405c71c28..ef73da339e341 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -8,7 +8,7 @@ class stdClass } /** @refcount 1 */ -function _clone(object $object): object {} +function _clone(object $object, array $withProperties = []): object {} function exit(string|int $status = 0): never {} diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index 1c595ecd5777c..df68148638a6b 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,8 +1,9 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 12327caa3fe940ccef68ed99f9278982dc0173a5 */ + * Stub hash: 0be87bb6b55e100c022e70aa6f3b17001725784f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_clone, 0, 1, IS_OBJECT, 0) ZEND_ARG_TYPE_INFO(0, object, IS_OBJECT, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, withProperties, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_exit, 0, 0, IS_NEVER, 0) diff --git a/Zend/zend_iterators.c b/Zend/zend_iterators.c index f67033b11161c..64dbb0541a80d 100644 --- a/Zend/zend_iterators.c +++ b/Zend/zend_iterators.c @@ -31,6 +31,7 @@ static const zend_object_handlers iterator_object_handlers = { iter_wrapper_free, iter_wrapper_dtor, NULL, /* clone_obj */ + NULL, /* clone_obj_with */ NULL, /* prop read */ NULL, /* prop write */ NULL, /* read dim */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 732b7e8f2bd96..55034f5452f71 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -2541,6 +2541,7 @@ ZEND_API const zend_object_handlers std_object_handlers = { zend_object_std_dtor, /* free_obj */ zend_objects_destroy_object, /* dtor_obj */ zend_objects_clone_obj, /* clone_obj */ + zend_objects_clone_obj_with, /* clone_obj_with */ zend_std_read_property, /* read_property */ zend_std_write_property, /* write_property */ diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index fb87695a2ed25..84d0b57d7aa28 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -180,6 +180,7 @@ typedef void (*zend_object_free_obj_t)(zend_object *object); typedef void (*zend_object_dtor_obj_t)(zend_object *object); typedef zend_object* (*zend_object_clone_obj_t)(zend_object *object); +typedef zend_object* (*zend_object_clone_obj_with_t)(zend_object *object, const zend_class_entry *scope, const HashTable *properties); /* Get class name for display in var_dump and other debugging functions. * Must be defined and must return a non-NULL value. */ @@ -209,6 +210,7 @@ struct _zend_object_handlers { zend_object_free_obj_t free_obj; /* required */ zend_object_dtor_obj_t dtor_obj; /* required */ zend_object_clone_obj_t clone_obj; /* optional */ + zend_object_clone_obj_with_t clone_obj_with; /* optional */ zend_object_read_property_t read_property; /* required */ zend_object_write_property_t write_property; /* required */ zend_object_read_dimension_t read_dimension; /* required */ diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 933c1161fe960..4b4187f423859 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -276,6 +276,52 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, } } +ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const zend_class_entry *scope, const HashTable *properties) +{ + zend_object *new_object = old_object->handlers->clone_obj(old_object); + + if (EXPECTED(!EG(exception))) { + /* Unlock readonly properties once more. */ + if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { + for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { + zval* prop = OBJ_PROP_NUM(new_object, i); + Z_PROP_FLAG_P(prop) |= IS_PROP_REINITABLE; + } + } + + const zend_class_entry *old_scope = EG(fake_scope); + + EG(fake_scope) = scope; + + ZEND_HASH_FOREACH_KEY_VAL(properties, zend_ulong num_key, zend_string *key, zval *val) { + if (UNEXPECTED(Z_ISREF_P(val))) { + if (Z_REFCOUNT_P(val) == 1) { + val = Z_REFVAL_P(val); + } else { + zend_throw_error(NULL, "Cannot assign by reference when cloning with updated properties"); + break; + } + } + + if (UNEXPECTED(key == NULL)) { + key = zend_long_to_str(num_key); + new_object->handlers->write_property(new_object, key, val, NULL); + zend_string_release_ex(key, false); + } else { + new_object->handlers->write_property(new_object, key, val, NULL); + } + + if (UNEXPECTED(EG(exception))) { + break; + } + } ZEND_HASH_FOREACH_END(); + + EG(fake_scope) = old_scope; + } + + return new_object; +} + ZEND_API zend_object *zend_objects_clone_obj(zend_object *old_object) { zend_object *new_object; diff --git a/Zend/zend_objects.h b/Zend/zend_objects.h index 41e3bcd9594b1..712fd442da5a4 100644 --- a/Zend/zend_objects.h +++ b/Zend/zend_objects.h @@ -30,6 +30,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, ZEND_API void zend_object_std_dtor(zend_object *object); ZEND_API void zend_objects_destroy_object(zend_object *object); ZEND_API zend_object *zend_objects_clone_obj(zend_object *object); +ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *object, const zend_class_entry *scope, const HashTable *properties); void zend_object_dtor_dynamic_properties(zend_object *object); void zend_object_dtor_property(zend_object *object, zval *p); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c0e05aef470ae..4ed034e619012 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6006,7 +6006,8 @@ ZEND_VM_COLD_CONST_HANDLER(110, ZEND_CLONE, CONST|TMPVAR|UNUSED|THIS|CV, ANY) SAVE_OPLINE(); obj = GET_OP1_OBJ_ZVAL_PTR_UNDEF(BP_VAR_R); - /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. + * The OPcode intentionally does not support a clone-with property list to keep it simple. */ do { if (OP1_TYPE == IS_CONST || diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 387a05943dd19..8386dd79a26c7 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -5180,7 +5180,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CONST_ SAVE_OPLINE(); obj = RT_CONSTANT(opline, opline->op1); - /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. + * The OPcode intentionally does not support a clone-with property list to keep it simple. */ do { if (IS_CONST == IS_CONST || @@ -15427,7 +15428,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_TMPVAR_HANDLER(ZEND SAVE_OPLINE(); obj = _get_zval_ptr_var(opline->op1.var EXECUTE_DATA_CC); - /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. + * The OPcode intentionally does not support a clone-with property list to keep it simple. */ do { if ((IS_TMP_VAR|IS_VAR) == IS_CONST || @@ -33522,7 +33524,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_UNUSED_HANDLER(ZEND SAVE_OPLINE(); obj = &EX(This); - /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. + * The OPcode intentionally does not support a clone-with property list to keep it simple. */ do { if (IS_UNUSED == IS_CONST || @@ -41041,7 +41044,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CLONE_SPEC_CV_HANDLER(ZEND_OPC SAVE_OPLINE(); obj = EX_VAR(opline->op1.var); - /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. */ + /* ZEND_CLONE also exists as the clone() function and both implementations must be kept in sync. + * The OPcode intentionally does not support a clone-with property list to keep it simple. */ do { if (IS_CV == IS_CONST || diff --git a/ext/com_dotnet/com_handlers.c b/ext/com_dotnet/com_handlers.c index af980b7b86f2a..638fc5d8a3ae6 100644 --- a/ext/com_dotnet/com_handlers.c +++ b/ext/com_dotnet/com_handlers.c @@ -514,6 +514,7 @@ zend_object_handlers php_com_object_handlers = { php_com_object_free_storage, zend_objects_destroy_object, php_com_object_clone, + NULL, /* clone_with */ com_property_read, com_property_write, com_read_dimension, diff --git a/ext/com_dotnet/com_saproxy.c b/ext/com_dotnet/com_saproxy.c index ea0e9e47a13d9..ec79faa30a32b 100644 --- a/ext/com_dotnet/com_saproxy.c +++ b/ext/com_dotnet/com_saproxy.c @@ -402,6 +402,7 @@ zend_object_handlers php_com_saproxy_handlers = { saproxy_free_storage, zend_objects_destroy_object, saproxy_clone, + NULL, /* clone_with */ saproxy_property_read, saproxy_property_write, saproxy_read_dimension, From bdca73cc142ae682cdfea3b21b89354fad346ffe Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:22:10 +0200 Subject: [PATCH 3/8] ext/hash: Remove incorrect zval_ptr_dtor --- ext/hash/hash.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/hash/hash.c b/ext/hash/hash.c index 19d72ed7699b1..58b68fc63b9b7 100644 --- a/ext/hash/hash.c +++ b/ext/hash/hash.c @@ -850,8 +850,6 @@ PHP_FUNCTION(hash_copy) RETVAL_OBJ(Z_OBJ_HANDLER_P(zhash, clone_obj)(Z_OBJ_P(zhash))); if (php_hashcontext_from_object(Z_OBJ_P(return_value))->context == NULL) { - zval_ptr_dtor(return_value); - zend_throw_error(NULL, "Cannot copy hash"); RETURN_THROWS(); } From d11099ae325b0ade036c039f3e6962120c0a1709 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 17 Jul 2025 20:22:16 +0200 Subject: [PATCH 4/8] ext/socket: Remove incorrect zval_ptr_dtor --- ext/sockets/sockets.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index c252dc6e07a41..a0d5e52458f32 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -2511,7 +2511,6 @@ PHP_FUNCTION(socket_addrinfo_bind) } default: close(php_sock->bsd_socket); - zval_ptr_dtor(return_value); zend_argument_value_error(1, "must be one of AF_UNIX, AF_INET, or AF_INET6"); RETURN_THROWS(); } @@ -2575,7 +2574,6 @@ PHP_FUNCTION(socket_addrinfo_connect) default: zend_argument_value_error(1, "socket type must be one of AF_UNIX, AF_INET, or AF_INET6"); close(php_sock->bsd_socket); - zval_ptr_dtor(return_value); RETURN_THROWS(); } From 86aaded7e57dd2b2f5a4dd4e6a6d9511e6aec4e7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:51:46 +0200 Subject: [PATCH 5/8] NEWS for GH-19162 Closes GH-19162. --- NEWS | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 06427de7f9652..cbd68f4ce7484 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.25 +- Hash: + . Fix crash on clone failure. (nielsdos) + - LDAP: . Fixed bug GH-18529 (additional inheriting of TLS int options). (Jakub Zelenka) @@ -11,6 +14,8 @@ PHP NEWS return value check). (nielsdos, botovq) . Fix error return check of EVP_CIPHER_CTX_ctrl(). (nielsdos) +- Sockets: + . Fix some potential crashes on incorrect argument value. (nielsdos) 31 Jul 2025, PHP 8.3.24 From 78d96e94fa8e05dd59d03aa4891fa843ebc93ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 17 Jul 2025 22:11:24 +0200 Subject: [PATCH 6/8] zend_API: Make `scope` a `const zend_class_entry*` for `zend_update_property_*()` (#19156) Follow-up for php/php-src#19060. --- Zend/zend_API.c | 20 ++++++++++---------- Zend/zend_API.h | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5262cc0b6c95f..11fa2e740cb7e 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -5000,7 +5000,7 @@ ZEND_API void zend_declare_class_constant_string(zend_class_entry *ce, const cha } /* }}} */ -ZEND_API void zend_update_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zval *value) /* {{{ */ +ZEND_API void zend_update_property_ex(const zend_class_entry *scope, zend_object *object, zend_string *name, zval *value) /* {{{ */ { const zend_class_entry *old_scope = EG(fake_scope); @@ -5012,7 +5012,7 @@ ZEND_API void zend_update_property_ex(zend_class_entry *scope, zend_object *obje } /* }}} */ -ZEND_API void zend_update_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value) /* {{{ */ +ZEND_API void zend_update_property(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value) /* {{{ */ { zend_string *property; const zend_class_entry *old_scope = EG(fake_scope); @@ -5027,7 +5027,7 @@ ZEND_API void zend_update_property(zend_class_entry *scope, zend_object *object, } /* }}} */ -ZEND_API void zend_update_property_null(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length) /* {{{ */ +ZEND_API void zend_update_property_null(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length) /* {{{ */ { zval tmp; @@ -5036,7 +5036,7 @@ ZEND_API void zend_update_property_null(zend_class_entry *scope, zend_object *ob } /* }}} */ -ZEND_API void zend_unset_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length) /* {{{ */ +ZEND_API void zend_unset_property(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length) /* {{{ */ { zend_string *property; const zend_class_entry *old_scope = EG(fake_scope); @@ -5051,7 +5051,7 @@ ZEND_API void zend_unset_property(zend_class_entry *scope, zend_object *object, } /* }}} */ -ZEND_API void zend_update_property_bool(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value) /* {{{ */ +ZEND_API void zend_update_property_bool(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value) /* {{{ */ { zval tmp; @@ -5060,7 +5060,7 @@ ZEND_API void zend_update_property_bool(zend_class_entry *scope, zend_object *ob } /* }}} */ -ZEND_API void zend_update_property_long(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value) /* {{{ */ +ZEND_API void zend_update_property_long(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value) /* {{{ */ { zval tmp; @@ -5069,7 +5069,7 @@ ZEND_API void zend_update_property_long(zend_class_entry *scope, zend_object *ob } /* }}} */ -ZEND_API void zend_update_property_double(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, double value) /* {{{ */ +ZEND_API void zend_update_property_double(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, double value) /* {{{ */ { zval tmp; @@ -5078,7 +5078,7 @@ ZEND_API void zend_update_property_double(zend_class_entry *scope, zend_object * } /* }}} */ -ZEND_API void zend_update_property_str(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_string *value) /* {{{ */ +ZEND_API void zend_update_property_str(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_string *value) /* {{{ */ { zval tmp; @@ -5087,7 +5087,7 @@ ZEND_API void zend_update_property_str(zend_class_entry *scope, zend_object *obj } /* }}} */ -ZEND_API void zend_update_property_string(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value) /* {{{ */ +ZEND_API void zend_update_property_string(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value) /* {{{ */ { zval tmp; @@ -5097,7 +5097,7 @@ ZEND_API void zend_update_property_string(zend_class_entry *scope, zend_object * } /* }}} */ -ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value, size_t value_len) /* {{{ */ +ZEND_API void zend_update_property_stringl(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value, size_t value_len) /* {{{ */ { zval tmp; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 25a96f8cc9e52..8ab32a280b01d 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -494,16 +494,16 @@ static zend_always_inline HashTable *zend_class_backed_enum_table(zend_class_ent } } -ZEND_API void zend_update_property_ex(zend_class_entry *scope, zend_object *object, zend_string *name, zval *value); -ZEND_API void zend_update_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value); -ZEND_API void zend_update_property_null(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length); -ZEND_API void zend_update_property_bool(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value); -ZEND_API void zend_update_property_long(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value); -ZEND_API void zend_update_property_double(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, double value); -ZEND_API void zend_update_property_str(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_string *value); -ZEND_API void zend_update_property_string(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value); -ZEND_API void zend_update_property_stringl(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value, size_t value_length); -ZEND_API void zend_unset_property(zend_class_entry *scope, zend_object *object, const char *name, size_t name_length); +ZEND_API void zend_update_property_ex(const zend_class_entry *scope, zend_object *object, zend_string *name, zval *value); +ZEND_API void zend_update_property(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zval *value); +ZEND_API void zend_update_property_null(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length); +ZEND_API void zend_update_property_bool(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value); +ZEND_API void zend_update_property_long(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_long value); +ZEND_API void zend_update_property_double(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, double value); +ZEND_API void zend_update_property_str(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, zend_string *value); +ZEND_API void zend_update_property_string(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value); +ZEND_API void zend_update_property_stringl(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length, const char *value, size_t value_length); +ZEND_API void zend_unset_property(const zend_class_entry *scope, zend_object *object, const char *name, size_t name_length); ZEND_API zend_result zend_update_static_property_ex(zend_class_entry *scope, zend_string *name, zval *value); ZEND_API zend_result zend_update_static_property(zend_class_entry *scope, const char *name, size_t name_length, zval *value); From 6b3f302132c7bb1d2e3e82c5d529af0cd9ec273b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Thu, 17 Jul 2025 23:01:43 +0200 Subject: [PATCH 7/8] streams: Re-add support for arbitrary metadata for stream notifier functions (#19158) Support for attaching arbitrary metadata for consumption by stream notifier functions got broken in php/php-src#19024 when the previous `zval ptr` got changed to `zend_fcall_info_cache *fcc`, making the data specific to the `user_space_stream_notifier`. Fix this by changing the field to `void *ptr`, avoiding the indirection through a `zval`, but preserving the ability to store arbitrary data. If necessary to support different types than `IS_PTR`, extensions can heap-allocate a `zval` to store within `->ptr` as a minimal change to keep compatibility with PHP 8.4 or lower. --- NEWS | 1 + UPGRADING.INTERNALS | 5 +++++ ext/standard/streamsfuncs.c | 20 +++++++++----------- main/streams/php_stream_context.h | 2 +- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index e317e0c234a3e..0ff1a5bbe0a75 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ PHP NEWS - Core: . Add clone-with support to the clone() function. (timwolla, edorian) + . Fix support for non-userland stream notifiers. (timwolla) - Curl: . Add support for CURLINFO_CONN_ID in curl_getinfo() (thecaliskan) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 7a14a4907ea62..0e0a0e94c9477 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -23,6 +23,11 @@ PHP 8.5 INTERNALS UPGRADE NOTES the user side when requiring libphp.so, by using dlmopen with LM_ID_NEWLM instead of dlopen. RTLD_DEEPBIND is still enabled when the Apache SAPI is in use. + . The ptr field of the php_stream_notifier struct is now a void* instead + of a zval. If the zval was used to store IS_PTR values only, the + extra layer of indirection can be removed. In other cases a zval can + be heap-allocated and stored in the pointer as a minimal change to keep + compatibility. - Zend . Added zend_safe_assign_to_variable_noref() function to safely assign diff --git a/ext/standard/streamsfuncs.c b/ext/standard/streamsfuncs.c index 2e38965232c74..bf7cbc9fa4623 100644 --- a/ext/standard/streamsfuncs.c +++ b/ext/standard/streamsfuncs.c @@ -869,19 +869,17 @@ static void user_space_stream_notifier(php_stream_context *context, int notifyco ZVAL_LONG(&zvs[4], bytes_sofar); ZVAL_LONG(&zvs[5], bytes_max); - zend_call_known_fcc(context->notifier->fcc, NULL, 6, zvs, NULL); + zend_call_known_fcc(context->notifier->ptr, NULL, 6, zvs, NULL); /* Free refcounted string parameter */ zval_ptr_dtor_str(&zvs[2]); } static void user_space_stream_notifier_dtor(php_stream_notifier *notifier) { - ZEND_ASSERT(notifier); - ZEND_ASSERT(notifier->fcc); - ZEND_ASSERT(notifier->fcc->function_handler); - zend_fcc_dtor(notifier->fcc); - efree(notifier->fcc); - notifier->fcc = NULL; + zend_fcall_info_cache *fcc = notifier->ptr; + zend_fcc_dtor(fcc); + efree(notifier->ptr); + notifier->ptr = NULL; } static zend_result parse_context_options(php_stream_context *context, HashTable *options) @@ -931,7 +929,7 @@ static zend_result parse_context_params(php_stream_context *context, HashTable * context->notifier = php_stream_notification_alloc(); context->notifier->func = user_space_stream_notifier; - context->notifier->fcc = fcc; + context->notifier->ptr = fcc; context->notifier->dtor = user_space_stream_notifier_dtor; } if (NULL != (tmp = zend_hash_str_find(params, "options", sizeof("options")-1))) { @@ -1128,10 +1126,10 @@ PHP_FUNCTION(stream_context_get_params) } array_init(return_value); - if (context->notifier && context->notifier->fcc) { - ZEND_ASSERT(context->notifier->func == user_space_stream_notifier); + if (context->notifier && context->notifier->func == user_space_stream_notifier) { + zend_fcall_info_cache *fcc = context->notifier->ptr; zval fn; - zend_get_callable_zval_from_fcc(context->notifier->fcc, &fn); + zend_get_callable_zval_from_fcc(fcc, &fn); add_assoc_zval_ex(return_value, ZEND_STRL("notification"), &fn); } Z_TRY_ADDREF(context->options); diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index f61604c929d78..b983bbb10efe2 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -44,7 +44,7 @@ typedef struct _php_stream_notifier php_stream_notifier; struct _php_stream_notifier { php_stream_notification_func func; void (*dtor)(php_stream_notifier *notifier); - zend_fcall_info_cache *fcc; + void *ptr; int mask; size_t progress, progress_max; /* position for progress notification */ }; From aadd7243626b5a2702d1a2154e6addb0e94b8f74 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 17 Jul 2025 23:00:05 +0100 Subject: [PATCH 8/8] ext/intl: Refactor IntlRuleBasedBreakIterator::__construct() (#19164) There is no need to delegate this to a seperate function and overwrite the error handler to promote warnings --- .../rulebasedbreakiterator_methods.cpp | 40 +++++-------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp index c84972fe5b98c..e26b4c07ac33f 100644 --- a/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp +++ b/ext/intl/breakiterator/rulebasedbreakiterator_methods.cpp @@ -32,17 +32,16 @@ static inline RuleBasedBreakIterator *fetch_rbbi(BreakIterator_object *bio) { return (RuleBasedBreakIterator*)bio->biter; } -static void _php_intlrbbi_constructor_body(INTERNAL_FUNCTION_PARAMETERS, zend_error_handling *error_handling, bool *error_handling_replaced) +U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) { - char *rules; - size_t rules_len; - bool compiled = false; - UErrorCode status = U_ZERO_ERROR; + zend_string *rules; + bool compiled = false; + UErrorCode status = U_ZERO_ERROR; BREAKITER_METHOD_INIT_VARS; object = ZEND_THIS; ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STRING(rules, rules_len) + Z_PARAM_STR(rules) Z_PARAM_OPTIONAL Z_PARAM_BOOL(compiled) ZEND_PARSE_PARAMETERS_END(); @@ -53,20 +52,14 @@ static void _php_intlrbbi_constructor_body(INTERNAL_FUNCTION_PARAMETERS, zend_er RETURN_THROWS(); } - zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, error_handling); - *error_handling_replaced = 1; - - // instantiation of ICU object RuleBasedBreakIterator *rbbi; if (!compiled) { UnicodeString rulesStr; UParseError parseError = UParseError(); - if (intl_stringFromChar(rulesStr, rules, rules_len, &status) - == FAILURE) { + if (intl_stringFromChar(rulesStr, ZSTR_VAL(rules), ZSTR_LEN(rules), &status) == FAILURE) { zend_throw_exception(IntlException_ce_ptr, - "IntlRuleBasedBreakIterator::__construct(): " - "rules were not a valid UTF-8 string", 0); + "IntlRuleBasedBreakIterator::__construct(): rules were not a valid UTF-8 string", 0); RETURN_THROWS(); } @@ -84,29 +77,16 @@ static void _php_intlrbbi_constructor_body(INTERNAL_FUNCTION_PARAMETERS, zend_er RETURN_THROWS(); } } else { // compiled - rbbi = new RuleBasedBreakIterator((uint8_t*)rules, rules_len, status); + rbbi = new RuleBasedBreakIterator(reinterpret_cast(ZSTR_VAL(rules)), ZSTR_LEN(rules), status); if (U_FAILURE(status)) { zend_throw_exception(IntlException_ce_ptr, - "IntlRuleBasedBreakIterator::__construct(): " - "unable to create instance from compiled rules", 0); + "IntlRuleBasedBreakIterator::__construct(): unable to create instance from compiled rules", 0); delete rbbi; RETURN_THROWS(); } } - breakiterator_object_create(return_value, rbbi, 0); -} - -U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, __construct) -{ - zend_error_handling error_handling; - bool error_handling_replaced = 0; - - return_value = ZEND_THIS; - _php_intlrbbi_constructor_body(INTERNAL_FUNCTION_PARAM_PASSTHRU, &error_handling, &error_handling_replaced); - if (error_handling_replaced) { - zend_restore_error_handling(&error_handling); - } + breakiterator_object_create(object, rbbi, false); } U_CFUNC PHP_METHOD(IntlRuleBasedBreakIterator, getRules)