parent
aab412084f
commit
b6d0488ea6
14 changed files with 373 additions and 29 deletions
@ -0,0 +1,33 @@ |
|||||||
|
--TEST-- |
||||||
|
foreach list destructuring can write into object properties and array dimensions |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class ForeachListWriteBox |
||||||
|
{ |
||||||
|
public $name = ''; |
||||||
|
public $value = 0; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$rows = [ |
||||||
|
['first', 10], |
||||||
|
['second', 20], |
||||||
|
]; |
||||||
|
$box = new ForeachListWriteBox(); |
||||||
|
$out = []; |
||||||
|
|
||||||
|
foreach ($rows as [$box->name, $out['value']]) { |
||||||
|
$box->value += $out['value']; |
||||||
|
var_dump($box->name, $out['value'], $box->value); |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(5) "first" |
||||||
|
int(10) |
||||||
|
int(10) |
||||||
|
string(6) "second" |
||||||
|
int(20) |
||||||
|
int(30) |
||||||
@ -0,0 +1,16 @@ |
|||||||
|
--TEST-- |
||||||
|
static arrow function captures local variables by value |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$factor = 3; |
||||||
|
$map = static fn (int $value): int => $value * $factor; |
||||||
|
$factor = 10; |
||||||
|
|
||||||
|
var_dump($map(4)); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(12) |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
--TEST-- |
||||||
|
clone operand expression is evaluated once and __clone runs |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class CloneSideEffect |
||||||
|
{ |
||||||
|
public int $value = 1; |
||||||
|
|
||||||
|
public function __clone() |
||||||
|
{ |
||||||
|
echo "__clone\n"; |
||||||
|
$this->value++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function make_clone_source(): CloneSideEffect |
||||||
|
{ |
||||||
|
echo "make\n"; |
||||||
|
return new CloneSideEffect(); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$copy = clone make_clone_source(); |
||||||
|
var_dump($copy->value); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
make |
||||||
|
__clone |
||||||
|
int(2) |
||||||
@ -0,0 +1,23 @@ |
|||||||
|
--TEST-- |
||||||
|
Error suppression should restore error_reporting after expression evaluation |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function suppressed_reporting_level(): int |
||||||
|
{ |
||||||
|
return error_reporting(); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$before = error_reporting(); |
||||||
|
$inside = @suppressed_reporting_level(); |
||||||
|
$after = error_reporting(); |
||||||
|
|
||||||
|
var_dump($inside === $before); |
||||||
|
var_dump($after === $before); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(false) |
||||||
|
bool(true) |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
--TEST-- |
||||||
|
$GLOBALS array assignment and reference parameter update global slot |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function globals_assign_ref_inc(&$value): void |
||||||
|
{ |
||||||
|
$value++; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$GLOBALS['aot_globals_assign_ref_value'] = 40; |
||||||
|
$GLOBALS['aot_globals_assign_ref_value'] += 1; |
||||||
|
globals_assign_ref_inc($GLOBALS['aot_globals_assign_ref_value']); |
||||||
|
|
||||||
|
global $aot_globals_assign_ref_value; |
||||||
|
var_dump($aot_globals_assign_ref_value); |
||||||
|
var_dump($GLOBALS['aot_globals_assign_ref_value']); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(42) |
||||||
|
int(42) |
||||||
@ -0,0 +1,21 @@ |
|||||||
|
--TEST-- |
||||||
|
$GLOBALS array element can be passed to reference parameter |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function globals_ref_inc(&$value): void |
||||||
|
{ |
||||||
|
$value++; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
global $aot_globals_ref_value; |
||||||
|
$aot_globals_ref_value = 41; |
||||||
|
|
||||||
|
globals_ref_inc($GLOBALS['aot_globals_ref_value']); |
||||||
|
var_dump($aot_globals_ref_value); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(42) |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
--TEST-- |
||||||
|
include path expression side effects and return value |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function include_target(string $name): string |
||||||
|
{ |
||||||
|
echo "target:$name\n"; |
||||||
|
return __DIR__ . '/' . $name; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$ret = include include_target('test_include_return.inc'); |
||||||
|
var_dump($ret); |
||||||
|
|
||||||
|
$ret = include_once include_target('test_include_return.inc'); |
||||||
|
var_dump($ret); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
target:test_include_return.inc |
||||||
|
included:test_include_return.inc |
||||||
|
int(123) |
||||||
|
target:test_include_return.inc |
||||||
|
bool(true) |
||||||
@ -0,0 +1,3 @@ |
|||||||
|
<?php |
||||||
|
echo "included:" . basename(__FILE__) . "\n"; |
||||||
|
return 123; |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
--TEST-- |
||||||
|
instanceof dynamic class expression side effects |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
interface InstanceofMarker {} |
||||||
|
class InstanceofSubject implements InstanceofMarker {} |
||||||
|
|
||||||
|
function make_instanceof_subject(): object |
||||||
|
{ |
||||||
|
echo "object\n"; |
||||||
|
return new InstanceofSubject(); |
||||||
|
} |
||||||
|
|
||||||
|
function make_instanceof_class(string $class): string |
||||||
|
{ |
||||||
|
echo "class:$class\n"; |
||||||
|
return $class; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
var_dump(make_instanceof_subject() instanceof (make_instanceof_class(InstanceofMarker::class))); |
||||||
|
var_dump(make_instanceof_subject() instanceof (make_instanceof_class(stdClass::class))); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
object |
||||||
|
class:InstanceofMarker |
||||||
|
bool(true) |
||||||
|
object |
||||||
|
class:stdClass |
||||||
|
bool(false) |
||||||
@ -0,0 +1,37 @@ |
|||||||
|
--TEST-- |
||||||
|
null coalescing assignment with object property and array dim targets |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class CoalesceBox |
||||||
|
{ |
||||||
|
public $value = null; |
||||||
|
public array $items = []; |
||||||
|
} |
||||||
|
|
||||||
|
function make_default(string $label): string |
||||||
|
{ |
||||||
|
echo "default:$label\n"; |
||||||
|
return $label; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$box = new CoalesceBox(); |
||||||
|
$box->value ??= make_default('prop'); |
||||||
|
$box->value ??= make_default('prop-skip'); |
||||||
|
var_dump($box->value); |
||||||
|
|
||||||
|
$box->items['name'] ??= make_default('array'); |
||||||
|
$box->items['name'] ??= make_default('array-skip'); |
||||||
|
var_dump($box->items); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
default:prop |
||||||
|
string(4) "prop" |
||||||
|
default:array |
||||||
|
array(1) { |
||||||
|
["name"]=> |
||||||
|
string(5) "array" |
||||||
|
} |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
--TEST-- |
||||||
|
Nullsafe chain should stop at middle null and skip later arguments |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class NullsafeChainLeaf |
||||||
|
{ |
||||||
|
public function value(string $arg): string |
||||||
|
{ |
||||||
|
echo "leaf:$arg\n"; |
||||||
|
return $arg; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class NullsafeChainMiddle |
||||||
|
{ |
||||||
|
public function __construct(private ?NullsafeChainLeaf $leaf) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function leaf(string $arg): ?NullsafeChainLeaf |
||||||
|
{ |
||||||
|
echo "middle:$arg\n"; |
||||||
|
return $this->leaf; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class NullsafeChainRoot |
||||||
|
{ |
||||||
|
public function __construct(private ?NullsafeChainMiddle $middle) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public function middle(string $arg): ?NullsafeChainMiddle |
||||||
|
{ |
||||||
|
echo "root:$arg\n"; |
||||||
|
return $this->middle; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function make_arg(string $name): string |
||||||
|
{ |
||||||
|
echo "arg:$name\n"; |
||||||
|
return $name; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$root = new NullsafeChainRoot(new NullsafeChainMiddle(null)); |
||||||
|
var_dump($root?->middle(make_arg('root'))?->leaf(make_arg('middle'))?->value(make_arg('leaf'))); |
||||||
|
|
||||||
|
$root = new NullsafeChainRoot(new NullsafeChainMiddle(new NullsafeChainLeaf())); |
||||||
|
var_dump($root?->middle(make_arg('root2'))?->leaf(make_arg('middle2'))?->value(make_arg('leaf2'))); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
arg:root |
||||||
|
root:root |
||||||
|
arg:middle |
||||||
|
middle:middle |
||||||
|
NULL |
||||||
|
arg:root2 |
||||||
|
root:root2 |
||||||
|
arg:middle2 |
||||||
|
middle:middle2 |
||||||
|
arg:leaf2 |
||||||
|
leaf:leaf2 |
||||||
|
string(5) "leaf2" |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
--TEST-- |
||||||
|
static variable dynamic initializer runs only once |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
function make_static_items(int $seed): array |
||||||
|
{ |
||||||
|
echo "init:$seed\n"; |
||||||
|
return [$seed]; |
||||||
|
} |
||||||
|
|
||||||
|
function use_static_items(int $seed): void |
||||||
|
{ |
||||||
|
static $items = make_static_items($seed); |
||||||
|
var_dump($items); |
||||||
|
$items[] = $seed; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
use_static_items(10); |
||||||
|
use_static_items(20); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
init:10 |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
int(10) |
||||||
|
} |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
int(10) |
||||||
|
[1]=> |
||||||
|
int(10) |
||||||
|
} |
||||||
Loading…
Reference in new issue