diff --git a/Zend/tests/offsets/ArrayAccess_container_offset_behaviour.phpt b/Zend/tests/offsets/ArrayAccess_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..189dc0c1a2e6a --- /dev/null +++ b/Zend/tests/offsets/ArrayAccess_container_offset_behaviour.phpt @@ -0,0 +1,132 @@ +--TEST-- +ArrayAccess containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt b/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..2e43d57d18409 --- /dev/null +++ b/Zend/tests/offsets/ArrayObject_container_offset_behaviour.phpt @@ -0,0 +1,314 @@ +--TEST-- +ArrayObject containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/appending_containers.phpt b/Zend/tests/offsets/appending_containers.phpt new file mode 100644 index 0000000000000..0372c28bf1841 --- /dev/null +++ b/Zend/tests/offsets/appending_containers.phpt @@ -0,0 +1,75 @@ +--TEST-- +Appending containers +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECTF-- +NULL container: +array(1) { + [0]=> + string(5) "value" +} +false container: + +Deprecated: Automatic conversion of false to array is deprecated in %s on line %d +array(1) { + [0]=> + string(5) "value" +} +true container: +Cannot use a scalar value as an array +4 container: +Cannot use a scalar value as an array +5.5 container: +Cannot use a scalar value as an array +'10' container: +[] operator not supported for strings +'25.5' container: +[] operator not supported for strings +'string' container: +[] operator not supported for strings +[] container: +array(1) { + [0]=> + string(5) "value" +} +STDERR container: +Cannot use a scalar value as an array +new stdClass() container: +Cannot use object of type stdClass as array +new ArrayObject() container: +object(ArrayObject)#2 (1) { + ["storage":"ArrayObject":private]=> + array(1) { + [0]=> + string(5) "value" + } +} +new A() container: +string(12) "A::offsetSet" +NULL +string(5) "value" +object(A)#3 (0) { +} +new B() container: +string(12) "B::offsetSet" +NULL +string(5) "value" +object(B)#4 (1) { + ["storage":"ArrayObject":private]=> + array(0) { + } +} diff --git a/Zend/tests/offsets/appending_containers_in_fetch.phpt b/Zend/tests/offsets/appending_containers_in_fetch.phpt new file mode 100644 index 0000000000000..e503b563fbce9 --- /dev/null +++ b/Zend/tests/offsets/appending_containers_in_fetch.phpt @@ -0,0 +1,79 @@ +--TEST-- +Appending containers via a fetch operation $c[][5] = $v; +--FILE-- +getMessage(), "\n"; + } +} + +?> +--EXPECTF-- +NULL container: +array(1) { + [0]=> + array(1) { + [5]=> + string(5) "value" + } +} +false container: + +Deprecated: Automatic conversion of false to array is deprecated in %s on line %d +array(1) { + [0]=> + array(1) { + [5]=> + string(5) "value" + } +} +true container: +Error: Cannot use a scalar value as an array +4 container: +Error: Cannot use a scalar value as an array +5.5 container: +Error: Cannot use a scalar value as an array +'10' container: +Error: [] operator not supported for strings +'25.5' container: +Error: [] operator not supported for strings +'string' container: +Error: [] operator not supported for strings +[] container: +array(1) { + [0]=> + array(1) { + [5]=> + string(5) "value" + } +} +STDERR container: +Error: Cannot use a scalar value as an array +new stdClass() container: +Error: Cannot use object of type stdClass as array +new ArrayObject() container: + +Notice: Indirect modification of overloaded element of ArrayObject has no effect in %s on line %d +object(ArrayObject)#2 (1) { + ["storage":"ArrayObject":private]=> + array(0) { + } +} +new A() container: +string(12) "A::offsetGet" +NULL + +Notice: Indirect modification of overloaded element of A has no effect in %s on line %d +Error: Cannot use a scalar value as an array +new B() container: + +Notice: Indirect modification of overloaded element of B has no effect in %s on line %d +ArgumentCountError: B::offsetGet(): Argument #1 ($offset) not passed diff --git a/Zend/tests/offsets/array_container_offset_behaviour.phpt b/Zend/tests/offsets/array_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..c39b8ca1432dd --- /dev/null +++ b/Zend/tests/offsets/array_container_offset_behaviour.phpt @@ -0,0 +1,268 @@ +--TEST-- +array containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/false_container_offset_behaviour.phpt b/Zend/tests/offsets/false_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..84be74523629e --- /dev/null +++ b/Zend/tests/offsets/false_container_offset_behaviour.phpt @@ -0,0 +1,275 @@ +--TEST-- +false containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/internal_handlers.phpt b/Zend/tests/offsets/internal_handlers.phpt new file mode 100644 index 0000000000000..d400a7b39beaf --- /dev/null +++ b/Zend/tests/offsets/internal_handlers.phpt @@ -0,0 +1,256 @@ +--TEST-- +Internal handlers +--EXTENSIONS-- +zend_test +--FILE-- +getMessage(), PHP_EOL; +//} + +echo 'nested write', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $o['foo']['bar'] = true; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +echo 'nested write: appending then write', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $o[]['bar'] = true; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +echo 'nested read-write', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $o['foo']['bar'] += 10; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +echo 'nested read-write: appending then write', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $o[]['bar'] += 10; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +echo 'nested isset', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $r = isset($o['foo']['bar']); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +// Illegal +//echo 'nested isset: appending then read', PHP_EOL; +//try { +// $o = new DimensionHandlersNoArrayAccess(); +// $r = isset($o[]['bar']); +//} catch (\Throwable $e) { +// echo $e::class, ': ', $e->getMessage(), PHP_EOL; +//} +//exportObject($o); + +echo 'nested empty', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $r = empty($o['foo']['bar']); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +// Illegal +//echo 'nested empty: appending then read', PHP_EOL; +//try { +// $o = new DimensionHandlersNoArrayAccess(); +// $r = empty($o[]['bar']); +//} catch (\Throwable $e) { +// echo $e::class, ': ', $e->getMessage(), PHP_EOL; +//} +//exportObject($o); + +echo 'nested null coalescing', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $r = $o['foo']['bar'] ?? 'default'; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +// Illegal +//echo 'nested null coalescing: appending then read', PHP_EOL; +//try { +// $o = new DimensionHandlersNoArrayAccess(); +// $r = $o[]['bar'] ?? 'default'; +//} catch (\Throwable $e) { +// echo $e::class, ': ', $e->getMessage(), PHP_EOL; +//} +//exportObject($o); + +echo 'nested appending', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $o['foo'][] = true; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +echo 'nested appending: appending then append', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + $o[][] = true; +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +echo 'nested unset', PHP_EOL; +$o = new DimensionHandlersNoArrayAccess(); +try { + unset($o['foo']['bar']); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} +exportObject($o); + +// Illegal +//echo 'nested unset: appending then read', PHP_EOL; +//try { +// $o = new DimensionHandlersNoArrayAccess(); +// unset($o[]['bar']); +//} catch (\Throwable $e) { +// echo $e::class, ': ', $e->getMessage(), PHP_EOL; +//} +//exportObject($o); + +?> +--EXPECTF-- +read op +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_R, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +write op +DimensionHandlersNoArrayAccess, read: false, write: true, has: false, unset: false, readType: uninitialized, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +read-write op +DimensionHandlersNoArrayAccess, read: true, write: true, has: false, unset: false, readType: BP_VAR_R, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +isset op +DimensionHandlersNoArrayAccess, read: false, write: false, has: true, unset: false, readType: uninitialized, hasOffset: true, checkEmpty: 0, offset: 'foo' +empty op +DimensionHandlersNoArrayAccess, read: false, write: false, has: true, unset: false, readType: uninitialized, hasOffset: true, checkEmpty: 1, offset: 'foo' +null coalescing op +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_IS, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +appending op +DimensionHandlersNoArrayAccess, read: false, write: true, has: false, unset: false, readType: uninitialized, hasOffset: false, checkEmpty: uninitialized, offset: uninitialized +unset op +DimensionHandlersNoArrayAccess, read: false, write: false, has: false, unset: true, readType: uninitialized, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested read + +Warning: Trying to access array offset on true in %s on line %d +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_R, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested write + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot use a scalar value as an array +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_W, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested write: appending then write + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot use a scalar value as an array +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_W, hasOffset: false, checkEmpty: uninitialized, offset: uninitialized +nested read-write + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot use a scalar value as an array +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_RW, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested read-write: appending then write + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot use a scalar value as an array +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_RW, hasOffset: false, checkEmpty: uninitialized, offset: uninitialized +nested isset +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_IS, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested empty +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_IS, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested null coalescing +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_IS, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested appending + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot use a scalar value as an array +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_W, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +nested appending: appending then append + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot use a scalar value as an array +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_W, hasOffset: false, checkEmpty: uninitialized, offset: uninitialized +nested unset + +Notice: Indirect modification of overloaded element of DimensionHandlersNoArrayAccess has no effect in %s on line %d +Error: Cannot unset offset in a non-array variable +DimensionHandlersNoArrayAccess, read: true, write: false, has: false, unset: false, readType: BP_VAR_UNSET, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' diff --git a/Zend/tests/offsets/internal_handlers_extended.phpt b/Zend/tests/offsets/internal_handlers_extended.phpt new file mode 100644 index 0000000000000..4877c2fc22a6f --- /dev/null +++ b/Zend/tests/offsets/internal_handlers_extended.phpt @@ -0,0 +1,46 @@ +--TEST-- +Internal handlers that do not explicitly support userland ArrayAccess +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +NoImplement, read: true, write: false, has: false, unset: false, readType: BP_VAR_R, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' +DoImplement, read: true, write: false, has: false, unset: false, readType: BP_VAR_R, hasOffset: true, checkEmpty: uninitialized, offset: 'foo' diff --git a/Zend/tests/offsets/invalid_container_offset_behaviour.phpt b/Zend/tests/offsets/invalid_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..a3d41bd3743b7 --- /dev/null +++ b/Zend/tests/offsets/invalid_container_offset_behaviour.phpt @@ -0,0 +1,92 @@ +--TEST-- +Invalid containers with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/null_container_offset_behaviour.phpt b/Zend/tests/offsets/null_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..ec3f253e58a5b --- /dev/null +++ b/Zend/tests/offsets/null_container_offset_behaviour.phpt @@ -0,0 +1,267 @@ +--TEST-- +null containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/object_container_offset_behaviour.phpt b/Zend/tests/offsets/object_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..5f1569ced0014 --- /dev/null +++ b/Zend/tests/offsets/object_container_offset_behaviour.phpt @@ -0,0 +1,72 @@ +--TEST-- +object containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/runtime_compile_time_offset_access.phpt b/Zend/tests/offsets/runtime_compile_time_offset_access.phpt new file mode 100644 index 0000000000000..16bca6d6202c9 --- /dev/null +++ b/Zend/tests/offsets/runtime_compile_time_offset_access.phpt @@ -0,0 +1,82 @@ +--TEST-- +Test binary operands exposing the same behavior at compile as at run time +--INI-- +memory_limit=256M +opcache.file_update_protection=1 +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/string_container_offset_behaviour.phpt b/Zend/tests/offsets/string_container_offset_behaviour.phpt new file mode 100644 index 0000000000000..695008c5dbc28 --- /dev/null +++ b/Zend/tests/offsets/string_container_offset_behaviour.phpt @@ -0,0 +1,637 @@ +--TEST-- +String containers behaviour with offsets +--FILE-- + +--EXPECT-- +Executed tests diff --git a/Zend/tests/offsets/test_offset_helpers.inc b/Zend/tests/offsets/test_offset_helpers.inc new file mode 100644 index 0000000000000..8d4ccadef56ac --- /dev/null +++ b/Zend/tests/offsets/test_offset_helpers.inc @@ -0,0 +1,204 @@ +isInitialized($o)) { + if ($p === 'readType') { + echo match($o->$p) { + 0 => 'BP_VAR_R', + 1 => 'BP_VAR_W', + 2 => 'BP_VAR_RW', + 3 => 'BP_VAR_IS', + 4 => 'BP_VAR_FUNC_ARG', + 5 => 'BP_VAR_UNSET', + }; + } else { + echo zend_test_var_export($o->$p); + } + } else { + echo 'uninitialized'; + } +} + +function exportObject(object $o) { + echo $o::class; + foreach (get_class_vars($o::class) as $p => $v) { + exportProp($o, $p); + } + echo "\n"; +} + +/* Taken from run-tests.php */ +function expectf_to_regex(string $wanted): string +{ + $wanted_re = preg_quote($wanted); + return strtr($wanted_re, [ + '%e' => preg_quote(DIRECTORY_SEPARATOR, '/'), + '%s' => '[^\r\n]+', + '%S' => '[^\r\n]*', + '%a' => '.+', + '%A' => '.*', + '%w' => '\s*', + '%i' => '[+-]?\d+', + '%d' => '\d+', + '%x' => '[0-9a-fA-F]+', + '%f' => '[+-]?(?:\d+|(?=\.\d))(?:\.\d+)?(?:[Ee][+-]?\d+)?', + '%F' => '([+-]?(?:\d+|(?=\.\d))(?:\.\d+)?(?:[Ee][+-]?\d+)?)|(NAN)|(INF)', + '%c' => '.', + '%0' => '\x00', + ]); +} + +class A implements ArrayAccess { + public function offsetSet($offset, $value): void { + var_dump(__METHOD__); + var_dump($offset); + var_dump($value); + } + public function offsetGet($offset): mixed { + var_dump(__METHOD__); + var_dump($offset); + return 5; + } + public function offsetUnset($offset): void { + var_dump(__METHOD__); + var_dump($offset); + } + public function offsetExists($offset): bool { + var_dump(__METHOD__); + var_dump($offset); + return true; + } +} + +class B extends ArrayObject { + public function offsetSet($offset, $value): void { + var_dump(__METHOD__); + var_dump($offset); + var_dump($value); + } + public function offsetGet($offset): mixed { + var_dump(__METHOD__); + var_dump($offset); + return 5; + } + public function offsetUnset($offset): void { + var_dump(__METHOD__); + var_dump($offset); + } + public function offsetExists($offset): bool { + var_dump(__METHOD__); + var_dump($offset); + return true; + } + public function append(mixed $value) : void{ + var_dump(__METHOD__); + var_dump($value); + } +} + +$containers = [ + null, + false, + true, + 4, + 5.5, + '10', + '25.5', + 'string', + [], + STDERR, + new stdClass(), + new ArrayObject(), + new A(), + new B(), +]; + +$offsets = [ + null, + false, + true, + 4, + 5.5, + 6.0, + -12, + -13.5, + -14.0, + //PHP_INT_MAX, + //PHP_INT_MIN, + PHP_INT_MAX * 2, + PHP_INT_MIN * 2, + INF, + -INF, + NAN, + 'string', + '7', + '8.5', + '9.0', + '2e3', + '20a', + ' 21', + '22 ', + //"9179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368", + //"-9179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368", + '0x14', + '-15', + '-16.5', + '-17.0', + (string) PHP_INT_MAX * 2, + (string) PHP_INT_MIN * 2, + [], + STDERR, + new stdClass(), + new ArrayObject(), + new A(), + new B(), +]; + +$failures = []; +$failuresNb = 0; +$testCasesTotal = 0; + +$var_dim_filename = __DIR__ . DIRECTORY_SEPARATOR . 'test_variable_offsets.inc'; diff --git a/Zend/tests/offsets/test_variable_offsets.inc b/Zend/tests/offsets/test_variable_offsets.inc new file mode 100644 index 0000000000000..cb22aaf51dc37 --- /dev/null +++ b/Zend/tests/offsets/test_variable_offsets.inc @@ -0,0 +1,105 @@ +getMessage(), "\n"; +} +// Write +try { + echo "Write:\n"; + $container[$dimension] = 5; +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Read +try { + echo "Read:\n"; + var_dump($container[$dimension]); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Read-Write +try { + echo "Read-Write:\n"; + $container[$dimension] += 20; +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Is +try { + echo "isset():\n"; + var_dump(isset($container[$dimension])); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + echo "empty():\n"; + var_dump(empty($container[$dimension])); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + echo "null coalesce:\n"; + var_dump($container[$dimension] ?? 'default'); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Unset +try { + echo "unset():\n"; + unset($container[$dimension]); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Nested read +try { + echo "Nested read:\n"; + var_dump($container[$dimension][$dimension]); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Nested write +try { + echo "Nested write:\n"; + $container[$dimension][$dimension] = 5; +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Nested Read-Write +try { + echo "Nested Read-Write:\n"; + $container[$dimension][$dimension] += 25; +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Nested Is +try { + echo "Nested isset():\n"; + var_dump(isset($container[$dimension][$dimension])); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + echo "Nested empty():\n"; + var_dump(empty($container[$dimension][$dimension])); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + echo "Nested null coalesce:\n"; + var_dump($container[$dimension][$dimension] ?? 'default'); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} +// Nested unset +try { + echo "Nested unset():\n"; + unset($container[$dimension][$dimension]); +} catch (\Throwable $e) { + echo $e->getMessage(), "\n"; +} diff --git a/ext/zend_test/object_handlers.c b/ext/zend_test/object_handlers.c index b590c3488c1ba..15e362605f8ec 100644 --- a/ext/zend_test/object_handlers.c +++ b/ext/zend_test/object_handlers.c @@ -232,6 +232,52 @@ ZEND_METHOD(NumericCastableNoOperations, __construct) ZVAL_COPY(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), n); } +static zend_class_entry *dimension_handlers_no_ArrayAccess_ce; +static zend_object_handlers dimension_handlers_no_ArrayAccess_object_handlers; + +static zend_object* dimension_handlers_no_ArrayAccess_object_create(zend_class_entry* ce) { + zend_object *object = zend_objects_new(ce); + object_properties_init(object, ce); + object->handlers = &dimension_handlers_no_ArrayAccess_object_handlers; + return object; +} + +static void dimension_common_helper(zend_object *object, zval *offset, int prop_access_type) { + ZVAL_BOOL(OBJ_PROP_NUM(object, prop_access_type), true); + /* hasOffset */ + ZVAL_BOOL(OBJ_PROP_NUM(object, 5), offset != NULL); + if (offset) { + ZVAL_COPY(OBJ_PROP_NUM(object, 7), offset); + } +} + +static zval* dimension_handlers_no_ArrayAccess_read_dimension(zend_object *object, zval *offset, int type, zval *rv) { + dimension_common_helper(object, offset, 0); + /* ReadType */ + ZVAL_LONG(OBJ_PROP_NUM(object, 4), type); + + /* Normal logic */ + ZVAL_BOOL(rv, true); + return rv; +} + +static void dimension_handlers_no_ArrayAccess_write_dimension(zend_object *object, zval *offset, zval *value) { + dimension_common_helper(object, offset, 1); +} + +static int dimension_handlers_no_ArrayAccess_has_dimension(zend_object *object, zval *offset, int check_empty) { + /* checkEmpty */ + ZVAL_LONG(OBJ_PROP_NUM(object, 6), check_empty); + dimension_common_helper(object, offset, 2); + + /* Normal logic */ + return 1; +} + +static void dimension_handlers_no_ArrayAccess_unset_dimension(zend_object *object, zval *offset) { + dimension_common_helper(object, offset, 3); +} + void zend_test_object_handlers_init(void) { /* DoOperationNoCast class */ @@ -255,4 +301,12 @@ void zend_test_object_handlers_init(void) numeric_castable_no_operation_ce->create_object = numeric_castable_no_operation_object_create; memcpy(&numeric_castable_no_operation_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); numeric_castable_no_operation_object_handlers.cast_object = numeric_castable_no_operation_cast_object; + + dimension_handlers_no_ArrayAccess_ce = register_class_DimensionHandlersNoArrayAccess(); + dimension_handlers_no_ArrayAccess_ce->create_object = dimension_handlers_no_ArrayAccess_object_create; + memcpy(&dimension_handlers_no_ArrayAccess_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + dimension_handlers_no_ArrayAccess_object_handlers.read_dimension = dimension_handlers_no_ArrayAccess_read_dimension; + dimension_handlers_no_ArrayAccess_object_handlers.write_dimension = dimension_handlers_no_ArrayAccess_write_dimension; + dimension_handlers_no_ArrayAccess_object_handlers.has_dimension = dimension_handlers_no_ArrayAccess_has_dimension; + dimension_handlers_no_ArrayAccess_object_handlers.unset_dimension = dimension_handlers_no_ArrayAccess_unset_dimension; } diff --git a/ext/zend_test/object_handlers.stub.php b/ext/zend_test/object_handlers.stub.php index 81830532689e6..a474908b1095d 100644 --- a/ext/zend_test/object_handlers.stub.php +++ b/ext/zend_test/object_handlers.stub.php @@ -22,3 +22,14 @@ final class NumericCastableNoOperations { private int|float $val; public function __construct(int|float $val) {} } + +class DimensionHandlersNoArrayAccess { + public bool $read = false; + public bool $write = false; + public bool $has = false; + public bool $unset = false; + public int $readType; + public bool $hasOffset = false; + public int $checkEmpty; + public mixed $offset; +} diff --git a/ext/zend_test/object_handlers_arginfo.h b/ext/zend_test/object_handlers_arginfo.h index 1c66036a3038c..0e6321c9c1ac7 100644 --- a/ext/zend_test/object_handlers_arginfo.h +++ b/ext/zend_test/object_handlers_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 164cdd464289c8db351f4ec49979a66d44ba3e87 */ + * Stub hash: 81be60f2c465ffe5c036739d072ab80d9c388907 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_DoOperationNoCast___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, val, IS_LONG, 0) @@ -45,6 +45,11 @@ static const zend_function_entry class_NumericCastableNoOperations_methods[] = { ZEND_FE_END }; + +static const zend_function_entry class_DimensionHandlersNoArrayAccess_methods[] = { + ZEND_FE_END +}; + static zend_class_entry *register_class_DoOperationNoCast(void) { zend_class_entry ce, *class_entry; @@ -112,3 +117,61 @@ static zend_class_entry *register_class_NumericCastableNoOperations(void) return class_entry; } + +static zend_class_entry *register_class_DimensionHandlersNoArrayAccess(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "DimensionHandlersNoArrayAccess", class_DimensionHandlersNoArrayAccess_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + zval property_read_default_value; + ZVAL_FALSE(&property_read_default_value); + zend_string *property_read_name = zend_string_init("read", sizeof("read") - 1, 1); + zend_declare_typed_property(class_entry, property_read_name, &property_read_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_read_name); + + zval property_write_default_value; + ZVAL_FALSE(&property_write_default_value); + zend_string *property_write_name = zend_string_init("write", sizeof("write") - 1, 1); + zend_declare_typed_property(class_entry, property_write_name, &property_write_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_write_name); + + zval property_has_default_value; + ZVAL_FALSE(&property_has_default_value); + zend_string *property_has_name = zend_string_init("has", sizeof("has") - 1, 1); + zend_declare_typed_property(class_entry, property_has_name, &property_has_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_has_name); + + zval property_unset_default_value; + ZVAL_FALSE(&property_unset_default_value); + zend_string *property_unset_name = zend_string_init("unset", sizeof("unset") - 1, 1); + zend_declare_typed_property(class_entry, property_unset_name, &property_unset_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_unset_name); + + zval property_readType_default_value; + ZVAL_UNDEF(&property_readType_default_value); + zend_string *property_readType_name = zend_string_init("readType", sizeof("readType") - 1, 1); + zend_declare_typed_property(class_entry, property_readType_name, &property_readType_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_readType_name); + + zval property_hasOffset_default_value; + ZVAL_FALSE(&property_hasOffset_default_value); + zend_string *property_hasOffset_name = zend_string_init("hasOffset", sizeof("hasOffset") - 1, 1); + zend_declare_typed_property(class_entry, property_hasOffset_name, &property_hasOffset_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_BOOL)); + zend_string_release(property_hasOffset_name); + + zval property_checkEmpty_default_value; + ZVAL_UNDEF(&property_checkEmpty_default_value); + zend_string *property_checkEmpty_name = zend_string_init("checkEmpty", sizeof("checkEmpty") - 1, 1); + zend_declare_typed_property(class_entry, property_checkEmpty_name, &property_checkEmpty_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_checkEmpty_name); + + zval property_offset_default_value; + ZVAL_UNDEF(&property_offset_default_value); + zend_string *property_offset_name = zend_string_init("offset", sizeof("offset") - 1, 1); + zend_declare_typed_property(class_entry, property_offset_name, &property_offset_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ANY)); + zend_string_release(property_offset_name); + + return class_entry; +}