Giter Site home page Giter Site logo

ray.aop's Introduction

Ray.Aop

Aspect Oriented Framework

Scrutinizer Code Quality codecov Type Coverage Continuous Integration Total Downloads

[Japanese]

Ray.Aop package provides method interception. This feature enables you to write code that is executed each time a matching method is invoked. It's suited for cross cutting concerns ("aspects"), such as transactions, security and logging. Because interceptors divide a problem into aspects rather than objects, their use is called Aspect Oriented Programming (AOP).

A Matcher is a simple interface that either accepts or rejects a value. For Ray.AOP, you need two matchers: one that defines which classes participate, and another for the methods of those classes. To make this easy, there's factory class to satisfy the common scenarios.

MethodInterceptors are executed whenever a matching method is invoked. They have the opportunity to inspect the call: the method, its arguments, and the receiving instance. They can perform their cross-cutting logic and then delegate to the underlying method. Finally, they may inspect the return value or exception and return. Since interceptors may be applied to many methods and will receive many calls, their implementation should be efficient and unintrusive.

Example: Forbidding method calls on weekends

To illustrate how method interceptors work with Ray.Aop, we'll forbid calls to our pizza billing system on weekends. The delivery guys only work Monday thru Friday so we'll prevent pizza from being ordered when it can't be delivered! This example is structurally similar to use of AOP for authorization.

To mark select methods as weekdays-only, we define an attribute.

<?php
#[Attribute(Attribute::TARGET_METHOD)]
final class NotOnWeekends
{
}

...and apply it to the methods that need to be intercepted:

<?php
class RealBillingService
{
    #[NotOnWeekends] 
    public function chargeOrder(PizzaOrder $order, CreditCard $creditCard)
    {

Next, we define the interceptor by implementing the org.aopalliance.intercept.MethodInterceptor interface. When we need to call through to the underlying method, we do so by calling $invocation->proceed():

<?php
class WeekendBlocker implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        $today = getdate();
        if ($today['weekday'][0] === 'S') {
            throw new \RuntimeException(
                $invocation->getMethod()->getName() . " not allowed on weekends!"
            );
        }
        return $invocation->proceed();
    }
}

Finally, we configure everything. In this case we match any class, but only the methods with our @NotOnWeekends annotation:

<?php

use Ray\Aop\Sample\Annotation\NotOnWeekends;
use Ray\Aop\Sample\Annotation\RealBillingService;

$pointcut = new Pointcut(
    (new Matcher())->any(),
    (new Matcher())->annotatedWith(NotOnWeekends::class),
    [new WeekendBlocker()]
);
$bind = (new Bind)->bind(RealBillingService::class, [$pointcut]);
$billing = (new Weaver($bind, $tmpDir))->newInstance(RealBillingService::class, []);

try {
    echo $billing->chargeOrder();
} catch (\RuntimeException $e) {
    echo $e->getMessage() . "\n";
    exit(1);
}

Putting it all together, (and waiting until Saturday), we see the method is intercepted and our order is rejected:

chargeOrder not allowed on weekends!

Explicit method name match

<?php
$bind = (new Bind)->bindInterceptors('chargeOrder', [new WeekendBlocker]);
$compiler = new Weaver($bind, $tmpDir);
$billing = $compiler->newInstance('RealBillingService', [], $bind);
try {
    echo $billing->chargeOrder();
} catch (\RuntimeException $e) {
    echo $e->getMessage() . "\n";
    exit(1);
}

Own matcher

You can have your own matcher. To create contains matcher, You need to provide a class which have two method. One is matchesClass for class match. The other one is matchesMethod method match. Both return the boolean result of matched.

use Ray\Aop\AbstractMatcher;
use Ray\Aop\Matcher;

class IsContainsMatcher extends AbstractMatcher
{
    /**
     * {@inheritdoc}
     */
    public function matchesClass(\ReflectionClass $class, array $arguments) : bool
    {
        [$contains] = $arguments;

        return (strpos($class->name, $contains) !== false);
    }

    /**
     * {@inheritdoc}
     */
    public function matchesMethod(\ReflectionMethod $method, array $arguments) : bool
    {
        [$contains] = $arguments;

        return (strpos($method->name, $contains) !== false);
    }
}
$pointcut = new Pointcut(
    (new Matcher())->any(),
    new IsContainsMatcher('charge'),
    [new WeekendBlocker()]
);
$bind = (new Bind())->bind(RealBillingService::class, [$pointcut]);
$billing = (new Weaver($bind, $tmpDir))->newInstance(RealBillingService::class, [$arg1, $arg2]);

The matcher supports reading doctrine annotations or PHP8 attributes.

    public function matchesClass(\ReflectionClass $class, array $arguments) : bool
    {
        assert($class instanceof \Ray\Aop\ReflectionClass);
        $classAnnotation = $class->getAnnotation(Foo::class); // @Foo or #[Foo]
        // ...
    }

    public function matchesMethod(\ReflectionMethod $method, array $arguments) : bool
    {
         assert($method instanceof \Ray\Aop\ReflectionMethod);
         $methodAnnotation = $method->getAnnotation(Bar::class);
    }

Performance boost

Cached Weaver object can save the compiling, binding, annotation reading costs.

$weaver = unserialize(file_get_contentes('./serializedWeaver'));
$billing = (new Weaver($bind, $tmpDir))->newInstance(RealBillingService::class, [$arg1, $arg2]);

Priority

The order of interceptor invocation are determined by following rules.

  • Basically, it will be invoked in bind order.
  • PriorityPointcut has most priority.
  • Annotation method match is followed by PriorityPointcut. Invoked in annotation order as follows.
#[Auth]   // 1st
#[Cache]  // 2nd
#[Log]    // 3rd

Limitations

Behind the scenes, method interception is implemented by generating code at runtime. Ray.Aop dynamically creates a subclass that applies interceptors by overriding methods.

This approach imposes limits on what classes and methods can be intercepted:

  • Classes must be non-final
  • Methods must be public

Interceptor

In an interceptor a MethodInvocation object gets passed to the invoke method. We can the decorate the targetted instances so that you run computations before or after any methods on the target are invoked.

class MyInterceptor implements MethodInterceptor
{
    public function invoke(MethodInvocation $invocation)
    {
        // Before method invocation
        // ...
        
        // Method invocation
        $result = invocation->proceed();
        
        // After method invocation
        // ...
                
        return $result;
    }
}

With the MethodInvocation object, you can access the target method's invocation object, method's and parameters.

/** @var $method \Ray\Aop\ReflectionMethod */
$method = $invocation->getMethod();
/** @var $class \Ray\Aop\ReflectionClass */
$class = $invocation->getMethod()->getDeclaringClass();
  • $method->getAnnotations() - Get method annotations
  • $method->getAnnotation($name) - Get method annotation
  • $class->->getAnnotations() - Get class annotations
  • $class->->getAnnotation($name) - Get class annotation

Annotation/Attribute

Ray.Aop can be used either with doctrine/annotation in PHP 7/8 or with an Attributes in PHP8. See the annotation code examples in the older README(v2.9).

AOP Alliance

The method interceptor API implemented by Ray.Aop is a part of a public specification called AOP Alliance.

Installation

The recommended way to install Ray.Aop is through Composer.

# Add Ray.Aop as a dependency
$ composer require ray/aop ^2.0

Performance

Compilation of the AOP class allows Ray.Aop to run faster. Annotations are only loaded at first compile time, so they do not affect runtime performance. During the development phase and even at first runtime, PHP files are cached using the file timestamps, so normally you do not need to worry about the cost of annotation generation, but by setting up an annotation reader in the application bootstrap, first-time compile time performance. This setting is especially useful for large applications.

APCu

SevericeLocator::setReader(new PsrCachedReader(new Reader(), $apcuCache));

PHP8 attributes only (recommended)

SevericeLocator::setReader(new AttributeReader);`

Integrated DI framework

  • See also the DI framework Ray.Di which integrates DI and AOP.

  • Note: This documentation of the most part is taken from Guice/AOP.

ray.aop's People

Contributors

apple-x-co avatar fiahfy avatar iteman avatar izayoi256 avatar kawahara avatar kenjis avatar koriym avatar kuma-guy avatar mackstar avatar madapaja avatar momospnr avatar mugeso avatar naokitsuchiya avatar polidog avatar sasezaki avatar scrutinizer-auto-fixer avatar wand2016 avatar wyrihaximus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ray.aop's Issues

php7.1のnullabe引数が利用できない

nullableな引数がある関数に対してアノテーションをつけると、コンパイルしたファイルには ? が外れてしまい、実行時エラーになりました。

対象のfunction

/**
 * Annotation()
 */
function run(?int $id): int {}

コンパイルされたコード

function run(int $id): int {}

Matcher class has too many responsibility

It seems that Matcher class has responsibility:

  1. a DSL interface e.g. $matcher->subclassesOf()
  2. do verify constraints via __invoke magic method

This means current implementation violates the SRP.
Especially, it's a very violation that Macher class verifies various constrains.

So, I suggest refactoring Matcher class like PHPUnit.

Matcher:: createMatcher()

Own matcher

You can have your own matcher.
To create contains matcher, You need to provide a class which have two method. One is contains for interface.
The other one is isContains which return the result of the contains match.

use Ray\Aop\AbstractMatcher;

class MyMatcher extends AbstractMatcher
{
    /**
     * @param $contain
     *
     * @return MyMatcher
     */
    public function contains($contain)
    {
        $this->createMatcher(__FUNCTION__, $contain);

        return clone $this;

    }

    /**
     * Return isContains
     *
     * @param $name    class or method name
     * @param $target  \Ray\Aop\AbstractMatcher::TARGET_CLASS | \Ray\Aop\AbstractMatcher::Target_METHOD
     * @param $contain
     *
     * @return bool
     */
    protected function isContains($name, $target, $contain)
    {
        $result = (strpos($name, $contain) !== false);

        return $result;
    }
}

Composerでの依存性解決エラー

php composer.phar install 時に下記エラーが発生します。

Your requirements could not be solved to an installable set of packages.

  Problem 1
    - The requested package doctrine/common 2.3.* could not be found.

Potential causes:
 - A typo in the package name
 - The package is not available in a stable-enough version according to your minimum-stability setting
   see https://groups.google.com/d/topic/composer-dev/_g3ASeIFlrc/discussion for more details.

https://github.com/koriym/Ray.Aop/blob/master/composer.json#L17 のdoctrine/commonバージョンを、
"2.3.x-dev" にすることで解決するかと思います。

対応お願いします。

Don't work extend compiled parent

Sources:

class MetaCrawlerService extends CrawlerService

CrawlerService and MetaCrawlerService have pointcuts. But after compiling gets:

class MetaCrawlerService_KGlnUTY extends MetaCrawlerService implements WeavedInterface
abstract class CrawlerService_NXUWZ0A extends CrawlerService implements WeavedInterface

I expected that MetaCrawlerService_KGlnUTY will extend CrawlerService_NXUWZ0A or something similar.

Workaround:

  • copy MetaCrawlerService as is MetaCrawlerService_XXX
  • replace in MetaCrawlerService_XXX extends CrawlerService to CrawlerService_NXUWZ0A
  • replace in MetaCrawlerService_KGlnUTY extends MetaCrawlerService to MetaCrawlerService_XXX
  • in all methods of CrawlerService_NXUWZ0A replace
        if (!$this->isAspect) {
            $this->isAspect = true;
            call_user_func_array([$this, 'parent::' . __FUNCTION__], func_get_args());
            return;
        }

to

        if (!$this->isAspect) {
            $this->isAspect = true;

            $class = (new \ReflectionObject($this))->getParentClass();

            $rc1 = new \ReflectionClass($class->name);

            do {
                $method = new \ReflectionMethod($rc1->getParentClass()->name, __FUNCTION__);
                $rc1 = new \ReflectionClass($method->class);
            } while($rc1->implementsInterface(WeavedInterface::class) );

            $method = new \ReflectionMethod($rc1->name, __FUNCTION__);
            $method->invokeArgs($this, func_get_args());
            
            return;
        }

Without this gets exception:

Symfony\Component\Debug\Exception\FatalThrowableError  : Maximum function nesting level of '256' reached, aborting!

  at /var/www/app/vendor/ray/aop/src/ReflectiveMethodInvocation.php:110
    106|     {
    107|         if ($this->interceptors === [] && \is_callable($this->callable)) {
    108|             return call_user_func_array($this->callable, (array) $this->arguments);
    109|         }
  > 110|         $interceptor = array_shift($this->interceptors);
    111|         if ($interceptor instanceof MethodInterceptor) {
    112|             return $interceptor->invoke($this);
    113|         }
    114|

Add Compiler::noBindNewInstance()

Interceptor can be attached after aspect class creation.

This function is necessary to avoid interceptor's affect at dependency injection. (=setter method can't bind interceptor)

Sample code is like as below.

$instance = $compiler->noBindNewInstance($definition->class, $params, $bind);
$hasBinding = $bind->hasBinding();

// setter injection here
// ....

 // attach interceptors
if ($hasBinding) {
    $instance->rayAopBind = $bind;
}

Docblox copy

Weaved class should have doc comment block (class and method) as same as target class.

Matcher::startsWith()

Matcher interface need to updated for correct method name.

Updated

public function startsWith($prefix);

Deprecated

public function startWith($prefix);

BC should not break with this change.

Wrong compiled method of parent

Bug Report

#181 にて親クラスのメソッドのインターセプトが実装されましたが、
メソッドシグネチャにnamescpace外のクラスが記述されるとエラーになります。

How to reproduce

demo/src に以下の2つのクラスを作成します。

<?php
declare(strict_types=1);

namespace Ray\Aop\Demo;

use ArrayIterator;
use Traversable;

class A
{
    public function passIterator(ArrayIterator $iterator): Traversable
    {
        return $iterator;
    }
}
<?php
declare(strict_types=1);

namespace Ray\Aop\Demo;

class B extends A
{
}

demo に以下のファイルを作成し、実行します。

<?php

use Ray\Aop\NullInterceptor;

require __DIR__ . '/bootstrap.php';

$compiler = new Ray\Aop\Compiler(__DIR__ . '/tmp');
$bind = (new Ray\Aop\Bind())->bindInterceptors('passIterator', [new NullInterceptor()]);
$b = $compiler->newInstance(Ray\Aop\Demo\B::class, [], $bind);
$b->passIterator(new ArrayIterator());
PHP Fatal error:  Could not check compatibility between Ray\Aop\Demo\B_3617677882::passIterator(Ray\Aop\Demo\ArrayIterator $iterator): Ray\Aop\Demo\Traversable and Ray\Aop\Demo\A::passIterator(ArrayIterator $iterator): Traversable, because class Ray\Aop\Demo\ArrayIterator is not available in /Users/tsuchiya/work/tsuchiya/Ray.Aop/demo/tmp/Ray_Aop_Demo_B_3617677882.php on line 15

Fatal error: Could not check compatibility between Ray\Aop\Demo\B_3617677882::passIterator(Ray\Aop\Demo\ArrayIterator $iterator): Ray\Aop\Demo\Traversable and Ray\Aop\Demo\A::passIterator(ArrayIterator $iterator): Traversable, because class Ray\Aop\Demo\ArrayIterator is not available in /Users/tsuchiya/work/tsuchiya/Ray.Aop/demo/tmp/Ray_Aop_Demo_B_3617677882.php on line 15

生成されたクラスは以下です。

<?php

declare (strict_types=1);
namespace Ray\Aop\Demo;

use Ray\Aop\WeavedInterface;
use Ray\Aop\ReflectiveMethodInvocation as Invocation;
class B_3617677882 extends \Ray\Aop\Demo\B implements WeavedInterface
{
    public $bind;
    public $bindings = [];
    public $methodAnnotations = 'a:0:{}';
    public $classAnnotations = 'a:0:{}';
    private $isAspect = true;
    public function passIterator(ArrayIterator $iterator) : Traversable
    {
        if (!$this->isAspect) {
            $this->isAspect = true;
            return call_user_func_array([$this, 'parent::' . __FUNCTION__], func_get_args());
        }
        $this->isAspect = false;
        $result = (new Invocation($this, __FUNCTION__, func_get_args(), $this->bindings[__FUNCTION__]))->proceed();
        $this->isAspect = true;
        return $result;
    }
}

Can't compile with anonymous class

Bug Report

無名クラスが含まれるコードはAOPコードをコンパイルできません。

Code that contains an anonymous class cannot compile AOP code.

How to reproduce

class FakeAnonymousClass
{
    public function hasAnonymousClass($a): object
    {
        return new class {};
    }
}

Cuase Ray\Aop\Exception\MultipleClassInOneFileException
/Users/akihito/git/Ray.Aop/src/CodeVisitor.php:67

Google_Client(google\apiclient)の読み込みエラー

Bear.Sunday経由でRay.Aopを利用しておりますが、
google\apiclientと組み合わせて利用した際に下記のエラーが発生します。

1) kght6123\ossnote\Resource\App\GloginTest::testOnGet
The use statement with non-compound name 'Google_Client' has no effect

/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/var/tmp/app/di/kght6123_ossnote_Resource_App_Glogin_JxlmmSE.php:26
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/aop/src/Compiler.php:119
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/aop/src/Compiler.php:119
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/aop/src/Compiler.php:82
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/di/src/Dependency.php:117
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/compiler/src/OnDemandCompiler.php:61
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/compiler/src/ScriptInjector.php:235
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/compiler/src/ScriptInjector.php:196
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/compiler/src/ScriptInjector.php:138
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/bear/resource/src/AppAdapter.php:56
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/bear/resource/src/Factory.php:46
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/bear/resource/src/Resource.php:95
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/tests/Resource/App/GloginTest.php:21

ERRORS!
Tests: 3, Assertions: 20, Errors: 1.
Script phpunit handling the test event returned with error code 2

google\apiclientnamespaceが未定義で、autoload.phpを利用して解決しますが
その際にRay.Aopを経由すると、Google_Clientクラスがエラーで解決できないと想定しています。

試しにuse Google_Client;を除去すると、下記のnot foundエラーになります。

1) kght6123\ossnote\Resource\App\GloginTest::testOnGet
Error: Class 'kght6123\ossnote\Resource\App\Google_Client' not found

/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/src/Resource/App/Glogin.php:83
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/var/tmp/app/di/kght6123_ossnote_Resource_App_Glogin_JxlmmSE.php:39
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/aop/src/ReflectiveMethodInvocation.php:110
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/src/Interceptor/BenchMarker.php:23
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/ray/aop/src/ReflectiveMethodInvocation.php:114
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/var/tmp/app/di/kght6123_ossnote_Resource_App_Glogin_JxlmmSE.php:43
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/bear/resource/src/Invoker.php:41
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/bear/resource/src/AbstractRequest.php:144
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/vendor/bear/resource/src/Resource.php:146
/Users/kogahirotaka/develop/bear.sunday/kght6123.ossnotes/backend/tests/Resource/App/GloginTest.php:24

ERRORS!
Tests: 3, Assertions: 20, Errors: 1.
Script phpunit handling the test event returned with error code 2

下記はコードの抜粋です。

namespace kght6123\ossnote\Resource\App;

require __DIR__ . '/../../../vendor/autoload.php';

use Google_Client;// has no effect

class Glogin extends BaseResourceObject {
  public function onPost(string $userid, string $password, string $path): ResourceObject {
    $client = new Google_Client();// not found error.
  }
}

フルバージョンのコードは下記で公開しております。
https://github.com/kght6123/ossnotes/blob/master/backend/src/Resource/App/Glogin.php

可能であれば修正していただけますでしょうか?

コードの記述が想定と異なっていたり、
Issuesの投稿先が間違っていましたら、
申し訳ございません。

お手数をおかけいたしますが、
ご確認の程、よろしくお願いいたします。

🎉 10 years

🎉 It's been over 10 years since the initial release of Ray.Aop.

Initially released as a proxy, it later evolved to use the PHP-Parser for a codegen approach, providing a full-fledged AOP environment that also adheres to the AOP Alliance standards. Since its 2.0 release in 2015, it has continuously maintained backward compatibility.

We plan to use our own codegen engine. Initial benchmarks show performance more than 40 times faster than the current PHP-Parser based one.

🎉 Ray.Aopの最初のリリースから10年以上が経過しました。

当初はproxyとしてリリースされましたが、その後PHP-Parserを利用したcodegen方式へと進化し、AOPアラインアンスにも準拠した本格的なAOP環境を提供してきました。2015年の2.0のリリース以降は後方互換性を維持し続けています。

これから独自のcodegenエンジンを採用する予定です。初期のベンチマークでは、現在のPHP-Parserベースのものと比べて40倍以上の高速性能を発揮しています。


Compiler based on nikic/PHP-Parser

php-parser based 2023-09-17 19 50 28

Compiler based on our own optimized engine

costum codegen 2023-09-17 19 50 40

php7.1のvoid関数が利用できない

PHP7.1新機能/void 関数

再現方法

  • /demo/src/AnnotationRealBillingService.phpを一部改変
<?php
declare(strict_types=1);
namespace Ray\Aop\Demo;
class AnnotationRealBillingService implements BillingService
{
    /**
     * @WeekendBlock
     */
-   public function chargeOrder()
+   public function chargeOrder(): void
    {
        echo "Charged.\n";
    }
}
  • /demo/03-annotation-bind.php を実行
php --version
PHP 7.2.19-0ubuntu0.18.04.2 (cli) (built: Aug 12 2019 19:34:28) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.19-0ubuntu0.18.04.2, Copyright (c) 1999-2018, by Zend Technologies
php 03-annotation-bind.php
PHP Fatal error:  A void function must not return a value in /home/wand/Desktop/learn/repos/Ray.Aop/demo/tmp/Ray_Aop_Demo_AnnotationRealBillingService_NXhGYhg.php on line 22

コンパイル後のファイル

<?php

declare (strict_types=1);
namespace Ray\Aop\Demo;

use Ray\Aop\WeavedInterface;
use Ray\Aop\ReflectiveMethodInvocation as Invocation;
class AnnotationRealBillingService_NXhGYhg extends \Ray\Aop\Demo\AnnotationRealBillingService implements BillingService, WeavedInterface
{
    public $bind;
    public $bindings = [];
    public $methodAnnotations = 'a:1:{s:11:"chargeOrder";a:1:{i:0;O:25:"Ray\\Aop\\Demo\\WeekendBlock":0:{}}}';
    public $classAnnotations = 'a:0:{}';
    private $isAspect = true;
    /**
     * @WeekendBlock
     */
    public function chargeOrder() : void
    {
        if (!$this->isAspect) {
            $this->isAspect = true;
            return call_user_func_array([$this, 'parent::' . __FUNCTION__], func_get_args());
        }
        $this->isAspect = false;
        $result = (new Invocation($this, __FUNCTION__, func_get_args(), $this->bindings[__FUNCTION__]))->proceed();
        $this->isAspect = true;
        return $result;
    }
}

:void タイプヒントがついているのに$resultreturnしてしまっている

    public function chargeOrder() : void
    {
        ...
        return $result;  // ouch!
    }

Error with variadic parameters

The cause was refactoring to use the original argument as it is for speeding up.
This is wrong, and it can not do such a thing when it is a variable argument, and can not do it without getting with func_get_args (). Slow but safe.

原因は高速化のために、元の引数をそのまま使おうとしたリファクタリング。
これが間違っていて、可変引数の時はそのようなことができずfunc_get_args()で取得しないとできない。速度は劣るが安全。

Annotation cache

Hi,

I was wondering what your thoughts are regarding the hardcoded AnnotationReader in Bind.php. Would you guys be open to have a reader as a possible constructor argument for example?

In my case I would like to use the CachedReader instead for performance.

PS: Great library!

Upgrade has lead to the following error

Catchable fatal error: Argument 1 passed to Ray\Aop\Matcher::__construct() must implement interface Doctrine\Common\Annotations\Reader, none given, called in /Users/MackstarMBA/Sites/ProjectZulu/vendor/ray/di/src/AbstractModule.php on line 193 and defined in /Users/MackstarMBA/Sites/ProjectZulu/vendor/ray/aop/src/Matcher.php on line 54

Matcher::setAnnotationReader($reader)

Enable to set cached annotation reader for Matcher.
This annotation set statically for further use at Matcher.

<?php

use Doctrine\Common\Annotations\CachedReader;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Cache\ApcCache;

$reader = new CachedReader(new AnnotationReader(new ApcCache));
Matcher::setAnnotationReader($reader);

Organizing the generated AOP code

Current version:

class FakeMock_358616026 extends \Ray\Aop\FakeMock implements WeavedInterface
{
    public $bind;
    public $bindings = [];
    public $methodAnnotations = 'a:0:{}';
    public $classAnnotations = 'a:0:{}';
    public $isAspect = true;
    /**
     * doc comment of returnSame
     */
    public function returnSame($a)
    {
        if (!$this->isAspect) {
            $this->isAspect = true;
            return call_user_func_array([parent::class, __FUNCTION__], func_get_args());
        }
        $this->isAspect = false;
        $result = (new Invocation($this, __FUNCTION__, func_get_args(), $this->bindings[__FUNCTION__]))->proceed();
        $this->isAspect = true;
        return $result;
    }
}

Update version:

class FakeMock_358616026 extends \Ray\Aop\FakeMock implements WeavedInterface
{
    use AopTrait;

    /**
     * doc comment of returnSame
     */
    public function returnSame($a)
    {
        return $this->__aop(func_get_args(), __FUNCTION__);
    }
}
<?php

namespace Ray\Aop;

use Ray\Aop\ReflectiveMethodInvocation as Invocation;
use function call_user_func_array;
use function func_get_args;

trait AopTrait
{
    private $bind;
    private $bindings = [];
    public $methodAnnotations = 'a:0:{}';
    public $classAnnotations = 'a:0:{}';
    private $isAspect = true;

    private function __aop(array $args, string $func)
    {
        if (!$this->isAspect) {
            $this->isAspect = true;
            return call_user_func_array([parent::class, __FUNCTION__], func_get_args());
        }
        $this->isAspect = false;
        $result = (new Invocation($this, __FUNCTION__, func_get_args(), $this->bindings[__FUNCTION__]))->proceed();
        $this->isAspect = true;
        return $result;
    }
}

何が得られるか?

  • デバックトレースの時の読みやすさ、ステップ実行のしやすさ
  • レンダリング速度
    • php parserを使わなくてもいいかも? (依存を減らすことができるかもしれない)

Why autoload param disabled for class_exists in Compiler.php ?

Case: I use composer and every request your library recompiled all classes.

if (class_exists($aopClassName, false)) {

        if (class_exists($aopClassName, false)) {
class_exists ( string $class_name [, bool $autoload = TRUE ] ) : bool

This function checks whether or not the given class has been defined.

class_name
The class name. The name is matched in a case-insensitive manner.

autoload
Whether or not to call __autoload by default.

ytake/Laravel-Aspect#63

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.