Coder Social home page Coder Social logo

goaop / framework Goto Github PK

View Code? Open in Web Editor NEW
1.6K 85.0 160.0 2.53 MB

:gem: Go! AOP PHP - modern aspect-oriented framework for the new level of software development

Home Page: go.aopphp.com

License: MIT License

PHP 99.99% Procfile 0.01%
aspect-oriented-framework php aop framework interceptor

framework's People

Contributors

andrey-yantsen avatar andy-shea avatar badrutdinovrr avatar blanchonvincent avatar cordoval avatar daniel-sc avatar dependabot-preview[bot] avatar func0der avatar hax avatar icanhazstring avatar idr0id avatar infabo avatar k-meister avatar keichoro avatar lisachenko avatar mdoelker avatar naktibalda avatar natechicago avatar rastusik avatar rlasinski avatar samsonasik avatar schlessera avatar scrutinizer-auto-fixer avatar smuggli avatar sobolevn avatar szepeviktor avatar thecelavi avatar thiagolcks avatar tonyschmitt avatar wolg 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  avatar

Watchers

 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

framework's Issues

Use serialization/unserialization of interceptors per class to improve performance

Method AopChildFactory::injectJoinpoints() can receive list of advices as a second argument. If list of advices are present, we can easily wrap it with joinpoints and inject directly to the class. So, overhead for matching methods per class load will be smaller.

List of advices should be serialized and stored with the same class. This will give automatic invalidation of advices for debug mode and maximum performance for production mode.

Do not need to intercept all methods from proxy class

There is a conflict when both methods and properties are intercepted.
This is occurs when a proxy has new auto-generated methods (getter, setter and constructor or anything else) and getAdvicesForClass() uses proxy class reflection to extract all methods and generates code that relies on parent methods which can be absent in the general case.

Go! + ZF2 doesn't works

I tried to use Go! + ZF2 and received fatal error:

Cannot redeclare Go\Instrument\ClassLoading\ensureLibraryAutoloaderIsFirst() (previously declared in /home/denis/web/TulaCoFramework/tulaco-php-framework/vendor/lisachenko/go-aop-php/src/Go/Instrument/ClassLoading/composer.php:20) in /home/denis/web/TulaCoFramework/tulaco-php-framework/data/cache/aspect/vendor/lisachenko/go-aop-php/src/Go/Instrument/ClassLoading/composer.php on line 40

Maybe I configured something wrong?

Add support for privileged advices

Privileged aspect has access to private or protected resources of other types. So code in advices of that aspect will be able to use private and protected fields of original class.

For example:

/**
 * Test class
 */
class Test {
    private $value = 123;
    public function concrete() {}
}

...

/**
 * @Privileged
 */
class Aspect {
    /**
     * @Before("execution Test->concrete(*)")
     */
    function beforeConcrete(MethodInvocation $invocation)
    {
        $obj = $invocation->getThis();
        echo $obj->value; // We can use private fields here because aspect is privileged
    }
}

Theoretically it's possible to use $this pointer directly for advices:

/**
 * @Privileged
 */
class Aspect {
    /**
     * @Before("execution Test->concrete(*)")
     */
    function beforeConcrete()
    {
        // $this will be in the scope of current object (Test), not the aspect scope!
        echo $this->value; // Just use private field of Test object
    }
}

[Pointcut] SignaturePropertyPointcut matches static property

Pointcut matcher should use one more check on inverted bitmask to prevent matching on static properties.
Notice relates to this bug:

User Notice: Trying to access undeclared property levels in 
    ../app/var/cache/aspect/vendor/monolog/src/Monolog/Logger.php line 427

Intercept methods in traits

Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies

New code can contain traits, so library should be able to intercept methods in traits. This can be done by generating proxy trait for original trait and renaming proxy trait to the original trait name to intercept methods.

This is an unique feature and probably an unique pattern of design that allows to create proxy even for trait! 🤘

Add ordering for advices

To build correct chain there is a need to use ordering for advices, this information should be defined with the help of annotations.

Support for annotation pointcuts

Subject should be implemented for flexible control over joinpoints. It will be awesome to add an annotation before the method and intercept it with AOP:

Method in the class:

    /**
     * Test cacheable by annotation
     *
     * @Cacheable
     * @param float $timeToSleep Amount of time to sleep
     *
     * @return string
     */
    public function cacheMe($timeToSleep)
    {
        usleep($timeToSleep * 1e6);
        return 'Yeah';
    }

Advice definition:

    /**
     * Cacheable methods
     *
     * @param MethodInvocation $invocation Invocation
     *
     * @Around("@annotation(Annotation\Cacheable)")
     */
    public function aroundCacheable(MethodInvocation $invocation)
    {
        static $memoryCache = array();

        $obj   = $invocation->getThis();
        $class = is_object($obj) ? get_class($obj) : $obj;
        $key   = $class . ':' . $invocation->getMethod()->name;
        if (!isset($memoryCache[$key])) {
            $memoryCache[$key] = $invocation->proceed();
        }
        return $memoryCache[$key];
    }

Handle an exceptions in the WeaverTransformer during source parsing

There are cases when TokenReflection couldn't parse the source correctly (for example, NativeSessionHandler.php). In that cases FileProcessingException is thrown.
WeaverTransfomer should handle this exception and just skip that source. Unfortunately, it's impossible to generate a notice or a warning, because this can lead to an ErrorException in a modern frameworks that convert notices and warnings into exception in the development mode.

Enable serialization of interceptors for future caching

All interceptors should implement Serializable interface. Key problem here is that Closure can not be serialized, so need to decide how this can be implemented.

With serialization support overhead for production mode can be lowered by getting the list of advices for the class from the cache.

[Core] Introduction support for PHP 5.4

Support for introduction advice should be available for PHP5.4 with the help of traits. Introduction will combine two parts: interfaces and traits into one declare-parents advice that can be applied to the specific classes.

Add real pointcut references to the pointcut expression

Currently, pointcut can be referenced from another pointcut only by special 'pointcut' property. There is no way to combine a referenced pointcut with another pointcut.

This can be done in the PointcutParser, syntax for pointcut references should be the following: AspectClassName->pointcutMethodName

Notice: Indirect modification of overloaded property ... has no effect

There is a class with protected property $tasks which is an array:

class TaskManager {

    protected $tasks = array();
}

There is also an advice which intercepts an access to this property:

    /**
     * Access interceptor
     * @param FieldAccess $property Joinpoint
     *
     * @Around("access(protected TaskManager->tasks)")
     * @return mixed
     */
    public function tasksChangeWatcher(FieldAccess $property) {
        static $tasksCount = 0;
        $value = $property->proceed();
        $tasksCnt = count($value);
        if ($tasksCount != $tasksCnt) {
            $tasksCount = $tasksCnt;
            Logger::getInstance()->addInfo("Number of tasks in the queue: $tasksCount");
        }
        return $value;
    }

During changing the value of this property in the application source code, php raises a warning:

Notice: Indirect modification of overloaded property TaskManager::$tasks has no effect in ...TaskManager.class.php on line 80

So if an advice applied to the private/protected property which is an array, then main source code cannot change it indirectly.

Intercept function calls from the global namespace

According to the official documentation http://www.php.net/manual/en/language.namespaces.rules.php there is a name resolution rule:

Inside namespace (say A\B), calls to unqualified functions are resolved at run-time. Here is how a call to function foo() is resolved:

It looks for a function from the current namespace: A\B\foo().
It tries to find and call the global function foo().

This rule can be used by Go! library to dynamically intercept global function calls in the namespaces. For example, if we want to intercept a function file_get_contents() in the application, we can create this function in the current namespace, so it will be used instead of global function.

[Pointcut] "Not" expression allows only primitive pointcuts

There is a bug in the pointcut grammar, that restricts use of "negate" for complex pointcuts:

$this('Pointcut')
    ->is('Pointcut', '||', 'Pointcut')
    ->call(function($first, $_, $second) {
        return new OrPointcut($first, $second);
    })
// ...
    ->is('!', 'SinglePointcut')
    ->call(function($_, $first) {
        return new NotPointcut($first);
    })

    ->is('SinglePointcut');

Expression is('!', 'SinglePointcut') should be replaced with is('!', 'Pointcut'). Parse table should be also regenerated.

[Cache] Enable use of cache for debug mode (XDebug support)

To improve performance in development mode, need to use cache file, but this will break debugging with XDebug. This can be solved by using caching transformer that will be able to take transformed source from the cache. FilterInjector should be fixed too to trigger cache creation.

AOP is not working with ZendOptimizer+ in the debug mode

I'm trying to do a integration in a framework I'm bulding (Im using symfony HttpFoundation)

here is what I got so far

<?php

include __DIR__ . '/../vendor/lisachenko/go-aop-php/src/Go/Core/AspectKernel.php';

use Go\Core\AspectKernel;
use Go\Core\AspectContainer;

/**
 * Application Aspect Kernel
 */
class ApplicationAspectKernel extends AspectKernel
{

    /**
     * Configure an AspectContainer with advisors, aspects and pointcuts
     *
     * @param AspectContainer $container
     *
     * @return void
     */
    protected function configureAop(AspectContainer $container)
    {
        $container->registerAspect(new Nucleus\Cache\Caching());
    }
}

ApplicationAspectKernel::getInstance()->init(array(
    'appLoader' => __DIR__ . '/../vendor/autoload.php',
    // Configuration for autoload namespaces
    'autoloadPaths' => array(
        'Go' => __DIR__ . '/../vendor/lisachenko/go-aop-php/src/',
        'TokenReflection' => __DIR__ . '/../vendor/andrewsville/php-token-reflection/',
        'Doctrine\\Common' => __DIR__ . '/../vendor/doctrine/common/lib/',
        'Dissect' => __DIR__ . '/../vendor/jakubledl/dissect/src/'
    ),
    'includePaths' => array(
        __DIR__ . '/../vendor',
        __DIR__ . '/../src'
    )
));

$request = Symfony\Component\HttpFoundation\Request::createFromGlobals();

And a Aspect for testing

<?php 

namespace Nucleus\Cache;

use Go\Aop\Aspect;
use Go\Aop\Intercept\FieldAccess;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\After;
use Go\Lang\Annotation\Before;
use Go\Lang\Annotation\Around;
use Go\Lang\Annotation\Pointcut;

/**
 * Monitor aspect
 */
class Caching implements Aspect
{

    /**
     * Method that will be called before real method
     *
     * @param MethodInvocation $invocation Invocation
     * @Before("execution(public *->*(*))")
     */
    public function beforeMethodExecution(MethodInvocation $invocation)
    {
        $obj = $invocation->getThis();
        echo 'Calling Before Interceptor for method: ',
             is_object($obj) ? get_class($obj) : $obj,
             $invocation->getMethod()->isStatic() ? '::' : '->',
             $invocation->getMethod()->getName(),
             '()',
             ' with arguments: ',
             json_encode($invocation->getArguments()),
             "<br>\n";
    }
}

nothing is happening...

I check that the filter is properly registered using the stream_get_filters function;

array (size=13)
  0 => string 'convert.iconv.*' (length=15)
  1 => string 'string.rot13' (length=12)
  2 => string 'string.toupper' (length=14)
  3 => string 'string.tolower' (length=14)
  4 => string 'string.strip_tags' (length=17)
  5 => string 'convert.*' (length=9)
  6 => string 'consumed' (length=8)
  7 => string 'dechunk' (length=7)
  8 => string 'zlib.*' (length=6)
  9 => string 'bzip2.*' (length=7)
  10 => string 'mcrypt.*' (length=8)
  11 => string 'mdecrypt.*' (length=10)
  12 => string 'go.source.transforming.loader' (length=29)

And the path return by FilterInjectorTransformer::rewrite

php://filter/read=go.source.transforming.loader/resource=D:\nucleus\nucleus\web/../vendor/autoload.php

The filter function of SourceTransformingLoader is never call, probably whe it doesn't work...

Is there something I don't do correctly ? Or maybe a comflict with other filter and it never reach th filter that is registered ?

Scope error when static method is intercepted

There is a scope error: Cannot access property ComposerAutoloaderInitXXX::$loader when trying to intercept static method that uses access to the private fields. This happens due to scope rebinding for LSB support, however, private fields will be in another (parent) scope, so there is an error.

[Pointcut] Use case-sensitive matching for matchers

Seems, it's better to use case-sensitive matching, because sometimes case-insensitive matching can give unpredictable result.
For example, *Name() pattern will match setName() and setSurname() methods.

Unit tests

hi,

Go! look nice, but i have not found the unit tests. Where are the unit tests ?
Framework is stable ?

Thx.

Can not cache transformed source files in the filesystem

Currently, only on-fly weaving is performing. This reduce overall performance as static reflection will take the place on each request. Additionally, op-code accelerators can not cache dynamic includes and just simply ignore that classes.

However, it's not easy to put transformed classes into another directory, as this will break an existing application, because DIR, FILE constants will be inside cache directory instead of original application one. ReflectionClass->getFileName() is also become incorrect.

Constants can be easily resolved during the source transforming process.

Fix dependency for doctrine/common

Go! requires "doctrine/common": "~2.2", but doctrine use branch aliasing "branch-alias": {"dev-master": "2.4.x-dev"}, so the last updates from master will be installed instead of stable version:

Loading composer repositories with package information
Updating dependencies
  - Installing doctrine/lexer (v1.0)
    Loading from cache

  - Installing doctrine/annotations (v1.0)
    Loading from cache

  - Installing doctrine/collections (v1.0)
    Loading from cache

  - Installing doctrine/cache (v1.0)
    Loading from cache

  - Installing doctrine/inflector (v1.0)
    Loading from cache

  - Installing doctrine/common (dev-master 185e6d7)
    Cloning 185e6d76765fe37ddb1a94aad8f51d97c92b0649

Additionally, doctrine introduces subpackages for 2.4.x. So autoloading for library is not working now, because composer can not be used for internal libraries of Go!

Support multiple autoload paths for a single namespace

Composer supports providing an array of autoloading paths for a single namespace. In WeavingTransformer though, array_map is called for "realpath". That assumes all values are strings. It should be "array_map_recursive" type thing that at least goes one more array deep.

Stream filter already registered

С письменным английским тяжело, посему отпишу по-русски.

При создании модульных тестов для приложения наткнулся на забавный момент:
в setUp() всё отлично инициализируется, но только в первой итерации тестов. Как только внутри PHPUnit вызывается setUp для следующего теста, ловим исключение о зарегистрированном потоке.
Очевидно, что в tearDown() поток не освобождается.

Трейс исключения: http://www.everfall.com/paste/id.php?ui2dua77pkop

И ещё 1 момент: в Go\Core\AspectKernel неплохо было бы добавить метод resetInstance всё для того же тестирования с помощью PHPUnit (привет синглтонам)

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.