Skip to content

Commit f7fa0b3

Browse files
authored
Merge pull request #428 from goaop/fix/initialization-joinpoint-original-class-name
Fix constructor invocation to know about special parent class names
2 parents 233f71c + 523e311 commit f7fa0b3

File tree

2 files changed

+110
-4
lines changed

2 files changed

+110
-4
lines changed

src/Aop/Framework/ReflectionConstructorInvocation.php

+14-4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Go\Aop\Framework;
1212

1313
use Go\Aop\Intercept\ConstructorInvocation;
14+
use Go\Core\AspectContainer;
1415
use ReflectionClass;
1516
use ReflectionMethod;
1617

@@ -49,11 +50,16 @@ class ReflectionConstructorInvocation extends AbstractInvocation implements Cons
4950
*/
5051
public function __construct($className, $type, array $advices)
5152
{
52-
$this->class = new ReflectionClass($className);
53+
$originalClass = $className;
54+
if (strpos($originalClass, AspectContainer::AOP_PROXIED_SUFFIX) !== false) {
55+
$originalClass = substr($originalClass, 0, -strlen(AspectContainer::AOP_PROXIED_SUFFIX));
56+
}
57+
58+
$this->class = new ReflectionClass($originalClass);
5359
$this->constructor = $constructor = $this->class->getConstructor();
5460

55-
// Give an access to call protected constructor
56-
if ($constructor && $constructor->isProtected()) {
61+
// Give an access to call protected/private constructors
62+
if ($constructor && !$constructor->isPublic()) {
5763
$constructor->setAccessible(true);
5864
}
5965

@@ -77,7 +83,11 @@ final public function proceed()
7783
return $currentInterceptor->invoke($this);
7884
}
7985

80-
$this->instance = $this->class->newInstance(...$this->arguments);
86+
$this->instance = $this->class->newInstanceWithoutConstructor();
87+
$constructor = $this->getConstructor();
88+
if ($constructor !== null) {
89+
$constructor->invoke($this->instance, ...$this->arguments);
90+
}
8191

8292
return $this->instance;
8393
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
/*
3+
* Go! AOP framework
4+
*
5+
* @copyright Copyright 2019, Lisachenko Alexander <[email protected]>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
11+
namespace Go\Aop\Framework;
12+
13+
use Go\Core\AspectContainer;
14+
15+
class ReflectionConstructorInvocationTest extends AbstractInterceptorTest
16+
{
17+
public function testCanCreateObjectDuringInvocation()
18+
{
19+
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
20+
$result = $invocation->__invoke();
21+
$this->assertInstanceOf(\Exception::class, $result);
22+
}
23+
24+
public function testKnowsAboutSpecialClassSuffix()
25+
{
26+
$specialName = \Exception::class . AspectContainer::AOP_PROXIED_SUFFIX;
27+
$invocation = new ReflectionConstructorInvocation($specialName, 'unused', []);
28+
$result = $invocation->__invoke();
29+
$this->assertInstanceOf(\Exception::class, $result);
30+
}
31+
32+
public function testCanExecuteAdvicesDuringConstruct()
33+
{
34+
$sequence = [];
35+
$advice = $this->getAdvice($sequence);
36+
$before = new BeforeInterceptor($advice);
37+
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', [$before]);
38+
$this->assertEmpty($sequence);
39+
$invocation->__invoke(['Message', 100]);
40+
$this->assertContains('advice', $sequence);
41+
}
42+
43+
public function testStringRepresentation()
44+
{
45+
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
46+
$name = (string)$invocation;
47+
48+
$this->assertEquals('initialization(Exception)', $name);
49+
}
50+
51+
public function testReturnsConstructor()
52+
{
53+
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
54+
$ctor = $invocation->getConstructor();
55+
$this->assertInstanceOf(\ReflectionMethod::class, $ctor);
56+
$this->assertEquals('__construct', $ctor->name);
57+
}
58+
59+
public function testReturnsThis()
60+
{
61+
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
62+
$instance = $invocation->getThis();
63+
$this->assertNull($instance);
64+
$object = $invocation->__invoke(['Some error', 100]);
65+
$this->assertEquals($object, $invocation->getThis());
66+
}
67+
68+
public function testCanCreateAnInstanceEvenWithNonPublicConstructor()
69+
{
70+
try {
71+
$testClassInstance = new class('Test') {
72+
public $message;
73+
74+
private function __construct(string $message)
75+
{
76+
$this->message = $message;
77+
}
78+
};
79+
$loadedClass = get_class($testClassInstance);
80+
} catch (\Error $e) {
81+
// let's look for all class names to find our anonymous one
82+
foreach (get_declared_classes() as $loadedClass) {
83+
$refClass = new \ReflectionClass($loadedClass);
84+
if ($refClass->getFileName() === __FILE__ && strpos($refClass->getName(), 'anonymous') !== false) {
85+
// loadedClass will contain our anonymous class
86+
break;
87+
}
88+
}
89+
}
90+
$testClassName = $loadedClass;
91+
$invocation = new ReflectionConstructorInvocation($testClassName, 'unused', []);
92+
$result = $invocation->__invoke(['Hello']);
93+
$this->assertInstanceOf($testClassName, $result);
94+
$this->assertSame('Hello', $result->message);
95+
}
96+
}

0 commit comments

Comments
 (0)