- Add hooks property to PropertyInfo class with get/set method mappings - Register property hooks as Zend metadata during AOT compilation - Implement proper virtual property handling with ZEND_VIRTUAL_PROPERTY_OFFSET - Support object introspection via Reflection and iteration with hook invocation - Add reflection metadata exposure through ReflectionProperty::hasHooks() and getHooks() - Implement backing storage detection to distinguish virtual vs backed properties - Add comprehensive test coverage for property hooks introspection and reflection - Document lazy object limitation with persistent AOT class entries in PHP 8.4 - Update documentation to reflect property hooks compilation strategy changespull/48/head
parent
01ea7c40be
commit
2a1ca3b1a0
6 changed files with 167 additions and 6 deletions
@ -0,0 +1,51 @@ |
||||
--TEST-- |
||||
PHP 8.4 object introspection invokes property get hooks |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class IntrospectedPropertyHooks |
||||
{ |
||||
private string $stored = 'initial'; |
||||
|
||||
public string $backed = 'raw' { |
||||
get => strtoupper($this->backed); |
||||
set (string $value) { |
||||
$this->backed = trim($value); |
||||
} |
||||
} |
||||
|
||||
public string $virtual { |
||||
get => 'virtual:' . $this->stored; |
||||
set (string $value) { |
||||
$this->stored = $value; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new IntrospectedPropertyHooks(); |
||||
$object->backed = ' value '; |
||||
$object->virtual = 'changed'; |
||||
|
||||
echo json_encode($object), "\n"; |
||||
var_dump(get_object_vars($object)); |
||||
foreach ($object as $name => $value) { |
||||
echo $name, '=', $value, "\n"; |
||||
} |
||||
|
||||
// PHP serialization exposes backing storage, not computed virtual values. |
||||
echo serialize($object), "\n"; |
||||
} |
||||
?> |
||||
--EXPECTF-- |
||||
{"backed":"VALUE","virtual":"virtual:changed"} |
||||
array(2) { |
||||
["backed"]=> |
||||
string(5) "VALUE" |
||||
["virtual"]=> |
||||
string(15) "virtual:changed" |
||||
} |
||||
backed=VALUE |
||||
virtual=virtual:changed |
||||
O:25:"IntrospectedPropertyHooks":2:{s:33:"%0IntrospectedPropertyHooks%0stored";s:7:"changed";s:6:"backed";s:5:"value";} |
||||
@ -0,0 +1,29 @@ |
||||
--TEST-- |
||||
PHP 8.4 property hooks expose Zend reflection metadata |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class ReflectedPropertyHooks |
||||
{ |
||||
public string $virtual { |
||||
get => 'value'; |
||||
set { |
||||
} |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$property = new ReflectionProperty(ReflectedPropertyHooks::class, 'virtual'); |
||||
var_dump($property->hasHooks()); |
||||
var_dump($property->isVirtual()); |
||||
foreach ($property->getHooks() as $kind => $hook) { |
||||
echo $kind, ':', $hook->getName(), ':', $hook->isFinal() ? 'final' : 'not-final', "\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
bool(true) |
||||
bool(true) |
||||
get:$virtual::get:not-final |
||||
set:$virtual::set:not-final |
||||
@ -0,0 +1,30 @@ |
||||
--TEST-- |
||||
PHP 8.4 lazy objects reject persistent AOT class entries safely |
||||
--SKIPIF-- |
||||
<?php |
||||
if (PHP_VERSION_ID < 80400) { |
||||
die('skip requires PHP 8.4'); |
||||
} |
||||
?> |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class AotLazyObjectTarget |
||||
{ |
||||
public string $name; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$reflection = new ReflectionClass(AotLazyObjectTarget::class); |
||||
try { |
||||
$reflection->newLazyGhost(function (AotLazyObjectTarget $target): void { |
||||
$target->name = 'loaded'; |
||||
}); |
||||
} catch (Error $error) { |
||||
echo $error->getMessage(), "\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
Cannot make instance of internal class lazy: AotLazyObjectTarget is internal |
||||
Loading…
Reference in new issue