parent
e44007846b
commit
7fa95d449f
2 changed files with 194 additions and 3 deletions
@ -0,0 +1,170 @@ |
|||||||
|
--TEST-- |
||||||
|
Object property reference assignment preserves aliases and typed-property sources |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
final class PropertyReferenceHolder |
||||||
|
{ |
||||||
|
public ?array $value = null; |
||||||
|
} |
||||||
|
|
||||||
|
final class PrivatePropertyReferenceHolder |
||||||
|
{ |
||||||
|
private ?array $value = null; |
||||||
|
|
||||||
|
public function bind(array &$source): void |
||||||
|
{ |
||||||
|
$this->value = &$source; |
||||||
|
} |
||||||
|
|
||||||
|
public function value(): ?array |
||||||
|
{ |
||||||
|
return $this->value; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final class PropertyReferenceDep |
||||||
|
{ |
||||||
|
public ?array $map = null; |
||||||
|
} |
||||||
|
|
||||||
|
function bindObjectProperty(object $holder, mixed &$source): void |
||||||
|
{ |
||||||
|
$holder->value = &$source; |
||||||
|
} |
||||||
|
|
||||||
|
function replaceReference(mixed &$target, mixed $value): void |
||||||
|
{ |
||||||
|
$target = $value; |
||||||
|
} |
||||||
|
|
||||||
|
function propertyReferenceTarget(array &$events, object $holder): object |
||||||
|
{ |
||||||
|
$events[] = 'object'; |
||||||
|
return $holder; |
||||||
|
} |
||||||
|
|
||||||
|
function propertyReferenceName(array &$events): string |
||||||
|
{ |
||||||
|
$events[] = 'property'; |
||||||
|
return 'value'; |
||||||
|
} |
||||||
|
|
||||||
|
function &propertyReferenceSource(array &$events, mixed &$source): mixed |
||||||
|
{ |
||||||
|
$events[] = 'source'; |
||||||
|
return $source; |
||||||
|
} |
||||||
|
|
||||||
|
function trackThroughSplObjectStorage(object $target): PropertyReferenceDep |
||||||
|
{ |
||||||
|
$storage = new SplObjectStorage(); |
||||||
|
$storage->offsetSet($target, []); |
||||||
|
$map = $storage[$target]; |
||||||
|
|
||||||
|
$dep = new PropertyReferenceDep(); |
||||||
|
$dep->map = &$map; |
||||||
|
$map['dep'] = $dep; |
||||||
|
$storage->offsetSet($target, $map); |
||||||
|
return $dep; |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$holder = new PropertyReferenceHolder(); |
||||||
|
$source = ['initial' => 1]; |
||||||
|
$holder->value = &$source; |
||||||
|
$source['source'] = 2; |
||||||
|
$holder->value['property'] = 3; |
||||||
|
var_dump($source); |
||||||
|
|
||||||
|
$dynamicHolder = new PropertyReferenceHolder(); |
||||||
|
$dynamicSource = []; |
||||||
|
bindObjectProperty($dynamicHolder, $dynamicSource); |
||||||
|
$dynamicSource['dynamic'] = true; |
||||||
|
var_dump($dynamicHolder->value); |
||||||
|
|
||||||
|
$wrong = 'invalid'; |
||||||
|
try { |
||||||
|
bindObjectProperty($dynamicHolder, $wrong); |
||||||
|
echo "missing initial TypeError\n"; |
||||||
|
} catch (TypeError $error) { |
||||||
|
echo "initial TypeError\n"; |
||||||
|
} |
||||||
|
$dynamicSource['preserved'] = true; |
||||||
|
var_dump($dynamicHolder->value); |
||||||
|
|
||||||
|
$replacement = ['replacement' => true]; |
||||||
|
$holder->value = &$replacement; |
||||||
|
replaceReference($source, 'detached'); |
||||||
|
var_dump($source); |
||||||
|
try { |
||||||
|
replaceReference($replacement, 'invalid'); |
||||||
|
echo "missing write TypeError\n"; |
||||||
|
} catch (TypeError $error) { |
||||||
|
echo "write TypeError\n"; |
||||||
|
} |
||||||
|
var_dump($holder->value); |
||||||
|
|
||||||
|
$privateSource = []; |
||||||
|
$privateHolder = new PrivatePropertyReferenceHolder(); |
||||||
|
$privateHolder->bind($privateSource); |
||||||
|
$privateSource['private'] = true; |
||||||
|
var_dump($privateHolder->value()); |
||||||
|
|
||||||
|
$events = []; |
||||||
|
$orderedSource = []; |
||||||
|
$orderedHolder = new PropertyReferenceHolder(); |
||||||
|
propertyReferenceTarget($events, $orderedHolder)->{propertyReferenceName($events)} |
||||||
|
= &propertyReferenceSource($events, $orderedSource); |
||||||
|
$orderedSource['ordered'] = true; |
||||||
|
var_dump($events, $orderedHolder->value); |
||||||
|
|
||||||
|
$dep = trackThroughSplObjectStorage(new stdClass()); |
||||||
|
var_dump(isset($dep->map['dep'])); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
array(3) { |
||||||
|
["initial"]=> |
||||||
|
int(1) |
||||||
|
["source"]=> |
||||||
|
int(2) |
||||||
|
["property"]=> |
||||||
|
int(3) |
||||||
|
} |
||||||
|
array(1) { |
||||||
|
["dynamic"]=> |
||||||
|
bool(true) |
||||||
|
} |
||||||
|
initial TypeError |
||||||
|
array(2) { |
||||||
|
["dynamic"]=> |
||||||
|
bool(true) |
||||||
|
["preserved"]=> |
||||||
|
bool(true) |
||||||
|
} |
||||||
|
string(8) "detached" |
||||||
|
write TypeError |
||||||
|
array(1) { |
||||||
|
["replacement"]=> |
||||||
|
bool(true) |
||||||
|
} |
||||||
|
array(1) { |
||||||
|
["private"]=> |
||||||
|
bool(true) |
||||||
|
} |
||||||
|
array(3) { |
||||||
|
[0]=> |
||||||
|
string(6) "object" |
||||||
|
[1]=> |
||||||
|
string(8) "property" |
||||||
|
[2]=> |
||||||
|
string(6) "source" |
||||||
|
} |
||||||
|
array(1) { |
||||||
|
["ordered"]=> |
||||||
|
bool(true) |
||||||
|
} |
||||||
|
bool(true) |
||||||
Loading…
Reference in new issue