Skip to content

Commit 1e35403

Browse files
authored
[Feature] Add private properties interception (#412)
Add ability to intercept private properties in the classes, see #411
1 parent 2305803 commit 1e35403

File tree

6 files changed

+36
-21
lines changed

6 files changed

+36
-21
lines changed

demos/Demo/Aspect/PropertyInterceptorAspect.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class PropertyInterceptorAspect implements Aspect
2828
*
2929
* @param FieldAccess $fieldAccess Joinpoint
3030
*
31-
* @Around("access(public|protected Demo\Example\PropertyDemo->*)")
31+
* @Around("access(public|protected|private Demo\Example\PropertyDemo->*)")
3232
* @return mixed
3333
*/
3434
public function aroundFieldAccess(FieldAccess $fieldAccess)

demos/Demo/Example/PropertyDemo.php

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class PropertyDemo
2020

2121
protected $protectedProperty = 456;
2222

23+
private $privateProperty = 'test';
24+
2325
protected $indirectModificationCheck = [4, 5, 6];
2426

2527
public function showProtected()
@@ -38,5 +40,6 @@ public function __construct()
3840
if (count($this->indirectModificationCheck) !== 6) {
3941
throw new \RuntimeException("Indirect modification doesn't work!");
4042
}
43+
$this->privateProperty = $this->privateProperty . 'bar';
4144
}
4245
}

src/Aop/Framework/ClassFieldAccess.php

+11-5
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,24 @@ public function &getValueToSet()
117117
*/
118118
public function ensureScopeRule(int $stackLevel = 2): void
119119
{
120-
$property = $this->reflectionProperty;
121-
122-
if ($property->isProtected()) {
120+
$property = $this->reflectionProperty;
121+
$isProtected = $property->isProtected();
122+
$isPrivate = $property->isPrivate();
123+
if ($isProtected || $isPrivate) {
123124
$backTrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, $stackLevel+1);
124125
$accessor = $backTrace[$stackLevel] ?? [];
125126
$propertyClass = $property->class;
126127
if (isset($accessor['class'])) {
127-
if ($accessor['class'] === $propertyClass || is_subclass_of($accessor['class'], $propertyClass)) {
128+
// For private and protected properties its ok to access from the same class
129+
if ($accessor['class'] === $propertyClass) {
130+
return;
131+
}
132+
// For protected properties its ok to access from any subclass
133+
if ($isProtected && is_subclass_of($accessor['class'], $propertyClass)) {
128134
return;
129135
}
130136
}
131-
throw new AspectException("Cannot access protected property {$propertyClass}::{$property->name}");
137+
throw new AspectException("Cannot access property {$propertyClass}::{$property->name}");
132138
}
133139
}
134140

src/Core/AdviceMatcher.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ private function getAdvicesFromAdvisor(
156156

157157
// Check properties in class only for property filters
158158
if ($filterKind & Aop\PointFilter::KIND_PROPERTY) {
159-
$mask = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED;
159+
$mask = ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED | ReflectionProperty::IS_PRIVATE;
160160
foreach ($class->getProperties($mask) as $property) {
161161
if ($filter->matches($property, $class) && !$property->isStatic()) {
162162
$classAdvices[AspectContainer::PROPERTY_PREFIX][$property->name][$advisorId] = $advisor->getAdvice();

src/Proxy/Part/InterceptedConstructorGenerator.php

+9-6
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,19 @@ private function getConstructorBody(array $interceptedProperties): string
8585
$assocProperties = [];
8686
$listProperties = [];
8787
foreach ($interceptedProperties as $propertyName) {
88-
$assocProperties[] = " '{$propertyName}' => &\$this->{$propertyName}";
89-
$listProperties[] = " \$this->{$propertyName}";
88+
$assocProperties[] = " '{$propertyName}' => &\$target->{$propertyName}";
89+
$listProperties[] = " \$target->{$propertyName}";
9090
}
9191
$lines = [
92-
'$this->__properties = [',
92+
'$accessor = function(array &$propertyStorage, object $target) {',
93+
' $propertyStorage = [',
9394
implode(',' . PHP_EOL, $assocProperties),
94-
'];',
95-
'unset(',
95+
' ];',
96+
' unset(',
9697
implode(',' . PHP_EOL, $listProperties),
97-
');'
98+
' );',
99+
'};',
100+
'($accessor->bindTo($this, parent::class))($this->__properties, $this);'
98101
];
99102

100103
return implode(PHP_EOL, $lines);

tests/Go/Proxy/Part/InterceptedConstructorGeneratorTest.php

+11-8
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,17 @@ public function testGenerateWithProperties(): void
9393
$expectedCode = preg_replace('/^\s+|\s+$/m', '', '
9494
public function __construct()
9595
{
96-
$this->__properties = [
97-
\'foo\' => &$this->foo,
98-
\'bar\' => &$this->bar
99-
];
100-
unset(
101-
$this->foo,
102-
$this->bar
103-
);
96+
$accessor = function(array &$propertyStorage, object $target) {
97+
$propertyStorage = [
98+
\'foo\' => &$target->foo,
99+
\'bar\' => &$target->bar
100+
];
101+
unset(
102+
$target->foo,
103+
$target->bar
104+
);
105+
};
106+
($accessor->bindTo($this, parent::class))($this->__properties, $this);
104107
}'
105108
);
106109
$this->assertSame($expectedCode, $generatedCode);

0 commit comments

Comments
 (0)