Skip to content

Commit

Permalink
Add support for PHP 8.4 property hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed May 18, 2024
1 parent 95e0618 commit 7fac7ed
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
12 changes: 11 additions & 1 deletion src/main/php/lang/reflection/Property.class.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace lang\reflection;

use ReflectionException, ReflectionUnionType, Throwable;
use ReflectionException, ReflectionProperty, ReflectionUnionType, Throwable;
use lang\{Reflection, XPClass, Type, VirtualProperty, TypeUnion};

/**
Expand All @@ -9,6 +9,11 @@
* @test lang.reflection.unittest.PropertiesTest
*/
class Property extends Member {
private static $HOOKS;

static function __static() {
self::$HOOKS= method_exists(ReflectionProperty::class, 'getHooks');
}

protected function meta() { return Reflection::meta()->propertyAnnotations($this->reflect); }

Expand All @@ -19,6 +24,11 @@ protected function meta() { return Reflection::meta()->propertyAnnotations($this
*/
public function comment() { return Reflection::meta()->propertyComment($this->reflect); }

/** Returns whether this property is virtual */
public function virtual() {
return $this->reflect instanceof VirtualProperty || self::$HOOKS && $this->reflect->isVirtual();
}

/** Returns a compound name consisting of `[CLASS]::$[NAME]` */
public function compoundName(): string {
return strtr($this->reflect->getDeclaringClass()->name , '\\', '.').'::$'.$this->reflect->getName();
Expand Down
59 changes: 59 additions & 0 deletions src/test/php/lang/reflection/unittest/PropertyHooksTest.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php namespace lang\reflection\unittest;

use ReflectionProperty;
use lang\reflection\AccessingFailed;
use test\verify\Condition;
use test\{Assert, Expect, Test};

#[Condition(assert: 'method_exists(ReflectionProperty::class, "getHooks")')]
class PropertyHooksTest {
use TypeDefinition;

#[Test]
public function is_virtual() {
$type= $this->declare('{ public string $fixture { get => "test"; } }');
Assert::true($type->properties()->named('fixture')->virtual());
}

#[Test]
public function get() {
$type= $this->declare('{ public string $fixture { get => "test"; } }');
$instance= $type->newInstance();

Assert::equals('test', $type->properties()->named('fixture')->get($instance));
}

#[Test]
public function set() {
$type= $this->declare('{ public string $fixture { set => ucfirst($value); } }');
$instance= $type->newInstance();
$type->properties()->named('fixture')->set($instance, 'test');

Assert::equals('Test', $instance->fixture);
}

#[Test]
public function set_with_parameter() {
$type= $this->declare('{ public string $fixture { set(string $arg) => ucfirst($arg); } }');
$instance= $type->newInstance();
$type->properties()->named('fixture')->set($instance, 'test');

Assert::equals('Test', $instance->fixture);
}

#[Test, Expect(AccessingFailed::class)]
public function get_set_only_raises() {
$type= $this->declare('{ public string $fixture { set => ucfirst($value); } }');
$instance= $type->newInstance();

$type->properties()->named('fixture')->get($instance);
}

#[Test, Expect(AccessingFailed::class)]
public function set_get_only_raises() {
$type= $this->declare('{ public string $fixture { get => "test"; } }');
$instance= $type->newInstance();

$type->properties()->named('fixture')->set($instance, 'test');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@ private function fixtures() {
DETAIL_RETURNS => 'string',
DETAIL_ARGUMENTS => [Modifiers::IS_PUBLIC | Modifiers::IS_READONLY]
];

yield $t;
if (PHP_VERSION_ID >= 80100) {
yield $this->declare('{ public readonly string $fixture; }');
}
}

#[Test, Values(from: 'fixtures')]
Expand All @@ -36,6 +32,11 @@ public function virtual_property_included_in_list($type) {
);
}

#[Test, Values(from: 'fixtures')]
public function is_virtual($type) {
Assert::true($type->properties()->named('fixture')->virtual());
}

#[Test, Values(from: 'fixtures')]
public function named_virtual($type) {
Assert::equals($type->property('fixture'), $type->properties()->named('fixture'));
Expand Down

0 comments on commit 7fac7ed

Please sign in to comment.