Skip to content

Commit

Permalink
Merge pull request #428 from goaop/fix/initialization-joinpoint-origi…
Browse files Browse the repository at this point in the history
…nal-class-name

Fix constructor invocation to know about special parent class names
  • Loading branch information
lisachenko authored Aug 13, 2019
2 parents 233f71c + 523e311 commit f7fa0b3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 4 deletions.
18 changes: 14 additions & 4 deletions src/Aop/Framework/ReflectionConstructorInvocation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Go\Aop\Framework;

use Go\Aop\Intercept\ConstructorInvocation;
use Go\Core\AspectContainer;
use ReflectionClass;
use ReflectionMethod;

Expand Down Expand Up @@ -49,11 +50,16 @@ class ReflectionConstructorInvocation extends AbstractInvocation implements Cons
*/
public function __construct($className, $type, array $advices)
{
$this->class = new ReflectionClass($className);
$originalClass = $className;
if (strpos($originalClass, AspectContainer::AOP_PROXIED_SUFFIX) !== false) {
$originalClass = substr($originalClass, 0, -strlen(AspectContainer::AOP_PROXIED_SUFFIX));
}

$this->class = new ReflectionClass($originalClass);
$this->constructor = $constructor = $this->class->getConstructor();

// Give an access to call protected constructor
if ($constructor && $constructor->isProtected()) {
// Give an access to call protected/private constructors
if ($constructor && !$constructor->isPublic()) {
$constructor->setAccessible(true);
}

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

$this->instance = $this->class->newInstance(...$this->arguments);
$this->instance = $this->class->newInstanceWithoutConstructor();
$constructor = $this->getConstructor();
if ($constructor !== null) {
$constructor->invoke($this->instance, ...$this->arguments);
}

return $this->instance;
}
Expand Down
96 changes: 96 additions & 0 deletions tests/Go/Aop/Framework/ReflectionConstructorInvocationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php
/*
* Go! AOP framework
*
* @copyright Copyright 2019, Lisachenko Alexander <[email protected]>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/

namespace Go\Aop\Framework;

use Go\Core\AspectContainer;

class ReflectionConstructorInvocationTest extends AbstractInterceptorTest
{
public function testCanCreateObjectDuringInvocation()
{
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
$result = $invocation->__invoke();
$this->assertInstanceOf(\Exception::class, $result);
}

public function testKnowsAboutSpecialClassSuffix()
{
$specialName = \Exception::class . AspectContainer::AOP_PROXIED_SUFFIX;
$invocation = new ReflectionConstructorInvocation($specialName, 'unused', []);
$result = $invocation->__invoke();
$this->assertInstanceOf(\Exception::class, $result);
}

public function testCanExecuteAdvicesDuringConstruct()
{
$sequence = [];
$advice = $this->getAdvice($sequence);
$before = new BeforeInterceptor($advice);
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', [$before]);
$this->assertEmpty($sequence);
$invocation->__invoke(['Message', 100]);
$this->assertContains('advice', $sequence);
}

public function testStringRepresentation()
{
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
$name = (string)$invocation;

$this->assertEquals('initialization(Exception)', $name);
}

public function testReturnsConstructor()
{
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
$ctor = $invocation->getConstructor();
$this->assertInstanceOf(\ReflectionMethod::class, $ctor);
$this->assertEquals('__construct', $ctor->name);
}

public function testReturnsThis()
{
$invocation = new ReflectionConstructorInvocation(\Exception::class, 'unused', []);
$instance = $invocation->getThis();
$this->assertNull($instance);
$object = $invocation->__invoke(['Some error', 100]);
$this->assertEquals($object, $invocation->getThis());
}

public function testCanCreateAnInstanceEvenWithNonPublicConstructor()
{
try {
$testClassInstance = new class('Test') {
public $message;

private function __construct(string $message)
{
$this->message = $message;
}
};
$loadedClass = get_class($testClassInstance);
} catch (\Error $e) {
// let's look for all class names to find our anonymous one
foreach (get_declared_classes() as $loadedClass) {
$refClass = new \ReflectionClass($loadedClass);
if ($refClass->getFileName() === __FILE__ && strpos($refClass->getName(), 'anonymous') !== false) {
// loadedClass will contain our anonymous class
break;
}
}
}
$testClassName = $loadedClass;
$invocation = new ReflectionConstructorInvocation($testClassName, 'unused', []);
$result = $invocation->__invoke(['Hello']);
$this->assertInstanceOf($testClassName, $result);
$this->assertSame('Hello', $result->message);
}
}

0 comments on commit f7fa0b3

Please sign in to comment.