1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Zend: Add tests for offsets and containers (#12723)

Add various tests showcasing the behavioural variations of different offset types used on various container types in all possible operations:
 - Read
 - Write
 - Read Write
 - Appending
 - Unsetting
 - Existence checks with isset()/empty/null coalesce operator
 - Behaviour of nesting dimensions (e.g. $container[$offset1][$offset2])

Add a test to ensure compile time and runtime behaviour is identical for offsets.

Add an internal class that overloads the dimension object handlers to zend_test
This commit is contained in:
Gina Peter Banyard
2024-01-25 15:06:59 +00:00
committed by GitHub
parent 8128d17cc2
commit a479ed7cc5
18 changed files with 3033 additions and 1 deletions

View File

@@ -0,0 +1,132 @@
--TEST--
ArrayAccess containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT = <<<OUTPUT
Read before write:
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
int(5)
Write:
string(12) "CLASS_NAME::offsetSet"
VAR_DUMP_OF_OFFSET
int(5)
Read:
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
int(5)
Read-Write:
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
string(12) "CLASS_NAME::offsetSet"
VAR_DUMP_OF_OFFSET
int(25)
isset():
string(15) "CLASS_NAME::offsetExists"
VAR_DUMP_OF_OFFSET
bool(true)
empty():
string(15) "CLASS_NAME::offsetExists"
VAR_DUMP_OF_OFFSET
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
bool(false)
null coalesce:
string(15) "CLASS_NAME::offsetExists"
VAR_DUMP_OF_OFFSET
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
int(5)
unset():
string(14) "CLASS_NAME::offsetUnset"
VAR_DUMP_OF_OFFSET
Nested read:
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
Warning: Trying to access array offset on int in %s on line 62
NULL
Nested write:
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
Notice: Indirect modification of overloaded element of CLASS_NAME has no effect in %s on line 69
Cannot use a scalar value as an array
Nested Read-Write:
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
Notice: Indirect modification of overloaded element of CLASS_NAME has no effect in %s on line 76
Cannot use a scalar value as an array
Nested isset():
string(15) "CLASS_NAME::offsetExists"
VAR_DUMP_OF_OFFSET
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
bool(false)
Nested empty():
string(15) "CLASS_NAME::offsetExists"
VAR_DUMP_OF_OFFSET
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
bool(true)
Nested null coalesce:
string(15) "CLASS_NAME::offsetExists"
VAR_DUMP_OF_OFFSET
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
string(7) "default"
Nested unset():
string(12) "CLASS_NAME::offsetGet"
VAR_DUMP_OF_OFFSET
Notice: Indirect modification of overloaded element of CLASS_NAME has no effect in %s on line 102
Cannot unset offset in a non-array variable
OUTPUT;
ob_start();
foreach (['A', 'B'] as $class) {
foreach ($offsets as $dimension) {
$container = new $class();
$error = "(new $class())[" . zend_test_var_export($dimension) . '] has different outputs' . "\n";
ob_start();
var_dump($dimension);
$var_dump_output = ob_get_clean();
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
$expected_output = str_replace(
["VAR_DUMP_OF_OFFSET\n", "CLASS_NAME"],
[$var_dump_output, $class],
EXPECTED_OUTPUT
);
if ($varOutput !== $expected_output) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_ArrayAccess_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,314 @@
--TEST--
ArrayObject containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT_VALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Undefined array key %s in %s on line 8
NULL
Write:
Read:
int(5)
Read-Write:
isset():
bool(true)
empty():
bool(false)
null coalesce:
int(25)
unset():
Nested read:
Warning: Undefined array key %s in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Nested Read-Write:
Nested isset():
bool(true)
Nested empty():
bool(false)
Nested null coalesce:
int(30)
Nested unset():
OUTPUT;
$EXPECTED_OUTPUT_VALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_VALID_OFFSETS) . '$/s';
const EXPECTF_OUTPUT_FLOAT_OFFSETS = <<<OUTPUT
Read before write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 8
Warning: Undefined array key %s in %s on line 8
NULL
Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 15
Read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 22
int(15)
Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 29
isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 36
bool(true)
empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 42
bool(false)
null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 48
int(35)
unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 55
Nested read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 62
Warning: Undefined array key 0 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Nested Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Nested isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
bool(true)
Nested empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
bool(false)
Nested null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
int(25)
Nested unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
OUTPUT;
$EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTF_OUTPUT_FLOAT_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS = <<<OUTPUT
Read before write:
Cannot access offset of type %s on ArrayObject
Write:
Cannot access offset of type %s on ArrayObject
Read:
Cannot access offset of type %s on ArrayObject
Read-Write:
Cannot access offset of type %s on ArrayObject
isset():
Cannot access offset of type %s in isset or empty
empty():
Cannot access offset of type %s in isset or empty
null coalesce:
Cannot access offset of type %s in isset or empty
unset():
Cannot unset offset of type %s on ArrayObject
Nested read:
Cannot access offset of type %s on ArrayObject
Nested write:
Cannot access offset of type %s on ArrayObject
Nested Read-Write:
Cannot access offset of type %s on ArrayObject
Nested isset():
Cannot access offset of type %s in isset or empty
Nested empty():
Cannot access offset of type %s in isset or empty
Nested null coalesce:
Cannot access offset of type %s in isset or empty
Nested unset():
Notice: Indirect modification of overloaded element of ArrayObject has no effect in %s on line 102
Cannot unset offset of type %s on ArrayObject
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_NULL_OFFSET = <<<OUTPUT
Read before write:
Warning: Undefined array key "" in %s on line 8
NULL
Write:
Read:
Warning: Undefined array key "" in %s on line 22
NULL
Read-Write:
Warning: Undefined array key "" in %s on line 29
isset():
bool(false)
empty():
bool(true)
null coalesce:
string(7) "default"
unset():
Nested read:
Warning: Undefined array key "" in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Nested Read-Write:
Nested isset():
bool(true)
Nested empty():
bool(false)
Nested null coalesce:
int(30)
Nested unset():
OUTPUT;
const EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS = <<<OUTPUT
Read before write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 8
Warning: Undefined array key 3 in %s on line 8
NULL
Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 15
Read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 22
int(5)
Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 29
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 29
isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 36
bool(true)
empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 42
bool(false)
null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 48
int(25)
unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 55
Nested read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 62
Warning: Undefined array key 3 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Nested Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Nested isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
bool(true)
Nested empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
bool(false)
Nested null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
int(30)
Nested unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
OUTPUT;
ob_start();
foreach ($offsets as $dimension) {
$container = new ArrayObject();
$error = '(new ArrayObject())[' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
ob_start();
var_dump($dimension);
$var_dump_output = ob_get_clean();
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if (
!preg_match($EXPECTED_OUTPUT_VALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX, $varOutput)
&& $varOutput !== EXPECTED_OUTPUT_NULL_OFFSET
&& $varOutput !== EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS
) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_ArrayObject_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,75 @@
--TEST--
Appending containers
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
foreach ($containers as $container) {
echo zend_test_var_export($container), " container:\n";
try {
$container[] = 'value';
var_dump($container);
} catch (\Throwable $e) {
echo $e->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) {
}
}

View File

@@ -0,0 +1,79 @@
--TEST--
Appending containers via a fetch operation $c[][5] = $v;
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
foreach ($containers as $container) {
echo zend_test_var_export($container), " container:\n";
try {
$container[][5] = 'value';
var_dump($container);
} catch (\Throwable $e) {
echo $e::class, ': ', $e->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

View File

@@ -0,0 +1,268 @@
--TEST--
array containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT_VALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Undefined array key %s in %s on line 8
NULL
Write:
Read:
int(5)
Read-Write:
isset():
bool(true)
empty():
bool(false)
null coalesce:
int(25)
unset():
Nested read:
Warning: Undefined array key %s in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Nested Read-Write:
Nested isset():
bool(true)
Nested empty():
bool(false)
Nested null coalesce:
int(30)
Nested unset():
OUTPUT;
$EXPECTED_OUTPUT_VALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_VALID_OFFSETS) . '$/s';
const EXPECTF_OUTPUT_FLOAT_OFFSETS = <<<OUTPUT
Read before write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 8
Warning: Undefined array key %s in %s on line 8
NULL
Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 15
Read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 22
int(15)
Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 29
isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 36
bool(true)
empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 42
bool(false)
null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 48
int(35)
unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 55
Nested read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 62
Warning: Undefined array key 0 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Nested Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Nested isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
bool(true)
Nested empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
bool(false)
Nested null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
int(25)
Nested unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
OUTPUT;
$EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTF_OUTPUT_FLOAT_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS = <<<OUTPUT
Read before write:
Cannot access offset of type %s on array
Write:
Cannot access offset of type %s on array
Read:
Cannot access offset of type %s on array
Read-Write:
Cannot access offset of type %s on array
isset():
Cannot access offset of type %s in isset or empty
empty():
Cannot access offset of type %s in isset or empty
null coalesce:
Cannot access offset of type %s on array
unset():
Cannot unset offset of type %s on array
Nested read:
Cannot access offset of type %s on array
Nested write:
Cannot access offset of type %s on array
Nested Read-Write:
Cannot access offset of type %s on array
Nested isset():
Cannot access offset of type %s on array
Nested empty():
Cannot access offset of type %s on array
Nested null coalesce:
Cannot access offset of type %s on array
Nested unset():
Cannot access offset of type %s on array
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS = <<<OUTPUT
Read before write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 8
Warning: Undefined array key 3 in %s on line 8
NULL
Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 15
Read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 22
int(5)
Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 29
isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 36
bool(true)
empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 42
bool(false)
null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 48
int(25)
unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 55
Nested read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 62
Warning: Undefined array key 3 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Nested Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Nested isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
bool(true)
Nested empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
bool(false)
Nested null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
int(30)
Nested unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
OUTPUT;
ob_start();
foreach ($offsets as $dimension) {
$container = [];
$error = '[][' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if (
!preg_match($EXPECTED_OUTPUT_VALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX, $varOutput)
&& $varOutput !== EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS
) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_array_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,275 @@
--TEST--
false containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT_VALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on false in %s on line 8
NULL
Write:
Deprecated: Automatic conversion of false to array is deprecated in %s on line 15
Read:
int(5)
Read-Write:
isset():
bool(true)
empty():
bool(false)
null coalesce:
int(25)
unset():
Nested read:
Warning: Undefined array key %s in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Nested Read-Write:
Nested isset():
bool(true)
Nested empty():
bool(false)
Nested null coalesce:
int(30)
Nested unset():
OUTPUT;
$EXPECTED_OUTPUT_VALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_VALID_OFFSETS) . '$/s';
const EXPECTF_OUTPUT_FLOAT_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on false in %s on line 8
NULL
Write:
Deprecated: Automatic conversion of false to array is deprecated in %s on line 15
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 15
Read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 22
int(5)
Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 29
isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 36
bool(true)
empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 42
bool(false)
null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 48
int(25)
unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 55
Nested read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 62
Warning: Undefined array key 0 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Nested Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Nested isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
bool(true)
Nested empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
bool(false)
Nested null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
int(30)
Nested unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
OUTPUT;
$EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTF_OUTPUT_FLOAT_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on false in %s on line 8
NULL
Write:
Deprecated: Automatic conversion of false to array is deprecated in %s on line 15
Cannot access offset of type %s on array
Read:
Cannot access offset of type %s on array
Read-Write:
Cannot access offset of type %s on array
isset():
Cannot access offset of type %s in isset or empty
empty():
Cannot access offset of type %s in isset or empty
null coalesce:
Cannot access offset of type %s on array
unset():
Cannot unset offset of type %s on array
Nested read:
Cannot access offset of type %s on array
Nested write:
Cannot access offset of type %s on array
Nested Read-Write:
Cannot access offset of type %s on array
Nested isset():
Cannot access offset of type %s on array
Nested empty():
Cannot access offset of type %s on array
Nested null coalesce:
Cannot access offset of type %s on array
Nested unset():
Cannot access offset of type %s on array
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on false in %s on line 8
NULL
Write:
Deprecated: Automatic conversion of false to array is deprecated in %s on line 15
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 15
Read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 22
int(5)
Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 29
isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 36
bool(true)
empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 42
bool(false)
null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 48
int(25)
unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 55
Nested read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 62
Warning: Undefined array key 3 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Nested Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Nested isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
bool(true)
Nested empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
bool(false)
Nested null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
int(30)
Nested unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
OUTPUT;
ob_start();
foreach ($offsets as $dimension) {
$container = false;
$error = 'false[' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if (
!preg_match($EXPECTED_OUTPUT_VALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX, $varOutput)
&& $varOutput !== EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS
) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_false_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,256 @@
--TEST--
Internal handlers
--EXTENSIONS--
zend_test
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
echo 'read op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$r = $o['foo'];
exportObject($o);
echo 'write op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$o['foo'] = true;
exportObject($o);
echo 'read-write op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$o['foo'] += 10;
exportObject($o);
echo 'isset op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$r = isset($o['foo']);
exportObject($o);
echo 'empty op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$r = empty($o['foo']);
exportObject($o);
echo 'null coalescing op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$r = $o['foo'] ?? 'default';
exportObject($o);
echo 'appending op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$o[] = true;
exportObject($o);
echo 'unset op', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
unset($o['foo']);
exportObject($o);
echo 'nested read', PHP_EOL;
$o = new DimensionHandlersNoArrayAccess();
$r = $o['foo']['bar'];
exportObject($o);
// Illegal
//echo 'nested read: appending then read', PHP_EOL;
//$o = new DimensionHandlersNoArrayAccess();
//try {
// $r = $o[]['bar'];
//} catch (\Throwable $e) {
// echo $e::class, ': ', $e->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'

View File

@@ -0,0 +1,46 @@
--TEST--
Internal handlers that do not explicitly support userland ArrayAccess
--EXTENSIONS--
zend_test
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
class NoImplement extends DimensionHandlersNoArrayAccess {}
class DoImplement extends DimensionHandlersNoArrayAccess 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 "dummy";
}
public function offsetUnset($offset): void {
var_dump(__METHOD__);
var_dump($offset);
}
public function offsetExists($offset): bool {
var_dump(__METHOD__);
var_dump($offset);
return true;
}
}
$no = new NoImplement();
$do = new DoImplement();
$no['foo'];
exportObject($no);
$do['foo'];
exportObject($do);
?>
--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'

View File

@@ -0,0 +1,92 @@
--TEST--
Invalid containers with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
$containers = [
//false,
true,
4,
5.5,
STDERR,
//new stdClass(),
];
ob_start();
foreach ($containers as $container) {
$containerStr = get_zend_debug_type($container);
$EXPECTED_OUTPUT = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on $containerStr in %s on line 8
NULL
Write:
Cannot use a scalar value as an array
Read:
Warning: Trying to access array offset on $containerStr in %s on line 22
NULL
Read-Write:
Cannot use a scalar value as an array
isset():
bool(false)
empty():
bool(true)
null coalesce:
string(7) "default"
unset():
Cannot unset offset in a non-array variable
Nested read:
Warning: Trying to access array offset on $containerStr in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Cannot use a scalar value as an array
Nested Read-Write:
Cannot use a scalar value as an array
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Cannot unset offset in a non-array variable
OUTPUT;
foreach ($offsets as $dimension) {
$error = $containerStr . '[' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if ($EXPECTED_OUTPUT !== $varOutput) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_invalid_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,267 @@
--TEST--
null containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT_VALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on null in %s on line 8
NULL
Write:
Read:
int(5)
Read-Write:
isset():
bool(true)
empty():
bool(false)
null coalesce:
int(25)
unset():
Nested read:
Warning: Undefined array key %s in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Nested Read-Write:
Nested isset():
bool(true)
Nested empty():
bool(false)
Nested null coalesce:
int(30)
Nested unset():
OUTPUT;
$EXPECTED_OUTPUT_VALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_VALID_OFFSETS) . '$/s';
const EXPECTF_OUTPUT_FLOAT_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on null in %s on line 8
NULL
Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 15
Read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 22
int(5)
Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 29
isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 36
bool(true)
empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 42
bool(false)
null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 48
int(25)
unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 55
Nested read:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 62
Warning: Undefined array key 0 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 69
Nested Read-Write:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 76
Nested isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
bool(true)
Nested empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
bool(false)
Nested null coalesce:
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 95
int(30)
Nested unset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 102
OUTPUT;
$EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTF_OUTPUT_FLOAT_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on null in %s on line 8
NULL
Write:
Cannot access offset of type %s on array
Read:
Cannot access offset of type %s on array
Read-Write:
Cannot access offset of type %s on array
isset():
Cannot access offset of type %s in isset or empty
empty():
Cannot access offset of type %s in isset or empty
null coalesce:
Cannot access offset of type %s on array
unset():
Cannot unset offset of type %s on array
Nested read:
Cannot access offset of type %s on array
Nested write:
Cannot access offset of type %s on array
Nested Read-Write:
Cannot access offset of type %s on array
Nested isset():
Cannot access offset of type %s on array
Nested empty():
Cannot access offset of type %s on array
Nested null coalesce:
Cannot access offset of type %s on array
Nested unset():
Cannot access offset of type %s on array
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS = <<<OUTPUT
Read before write:
Warning: Trying to access array offset on null in %s on line 8
NULL
Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 15
Read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 22
int(5)
Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 29
isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 36
bool(true)
empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 42
bool(false)
null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 48
int(25)
unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 55
Nested read:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 62
Warning: Undefined array key 3 in %s on line 62
Warning: Trying to access array offset on null in %s on line 62
NULL
Nested write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 69
Nested Read-Write:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 76
Nested isset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 83
bool(true)
Nested empty():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 89
bool(false)
Nested null coalesce:
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 95
int(30)
Nested unset():
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
Warning: Resource ID#3 used as offset, casting to integer (3) in %s on line 102
OUTPUT;
ob_start();
foreach ($offsets as $dimension) {
$container = null;
$error = 'null[' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if (
!preg_match($EXPECTED_OUTPUT_VALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_FLOAT_OFFSETS_REGEX, $varOutput)
&& $varOutput !== EXPECTED_OUTPUT_RESOURCE_STDERR_OFFSETS
) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_null_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,72 @@
--TEST--
object containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT = <<<OUTPUT
Read before write:
Cannot use object of type stdClass as array
Write:
Cannot use object of type stdClass as array
Read:
Cannot use object of type stdClass as array
Read-Write:
Cannot use object of type stdClass as array
isset():
Cannot use object of type stdClass as array
empty():
Cannot use object of type stdClass as array
null coalesce:
Cannot use object of type stdClass as array
unset():
Cannot use object of type stdClass as array
Nested read:
Cannot use object of type stdClass as array
Nested write:
Cannot use object of type stdClass as array
Nested Read-Write:
Cannot use object of type stdClass as array
Nested isset():
Cannot use object of type stdClass as array
Nested empty():
Cannot use object of type stdClass as array
Nested null coalesce:
Cannot use object of type stdClass as array
Nested unset():
Cannot use object of type stdClass as array
OUTPUT;
ob_start();
foreach ($offsets as $dimension) {
$container = new stdClass();
$error = '(new stdClass())[' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if ($varOutput !== EXPECTED_OUTPUT) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_object_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -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--
<?php
if (getenv("SKIP_SLOW_TESTS")) die('skip slow test');
?>
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
function makeTestFile($container, $offset) {
$offset_p = zend_test_var_export($offset);
$container_p = zend_test_var_export($container);
$fileContent = file_get_contents(__DIR__ . '/test_variable_offsets.inc');
$fileContent = str_replace('$dimension', $offset_p, $fileContent);
return str_replace('//$container var declaration in const generated file', "\$container = $container_p;", $fileContent);
}
function normalize_output(string $output, string $filename): string {
$output = str_replace(
[$filename],
['%s'],
$output
);
$output = preg_replace('/\)#\d+ \(/', ')#99 (', $output);
return $output;
}
$const_dim_filename = __DIR__ . DIRECTORY_SEPARATOR . 'compare_binary_offsets_temp.php';
ob_start();
foreach ($containers as $container_orig) {
foreach ($offsets as $offset) {
$error = zend_test_var_export($container_orig) . '[' . zend_test_var_export($offset) . '] has different outputs' . "\n";
file_put_contents($const_dim_filename, makeTestFile($container_orig, $offset));
include $const_dim_filename;
$constOutput = ob_get_contents();
ob_clean();
$constOutput = normalize_output($constOutput, $const_dim_filename);
$dimension = $offset;
$container = $container_orig;
if (is_object($container_orig)) {
$container = clone $container_orig;
}
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = normalize_output($varOutput, $var_dim_filename);
if ($constOutput !== $varOutput) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_{$failuresNb}_const.txt", $constOutput);
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_{$failuresNb}_var.txt", $varOutput);
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_{$failuresNb}_test_case.txt", makeTestFile($container_orig, $offset));
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--CLEAN--
<?php
$fl = __DIR__ . DIRECTORY_SEPARATOR . 'compare_binary_offsets_temp.php';
@unlink($fl);
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,637 @@
--TEST--
String containers behaviour with offsets
--FILE--
<?php
require_once __DIR__ . DIRECTORY_SEPARATOR . 'test_offset_helpers.inc';
const EXPECTED_OUTPUT_VALID_OFFSETS = <<<OUTPUT
Read before write:
Warning: Uninitialized string offset %d in %s on line 8
string(0) ""
Write:
Read:
string(1) "5"
Read-Write:
Cannot use assign-op operators with string offsets
isset():
bool(true)
empty():
bool(false)
null coalesce:
string(1) "5"
unset():
Cannot unset string offsets
Nested read:
Warning: Uninitialized string offset %d in %s on line 62
string(0) ""
Nested write:
Cannot use string offset as an array
Nested Read-Write:
Cannot use string offset as an array
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_VALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_VALID_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_VALID_OFFSETS_OUT_OF_RANGE = <<<OUTPUT
Read before write:
Warning: Uninitialized string offset %i in %s on line 8
string(0) ""
Write:
Warning: Illegal string offset %i in %s on line 15
Read:
Warning: Uninitialized string offset %i in %s on line 22
string(0) ""
Read-Write:
Cannot use assign-op operators with string offsets
isset():
bool(false)
empty():
bool(true)
null coalesce:
string(7) "default"
unset():
Cannot unset string offsets
Nested read:
Warning: Uninitialized string offset %i in %s on line 62
Warning: Uninitialized string offset %i in %s on line 62
string(0) ""
Nested write:
Cannot use string offset as an array
Nested Read-Write:
Cannot use string offset as an array
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_VALID_OFFSETS_OUT_OF_RANGE_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_VALID_OFFSETS_OUT_OF_RANGE) . '$/s';
const EXPECTED_OUTPUT_STRING_CAST_OFFSETS = <<<OUTPUT
Read before write:
Warning: String offset cast occurred in %s on line 8
Warning: Uninitialized string offset %d in %s on line 8
string(0) ""
Write:
Warning: String offset cast occurred in %s on line 15
Read:
Warning: String offset cast occurred in %s on line 22
string(1) "5"
Read-Write:
Warning: String offset cast occurred in %s on line 29
Cannot use assign-op operators with string offsets
isset():
bool(true)
empty():
bool(false)
null coalesce:
string(1) "5"
unset():
Cannot unset string offsets
Nested read:
Warning: String offset cast occurred in %s on line 62
Warning: String offset cast occurred in %s on line 62
Warning: Uninitialized string offset %d in %s on line 62
string(0) ""
Nested write:
Warning: String offset cast occurred in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: String offset cast occurred in %s on line 76
Cannot use string offset as an array
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Warning: String offset cast occurred in %s on line 102
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_STRING_CAST_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_STRING_CAST_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_STRING_CAST_OFFSETS_TO_0 = <<<OUTPUT
Read before write:
Warning: String offset cast occurred in %s on line 8
Warning: Uninitialized string offset 0 in %s on line 8
string(0) ""
Write:
Warning: String offset cast occurred in %s on line 15
Read:
Warning: String offset cast occurred in %s on line 22
string(1) "5"
Read-Write:
Warning: String offset cast occurred in %s on line 29
Cannot use assign-op operators with string offsets
isset():
bool(true)
empty():
bool(false)
null coalesce:
string(1) "5"
unset():
Cannot unset string offsets
Nested read:
Warning: String offset cast occurred in %s on line 62
Warning: String offset cast occurred in %s on line 62
string(1) "5"
Nested write:
Warning: String offset cast occurred in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: String offset cast occurred in %s on line 76
Cannot use string offset as an array
Nested isset():
bool(true)
Nested empty():
bool(false)
Nested null coalesce:
string(1) "5"
Nested unset():
Warning: String offset cast occurred in %s on line 102
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_STRING_CAST_OFFSETS_TO_0_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_STRING_CAST_OFFSETS_TO_0) . '$/s';
const EXPECTED_OUTPUT_STRING_CAST_OFFSETS_OUT_OF_RANGE = <<<OUTPUT
Read before write:
Warning: String offset cast occurred in %s on line 8
Warning: Uninitialized string offset %i in %s on line 8
string(0) ""
Write:
Warning: String offset cast occurred in %s on line 15
Warning: Illegal string offset %i in %s on line 15
Read:
Warning: String offset cast occurred in %s on line 22
Warning: Uninitialized string offset %i in %s on line 22
string(0) ""
Read-Write:
Warning: String offset cast occurred in %s on line 29
Cannot use assign-op operators with string offsets
isset():
bool(false)
empty():
bool(true)
null coalesce:
string(7) "default"
unset():
Cannot unset string offsets
Nested read:
Warning: String offset cast occurred in %s on line 62
Warning: Uninitialized string offset %i in %s on line 62
Warning: String offset cast occurred in %s on line 62
Warning: Uninitialized string offset %i in %s on line 62
string(0) ""
Nested write:
Warning: String offset cast occurred in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: String offset cast occurred in %s on line 76
Cannot use string offset as an array
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Warning: String offset cast occurred in %s on line 102
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_STRING_CAST_OFFSETS_OUT_OF_RANGE_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_STRING_CAST_OFFSETS_OUT_OF_RANGE) . '$/s';
const EXPECTF_OUTPUT_FLOAT_OFFSETS_OUT_OF_RANGE = <<<OUTPUT
Read before write:
Warning: String offset cast occurred in %s on line 8
Warning: Uninitialized string offset %i in %s on line 8
string(0) ""
Write:
Warning: String offset cast occurred in %s on line 15
Warning: Illegal string offset %i in %s on line 15
Read:
Warning: String offset cast occurred in %s on line 22
Warning: Uninitialized string offset %i in %s on line 22
string(0) ""
Read-Write:
Warning: String offset cast occurred in %s on line 29
Cannot use assign-op operators with string offsets
isset():
Deprecated: Implicit conversion from float %f to int loses precision in %s on line 36
bool(false)
empty():
Deprecated: Implicit conversion from float %f to int loses precision in %s on line 42
bool(true)
null coalesce:
string(7) "default"
unset():
Cannot unset string offsets
Nested read:
Warning: String offset cast occurred in %s on line 62
Warning: Uninitialized string offset %i in %s on line 62
Warning: String offset cast occurred in %s on line 62
Warning: Uninitialized string offset %i in %s on line 62
string(0) ""
Nested write:
Warning: String offset cast occurred in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: String offset cast occurred in %s on line 76
Cannot use string offset as an array
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Warning: String offset cast occurred in %s on line 102
Cannot use string offset as an array
OUTPUT;
$EXPECTF_OUTPUT_FLOAT_OFFSETS_OUT_OF_RANGE_REGEX = '/^' . expectf_to_regex(EXPECTF_OUTPUT_FLOAT_OFFSETS_OUT_OF_RANGE) . '$/s';
const EXPECTED_OUTPUT_FLOAT_INF_NAN_OFFSETS = <<<OUTPUT
Read before write:
Warning: String offset cast occurred in %s on line 8
Warning: Uninitialized string offset %d in %s on line 8
string(0) ""
Write:
Warning: String offset cast occurred in %s on line 15
Read:
Warning: String offset cast occurred in %s on line 22
string(1) "5"
Read-Write:
Warning: String offset cast occurred in %s on line 29
Cannot use assign-op operators with string offsets
isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 36
bool(true)
empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 42
bool(false)
null coalesce:
string(1) "5"
unset():
Cannot unset string offsets
Nested read:
Warning: String offset cast occurred in %s on line 62
Warning: String offset cast occurred in %s on line 62
string(1) "5"
Nested write:
Warning: String offset cast occurred in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: String offset cast occurred in %s on line 76
Cannot use string offset as an array
Nested isset():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 83
bool(true)
Nested empty():
Deprecated: Implicit conversion from float %F to int loses precision in %s on line 89
bool(false)
Nested null coalesce:
string(1) "5"
Nested unset():
Warning: String offset cast occurred in %s on line 102
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_FLOAT_INF_NAN_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_FLOAT_INF_NAN_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS = <<<OUTPUT
Read before write:
Cannot access offset of type %s on string
Write:
Cannot access offset of type %s on string
Read:
Cannot access offset of type %s on string
Read-Write:
Cannot access offset of type %s on string
isset():
bool(false)
empty():
bool(true)
null coalesce:
Cannot access offset of type %s on string
unset():
Cannot unset string offsets
Nested read:
Cannot access offset of type %s on string
Nested write:
Cannot access offset of type %s on string
Nested Read-Write:
Cannot access offset of type %s on string
Nested isset():
Cannot access offset of type %s on string
Nested empty():
Cannot access offset of type %s on string
Nested null coalesce:
Cannot access offset of type %s on string
Nested unset():
Cannot unset string offsets
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS_AS_STRINGS = <<<OUTPUT
Read before write:
Cannot access offset of type string on string
Write:
Cannot access offset of type string on string
Read:
Cannot access offset of type string on string
Read-Write:
Cannot access offset of type string on string
isset():
bool(false)
empty():
bool(true)
null coalesce:
string(7) "default"
unset():
Cannot unset string offsets
Nested read:
Cannot access offset of type string on string
Nested write:
Cannot access offset of type string on string
Nested Read-Write:
Cannot access offset of type string on string
Nested isset():
bool(false)
Nested empty():
bool(true)
Nested null coalesce:
string(7) "default"
Nested unset():
Cannot unset string offsets
OUTPUT;
const EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS = <<<OUTPUT
Read before write:
Warning: Illegal string offset %s in %s on line 8
Warning: Uninitialized string offset %d in %s on line 8
string(0) ""
Write:
Warning: Illegal string offset %s in %s on line 15
Read:
Warning: Illegal string offset %s in %s on line 22
string(1) "5"
Read-Write:
Warning: Illegal string offset %s in %s on line 29
Cannot use assign-op operators with string offsets
isset():
bool(false)
empty():
bool(true)
null coalesce:
Warning: Illegal string offset %s in %s on line 48
string(1) "5"
unset():
Cannot unset string offsets
Nested read:
Warning: Illegal string offset %s in %s on line 62
Warning: Illegal string offset %s in %s on line 62
Warning: Uninitialized string offset %d in %s on line 62
string(0) ""
Nested write:
Warning: Illegal string offset %s in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: Illegal string offset %s in %s on line 76
Cannot use string offset as an array
Nested isset():
Warning: Illegal string offset %s in %s on line 83
bool(false)
Nested empty():
Warning: Illegal string offset %s in %s on line 89
bool(true)
Nested null coalesce:
Warning: Illegal string offset %s in %s on line 95
Warning: Illegal string offset %s in %s on line 95
string(7) "default"
Nested unset():
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS) . '$/s';
const EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS_TO_0 = <<<OUTPUT
Read before write:
Warning: Illegal string offset %s in %s on line 8
Warning: Uninitialized string offset 0 in %s on line 8
string(0) ""
Write:
Warning: Illegal string offset %s in %s on line 15
Read:
Warning: Illegal string offset %s in %s on line 22
string(1) "5"
Read-Write:
Warning: Illegal string offset %s in %s on line 29
Cannot use assign-op operators with string offsets
isset():
bool(false)
empty():
bool(true)
null coalesce:
Warning: Illegal string offset %s in %s on line 48
string(1) "5"
unset():
Cannot unset string offsets
Nested read:
Warning: Illegal string offset %s in %s on line 62
Warning: Illegal string offset %s in %s on line 62
string(1) "5"
Nested write:
Warning: Illegal string offset %s in %s on line 69
Cannot use string offset as an array
Nested Read-Write:
Warning: Illegal string offset %s in %s on line 76
Cannot use string offset as an array
Nested isset():
Warning: Illegal string offset %s in %s on line 83
bool(false)
Nested empty():
Warning: Illegal string offset %s in %s on line 89
bool(true)
Nested null coalesce:
Warning: Illegal string offset %s in %s on line 95
Warning: Illegal string offset %s in %s on line 95
string(1) "5"
Nested unset():
Cannot use string offset as an array
OUTPUT;
$EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS_TO_0_REGEX = '/^' . expectf_to_regex(EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS_TO_0) . '$/s';
ob_start();
foreach ($offsets as $dimension) {
$container = '';
$error = '""[' . zend_test_var_export($dimension) . '] has different outputs' . "\n";
include $var_dim_filename;
$varOutput = ob_get_contents();
ob_clean();
$varOutput = str_replace(
[$var_dim_filename],
['%s'],
$varOutput
);
if (
!preg_match($EXPECTED_OUTPUT_VALID_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_VALID_OFFSETS_OUT_OF_RANGE_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_STRING_CAST_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_STRING_CAST_OFFSETS_TO_0_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_STRING_CAST_OFFSETS_OUT_OF_RANGE_REGEX, $varOutput)
&& !preg_match($EXPECTF_OUTPUT_FLOAT_OFFSETS_OUT_OF_RANGE_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_FLOAT_INF_NAN_OFFSETS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_REGEX, $varOutput)
&& $varOutput !== EXPECTED_OUTPUT_INVALID_OFFSETS_AS_STRINGS
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS_REGEX, $varOutput)
&& !preg_match($EXPECTED_OUTPUT_INVALID_OFFSETS_AS_LEADING_NUMERIC_STRINGS_TO_0_REGEX, $varOutput)
) {
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . "debug_string_container_{$failuresNb}.txt", $varOutput);
++$failuresNb;
$failures[] = $error;
}
++$testCasesTotal;
}
ob_end_clean();
echo "Executed tests\n";
if ($failures !== []) {
echo "Failures:\n" . implode($failures);
}
?>
--EXPECT--
Executed tests

View File

@@ -0,0 +1,204 @@
<?php
function get_zend_debug_type($v) {
if ($v === true) {
return 'true';
}
if ($v === false) {
return 'false';
}
if (is_resource($v)) {
return 'resource';
}
return get_debug_type($v);
}
function zend_test_var_export($value) {
if ($value === PHP_INT_MIN) {
return "PHP_INT_MIN";
}
if ($value === PHP_INT_MAX) {
return "PHP_INT_MAX";
}
if (is_array($value)) {
return "[]";
}
if (is_resource($value)) {
return "STDERR";
}
if ($value instanceof stdClass) {
return "new stdClass()";
}
if ($value instanceof B) {
return "new B()";
}
if ($value instanceof A) {
return "new A()";
}
if ($value instanceof ArrayObject) {
return "new ArrayObject()";
}
return var_export($value, true);
}
function exportProp(object $o, string $p) {
$rp = new ReflectionProperty($o, $p);
echo ', ', $p, ': ';
if ($rp->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';

View File

@@ -0,0 +1,105 @@
<?php
//$container var declaration in const generated file
// Read before write
try {
echo "Read before write:\n";
var_dump($container[$dimension]);
} catch (\Throwable $e) {
echo $e->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";
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}