Giter VIP home page Giter VIP logo

ray.aop's Introduction

Ray.Aop

Aspect Oriented Framework

Scrutinizer Code Quality codecov Type Coverage Continuous Integration Total Downloads

ray-di logo

[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 using the Aspect class:

use Ray\Aop\Aspect;
use Ray\Aop\Matcher;

$aspect = new Aspect();
$aspect->bind(
    (new Matcher())->any(),
    (new Matcher())->annotatedWith(NotOnWeekends::class),
    [new WeekendBlocker()]
);

$billing = $aspect->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!

PECL Extension

Ray.Aop also supports a PECL extension. When the extension is installed, you can use the weave method to apply aspects to all classes in a directory:

$aspect = new Aspect();
$aspect->bind(
    (new Matcher())->any(),
    (new Matcher())->annotatedWith(NotOnWeekends::class),
    [new WeekendBlocker()]
);
$aspect->weave(__DIR__ . '/src'); // Weave the aspects to all classes in the directory that match the matcher.

$billing = new RealBillingService();
echo $billing->chargeOrder(); // Interceptors applied

With the PECL extension:

  • You can create new instances anywhere in your code using the normal new keyword.
  • Interception works even with final classes and methods.

To use these features, simply install the PECL extension and Ray.Aop will automatically utilize it when available. PHP 8.1+ is required for the PECL extension.

Installing the PECL extension

PHP 8.1 or higher is required to use the PECL extension. For more information, see ext-rayaop.

Configuration Options

When creating an Aspect instance, you can optionally specify a temporary directory:

$aspect = new Aspect('/path/to/tmp/dir');

If not specified, the system's default temporary directory will be used.

This concludes the basic usage of Ray.Aop. For more detailed information and advanced usage, please refer to the full documentation.

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);
    }
}

Interceptor Details

In an interceptor, a MethodInvocation object is passed to the invoke method:

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

$invocation->proceed() invokes the next interceptor in the chain. If no more interceptors are present, it calls the target method. This chaining allows multiple interceptors for a single method, executing in the order bound.

Example execution flow for interceptors A, B, and C:

  1. Interceptor A (before)
  2. Interceptor B (before)
  3. Interceptor C (before)
  4. Target method
  5. Interceptor C (after)
  6. Interceptor B (after)
  7. Interceptor A (after)

This chaining mechanism allows you to combine multiple cross-cutting concerns (like logging, security, and performance monitoring) for a single method.

With the MethodInvocation object, you can:

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

Annotation/Attribute

Ray.Aop can be used either with doctrine/annotation in PHP 7/8 or with an Attributes in PHP8.

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

PHP8 attributes only (recommended)

SevericeLocator::setReader(new AttributeReader);`

Integrated DI framework

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

Technical Information

For more detailed technical information, please refer to the following resources:


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

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

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!

🎉 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

Mixed is converted to null|mixed

Bug Report

Mixed is converted to null|mixed

How to reproduce

    public function returnSame(mixed $param): void

This original method would be converted as follows.

    public function returnSame(null|mixed $param): void

null|mixed is not allowed as it is a redundant expression. Also, the only type that is not allowed to be nullable in this way is mixed as of PHP 8.3.

null|mixed は冗長な表現なので認められません。またこのようにnullableな型が認められないのはPHP8.3現在の今、mixedだけです。

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|

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

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" にすることで解決するかと思います。

対応お願いします。

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の投稿先が間違っていましたら、
申し訳ございません。

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

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;
    }
}

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

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

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!
    }

Docblox copy

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

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

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

対象のfunction

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

コンパイルされたコード

function run(int $id): int {}

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);

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;
}

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.

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を使わなくてもいいかも? (依存を減らすことができるかもしれない)

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;
    }
}

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.

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.