Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a396856

Browse filesBrowse files
committed
[PropertyInfo][PropertyAccess] Feature: customize behavior for property hooks on read and write
1 parent dd061aa commit a396856
Copy full SHA for a396856

File tree

11 files changed

+194
-18
lines changed
Filter options

11 files changed

+194
-18
lines changed

‎src/Symfony/Component/PropertyAccess/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.3
5+
---
6+
7+
* Allow to customize behavior for property hooks on read and write
8+
49
7.0
510
---
611

‎src/Symfony/Component/PropertyAccess/PropertyAccessor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/PropertyAccessor.php
+37-11Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ class PropertyAccessor implements PropertyAccessorInterface
4747
/** @var int Allow magic __call methods */
4848
public const MAGIC_CALL = ReflectionExtractor::ALLOW_MAGIC_CALL;
4949

50+
public const DO_NOT_BYPASS_HOOKS_ON_PROPERTY = 0;
51+
public const BYPASS_HOOKS_ON_PROPERTY_READ = 1 << 1;
52+
public const BYPASS_HOOKS_ON_PROPERTY_WRITE = 1 << 0;
53+
5054
public const DO_NOT_THROW = 0;
5155
public const THROW_ON_INVALID_INDEX = 1;
5256
public const THROW_ON_INVALID_PROPERTY_PATH = 2;
@@ -67,29 +71,35 @@ class PropertyAccessor implements PropertyAccessorInterface
6771
private PropertyWriteInfoExtractorInterface $writeInfoExtractor;
6872
private array $readPropertyCache = [];
6973
private array $writePropertyCache = [];
74+
private int $byPassPropertyHooks;
7075

7176
/**
7277
* Should not be used by application code. Use
7378
* {@link PropertyAccess::createPropertyAccessor()} instead.
7479
*
75-
* @param int $magicMethods A bitwise combination of the MAGIC_* constants
76-
* to specify the allowed magic methods (__get, __set, __call)
77-
* or self::DISALLOW_MAGIC_METHODS for none
78-
* @param int $throw A bitwise combination of the THROW_* constants
79-
* to specify when exceptions should be thrown
80+
* @param int $magicMethodsFlags A bitwise combination of the MAGIC_* constants
81+
* to specify the allowed magic methods (__get, __set, __call)
82+
* or self::DISALLOW_MAGIC_METHODS for none
83+
* @param int $throw A bitwise combination of the THROW_* constants
84+
* to specify when exceptions should be thrown
85+
* @param int $byPassPropertyHooks A bitwise combination of the BYPASS_HOOKS_* constants
86+
* to specify the hooks you want to bypass,
87+
* or self::DO_NOT_BYPASS_HOOKS_ON_PROPERTY for none
8088
*/
8189
public function __construct(
8290
private int $magicMethodsFlags = self::MAGIC_GET | self::MAGIC_SET,
8391
int $throw = self::THROW_ON_INVALID_PROPERTY_PATH,
8492
?CacheItemPoolInterface $cacheItemPool = null,
8593
?PropertyReadInfoExtractorInterface $readInfoExtractor = null,
8694
?PropertyWriteInfoExtractorInterface $writeInfoExtractor = null,
95+
int $byPassPropertyHooks = self::DO_NOT_BYPASS_HOOKS_ON_PROPERTY,
8796
) {
8897
$this->ignoreInvalidIndices = 0 === ($throw & self::THROW_ON_INVALID_INDEX);
8998
$this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value
9099
$this->ignoreInvalidProperty = 0 === ($throw & self::THROW_ON_INVALID_PROPERTY_PATH);
91100
$this->readInfoExtractor = $readInfoExtractor ?? new ReflectionExtractor([], null, null, false);
92101
$this->writeInfoExtractor = $writeInfoExtractor ?? new ReflectionExtractor(['set'], null, null, false);
102+
$this->byPassPropertyHooks = \PHP_VERSION_ID >= 80400 ? $byPassPropertyHooks : self::DO_NOT_BYPASS_HOOKS_ON_PROPERTY;
93103
}
94104

95105
public function getValue(object|array $objectOrArray, string|PropertyPathInterface $propertyPath): mixed
@@ -216,7 +226,7 @@ public function isReadable(object|array $objectOrArray, string|PropertyPathInter
216226
];
217227

218228
// handle stdClass with properties with a dot in the name
219-
if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) {
229+
if ($objectOrArray instanceof \stdClass && str_contains($propertyPath, '.') && property_exists($objectOrArray, $propertyPath)) {
220230
$this->readProperty($zval, $propertyPath, $this->ignoreInvalidProperty);
221231
} else {
222232
$this->readPropertiesUntil($zval, $propertyPath, $propertyPath->getLength(), $this->ignoreInvalidIndices);
@@ -414,12 +424,21 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
414424
throw $e;
415425
}
416426
} elseif (PropertyReadInfo::TYPE_PROPERTY === $type) {
417-
if (!isset($object->$name) && !\array_key_exists($name, (array) $object)) {
427+
$valueSet = false;
428+
$useBypass = $this->byPassPropertyHooks & self::BYPASS_HOOKS_ON_PROPERTY_READ && $access->hasHook() && !$access->isVirtual();
429+
$valueSeemsToBeNotSet = !isset($object->$name) && !\array_key_exists($name, (array) $object);
430+
if ($valueSeemsToBeNotSet || $useBypass) {
418431
try {
419432
$r = new \ReflectionProperty($class, $name);
433+
if ($valueSeemsToBeNotSet) {
434+
if ($r->isPublic() && !$r->hasType()) {
435+
throw new UninitializedPropertyException(\sprintf('The property "%s::$%s" is not initialized.', $class, $name));
436+
}
437+
}
420438

421-
if ($r->isPublic() && !$r->hasType()) {
422-
throw new UninitializedPropertyException(\sprintf('The property "%s::$%s" is not initialized.', $class, $name));
439+
if ($useBypass) {
440+
$result[self::VALUE] = $r->getRawValue($object);
441+
$valueSet = true;
423442
}
424443
} catch (\ReflectionException $e) {
425444
if (!$ignoreInvalidProperty) {
@@ -428,7 +447,9 @@ private function readProperty(array $zval, string $property, bool $ignoreInvalid
428447
}
429448
}
430449

431-
$result[self::VALUE] = $object->$name;
450+
if (!$valueSet) {
451+
$result[self::VALUE] = $object->$name;
452+
}
432453

433454
if (isset($zval[self::REF]) && $access->canBeReference()) {
434455
$result[self::REF] = &$object->$name;
@@ -531,7 +552,12 @@ private function writeProperty(array $zval, string $property, mixed $value, bool
531552
if (PropertyWriteInfo::TYPE_METHOD === $type) {
532553
$object->{$mutator->getName()}($value);
533554
} elseif (PropertyWriteInfo::TYPE_PROPERTY === $type) {
534-
$object->{$mutator->getName()} = $value;
555+
if ($this->byPassPropertyHooks & self::BYPASS_HOOKS_ON_PROPERTY_WRITE) {
556+
$r = new \ReflectionProperty($class, $mutator->getName());
557+
$r->setRawValue($object, $value);
558+
} else {
559+
$object->{$mutator->getName()} = $value;
560+
}
535561
} elseif (PropertyWriteInfo::TYPE_ADDER_AND_REMOVER === $type) {
536562
$this->writeCollection($zval, $property, $value, $mutator->getAdderInfo(), $mutator->getRemoverInfo());
537563
}
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
4+
5+
class TestClassHooks
6+
{
7+
public string $hookGetOnly = 'default' {
8+
get => $this->hookGetOnly . ' (hooked on get)';
9+
}
10+
11+
public string $hookSetOnly = 'default' {
12+
set(string $value) {
13+
$this->hookSetOnly = $value . ' (hooked on set)';
14+
}
15+
}
16+
17+
public string $hookBoth = 'default' {
18+
get => $this->hookBoth . ' (hooked on get)';
19+
set(string $value) {
20+
$this->hookBoth = $value . ' (hooked on set)';
21+
}
22+
}
23+
}

‎src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+40Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestAdderRemoverInvalidArgumentLength;
2626
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestAdderRemoverInvalidMethods;
2727
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
28+
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassHooks;
2829
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable;
2930
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
3031
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet;
@@ -1029,6 +1030,45 @@ public function testIsReadableWithMissingPropertyAndLazyGhost()
10291030
$this->assertFalse($this->propertyAccessor->isReadable($lazyGhost, 'dummy'));
10301031
}
10311032

1033+
/**
1034+
* @requires PHP 8.4
1035+
*/
1036+
public function testBypassHookOnRead()
1037+
{
1038+
$instance = new TestClassHooks();
1039+
$propertyAccessor = new PropertyAccessor(byPassPropertyHooks: PropertyAccessor::BYPASS_HOOKS_ON_PROPERTY_READ);
1040+
$this->assertSame('default', $propertyAccessor->getValue($instance, 'hookGetOnly'));
1041+
$this->assertSame('default (hooked on get)', $this->propertyAccessor->getValue($instance, 'hookGetOnly'));
1042+
$this->assertSame('default', $propertyAccessor->getValue($instance, 'hookSetOnly'));
1043+
$this->assertSame('default', $this->propertyAccessor->getValue($instance, 'hookSetOnly'));
1044+
$this->assertSame('default', $propertyAccessor->getValue($instance, 'hookBoth'));
1045+
$this->assertSame('default (hooked on get)', $this->propertyAccessor->getValue($instance, 'hookBoth'));
1046+
}
1047+
1048+
/**
1049+
* @requires PHP 8.4
1050+
*/
1051+
public function testBypassHookOnWrite()
1052+
{
1053+
$instance = new TestClassHooks();
1054+
$propertyAccessor = new PropertyAccessor(byPassPropertyHooks: PropertyAccessor::BYPASS_HOOKS_ON_PROPERTY_WRITE);
1055+
$propertyAccessor->setValue($instance, 'hookGetOnly', 'edited');
1056+
$propertyAccessor->setValue($instance, 'hookSetOnly', 'edited');
1057+
$propertyAccessor->setValue($instance, 'hookBoth', 'edited');
1058+
1059+
$instance2 = new TestClassHooks();
1060+
$this->propertyAccessor->setValue($instance2, 'hookGetOnly', 'edited');
1061+
$this->propertyAccessor->setValue($instance2, 'hookSetOnly', 'edited');
1062+
$this->propertyAccessor->setValue($instance2, 'hookBoth', 'edited');
1063+
1064+
$this->assertSame('edited (hooked on get)', $propertyAccessor->getValue($instance, 'hookGetOnly'));
1065+
$this->assertSame('edited (hooked on get)', $this->propertyAccessor->getValue($instance2, 'hookGetOnly'));
1066+
$this->assertSame('edited', $propertyAccessor->getValue($instance, 'hookSetOnly'));
1067+
$this->assertSame('edited (hooked on set)', $this->propertyAccessor->getValue($instance2, 'hookSetOnly'));
1068+
$this->assertSame('edited (hooked on get)', $propertyAccessor->getValue($instance, 'hookBoth'));
1069+
$this->assertSame('edited (hooked on set) (hooked on get)', $this->propertyAccessor->getValue($instance2, 'hookBoth'));
1070+
}
1071+
10321072
private function createUninitializedObjectPropertyGhost(): UninitializedObjectProperty
10331073
{
10341074
if (!class_exists(ProxyHelper::class)) {

‎src/Symfony/Component/PropertyInfo/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
7.3
55
---
66

7+
* Gather data from property hooks in ReflectionExtractor
78
* Add support for `non-positive-int`, `non-negative-int` and `non-zero-int` PHPStan types to `PhpStanExtractor`
89

910
7.1

‎src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php
+15-5Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -384,11 +384,11 @@ public function getReadInfo(string $class, string $property, array $context = []
384384
}
385385

386386
if ($allowMagicGet && $reflClass->hasMethod('__get') && (($r = $reflClass->getMethod('__get'))->getModifiers() & $this->methodReflectionFlags)) {
387-
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, PropertyReadInfo::VISIBILITY_PUBLIC, false, $r->returnsReference());
387+
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, PropertyReadInfo::VISIBILITY_PUBLIC, false, $r->returnsReference(), false, false);
388388
}
389389

390390
if ($hasProperty && (($r = $reflClass->getProperty($property))->getModifiers() & $this->propertyReflectionFlags)) {
391-
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, $this->getReadVisiblityForProperty($r), $r->isStatic(), true);
391+
return new PropertyReadInfo(PropertyReadInfo::TYPE_PROPERTY, $property, $this->getReadVisiblityForProperty($r), $r->isStatic(), true, $this->propertyHasHook($r, 'get'), $this->propertyIsVirtual($r));
392392
}
393393

394394
if ($allowMagicCall && $reflClass->hasMethod('__call') && ($reflClass->getMethod('__call')->getModifiers() & $this->methodReflectionFlags)) {
@@ -472,7 +472,7 @@ public function getWriteInfo(string $class, string $property, array $context = [
472472
if ($reflClass->hasProperty($property) && ($reflClass->getProperty($property)->getModifiers() & $this->propertyReflectionFlags)) {
473473
$reflProperty = $reflClass->getProperty($property);
474474
if (!$reflProperty->isReadOnly()) {
475-
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic());
475+
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, $this->getWriteVisiblityForProperty($reflProperty), $reflProperty->isStatic(), $this->propertyHasHook($reflProperty, 'set'));
476476
}
477477

478478
$errors[] = [\sprintf('The property "%s" in class "%s" is a promoted readonly property.', $property, $reflClass->getName())];
@@ -482,7 +482,7 @@ public function getWriteInfo(string $class, string $property, array $context = [
482482
if ($allowMagicSet) {
483483
[$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__set', 2);
484484
if ($accessible) {
485-
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, PropertyWriteInfo::VISIBILITY_PUBLIC, false);
485+
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_PROPERTY, $property, PropertyWriteInfo::VISIBILITY_PUBLIC, false, false);
486486
}
487487

488488
$errors[] = $methodAccessibleErrors;
@@ -491,7 +491,7 @@ public function getWriteInfo(string $class, string $property, array $context = [
491491
if ($allowMagicCall) {
492492
[$accessible, $methodAccessibleErrors] = $this->isMethodAccessible($reflClass, '__call', 2);
493493
if ($accessible) {
494-
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, 'set'.$camelized, PropertyWriteInfo::VISIBILITY_PUBLIC, false);
494+
return new PropertyWriteInfo(PropertyWriteInfo::TYPE_METHOD, 'set'.$camelized, PropertyWriteInfo::VISIBILITY_PUBLIC, false, false);
495495
}
496496

497497
$errors[] = $methodAccessibleErrors;
@@ -885,6 +885,16 @@ private function isMethodAccessible(\ReflectionClass $class, string $methodName,
885885
return [false, $errors];
886886
}
887887

888+
private function propertyHasHook(\ReflectionProperty $property, string $hookType): bool
889+
{
890+
return \PHP_VERSION_ID >= 80400 && $property->hasHook(\PropertyHookType::from($hookType));
891+
}
892+
893+
private function propertyIsVirtual(\ReflectionProperty $property): bool
894+
{
895+
return \PHP_VERSION_ID >= 80400 && $property->isVirtual();
896+
}
897+
888898
/**
889899
* Camelizes a given string.
890900
*/

‎src/Symfony/Component/PropertyInfo/PropertyReadInfo.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/PropertyReadInfo.php
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public function __construct(
3333
private readonly string $visibility,
3434
private readonly bool $static,
3535
private readonly bool $byRef,
36+
private readonly ?bool $hasHook = null,
37+
private readonly ?bool $isVirtual = null,
3638
) {
3739
}
3840

@@ -69,4 +71,14 @@ public function canBeReference(): bool
6971
{
7072
return $this->byRef;
7173
}
74+
75+
public function hasHook(): ?bool
76+
{
77+
return $this->hasHook;
78+
}
79+
80+
public function isVirtual(): ?bool
81+
{
82+
return $this->isVirtual;
83+
}
7284
}

‎src/Symfony/Component/PropertyInfo/PropertyWriteInfo.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/PropertyWriteInfo.php
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function __construct(
3939
private readonly ?string $name = null,
4040
private readonly ?string $visibility = null,
4141
private readonly ?bool $static = null,
42+
private readonly ?bool $hasHook = null,
4243
) {
4344
}
4445

@@ -116,4 +117,9 @@ public function hasErrors(): bool
116117
{
117118
return (bool) \count($this->errors);
118119
}
120+
121+
public function hasHook(): ?bool
122+
{
123+
return $this->hasHook;
124+
}
119125
}

‎src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Symfony\Component\PropertyInfo\Tests\Fixtures\ConstructorDummy;
2121
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
2222
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
23+
use Symfony\Component\PropertyInfo\Tests\Fixtures\HookedProperties;
2324
use Symfony\Component\PropertyInfo\Tests\Fixtures\NotInstantiable;
2425
use Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy;
2526
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php71Dummy;
@@ -754,8 +755,14 @@ public function testAsymmetricVisibilityAllowPrivateOnly()
754755
public function testVirtualProperties()
755756
{
756757
$this->assertTrue($this->extractor->isReadable(VirtualProperties::class, 'virtualNoSetHook'));
758+
$this->assertTrue($this->extractor->getReadInfo(VirtualProperties::class, 'virtualNoSetHook')->isVirtual());
759+
$this->assertTrue($this->extractor->getReadInfo(VirtualProperties::class, 'virtualNoSetHook')->hasHook());
757760
$this->assertTrue($this->extractor->isReadable(VirtualProperties::class, 'virtualSetHookOnly'));
761+
$this->assertTrue($this->extractor->getReadInfo(VirtualProperties::class, 'virtualSetHookOnly')->isVirtual());
762+
$this->assertFalse($this->extractor->getReadInfo(VirtualProperties::class, 'virtualSetHookOnly')->hasHook());
758763
$this->assertTrue($this->extractor->isReadable(VirtualProperties::class, 'virtualHook'));
764+
$this->assertTrue($this->extractor->getReadInfo(VirtualProperties::class, 'virtualHook')->isVirtual());
765+
$this->assertTrue($this->extractor->getReadInfo(VirtualProperties::class, 'virtualHook')->hasHook());
759766
$this->assertFalse($this->extractor->isWritable(VirtualProperties::class, 'virtualNoSetHook'));
760767
$this->assertTrue($this->extractor->isWritable(VirtualProperties::class, 'virtualSetHookOnly'));
761768
$this->assertTrue($this->extractor->isWritable(VirtualProperties::class, 'virtualHook'));
@@ -780,6 +787,22 @@ public function testAsymmetricVisibilityMutator(string $property, string $readVi
780787
$this->assertSame($writeVisibility, $writeMutator->getVisibility());
781788
}
782789

790+
/**
791+
* @requires PHP 8.4
792+
*/
793+
public function testHookedProperties()
794+
{
795+
$this->assertTrue($this->extractor->getReadInfo(HookedProperties::class, 'hookGetOnly')->hasHook());
796+
$this->assertFalse($this->extractor->getReadInfo(HookedProperties::class, 'hookGetOnly')->isVirtual());
797+
$this->assertFalse($this->extractor->getWriteInfo(HookedProperties::class, 'hookGetOnly')->hasHook());
798+
$this->assertFalse($this->extractor->getReadInfo(HookedProperties::class, 'hookSetOnly')->hasHook());
799+
$this->assertFalse($this->extractor->getReadInfo(HookedProperties::class, 'hookSetOnly')->isVirtual());
800+
$this->assertTrue($this->extractor->getWriteInfo(HookedProperties::class, 'hookSetOnly')->hasHook());
801+
$this->assertTrue($this->extractor->getReadInfo(HookedProperties::class, 'hookBoth')->hasHook());
802+
$this->assertFalse($this->extractor->getReadInfo(HookedProperties::class, 'hookBoth')->isVirtual());
803+
$this->assertTrue($this->extractor->getWriteInfo(HookedProperties::class, 'hookBoth')->hasHook());
804+
}
805+
783806
public static function provideAsymmetricVisibilityMutator(): iterable
784807
{
785808
yield ['publicPrivate', PropertyReadInfo::VISIBILITY_PUBLIC, PropertyWriteInfo::VISIBILITY_PRIVATE];
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\PropertyInfo\Tests\Fixtures;
13+
14+
class HookedProperties
15+
{
16+
public string $hookGetOnly {
17+
get => $this->hookGetOnly . ' (hooked on get)';
18+
}
19+
public string $hookSetOnly {
20+
set(string $value) {
21+
$this->hookSetOnly = $value . ' (hooked on set)';
22+
}
23+
}
24+
public string $hookBoth {
25+
get => $this->hookBoth . ' (hooked on get)';
26+
set(string $value) {
27+
$this->hookBoth = $value . ' (hooked on set)';
28+
}
29+
}
30+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.