New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Receive the annotation on a Annotation pointcut #66

Closed
mpoiriert opened this Issue Jun 30, 2013 · 4 comments

Comments

3 participants
@mpoiriert

I would like to be able to configure annotation so behavior of the aspect is change base on the annotation parameters. To give a simple example with your Cacheable annotation:

@Cacheable(ttl=60)

From the invocation call I could access the annotation (how need to be determine) and I would be able to use the ttl value (time to live) to put a expiration on the cache.

@pravednik

This comment has been minimized.

Show comment
Hide comment
@pravednik

pravednik Jul 1, 2013

$container->registerAspect(new SomeAspect($container->get('aspect.annotation.reader')));

class SomeAspect {
public function __construct(Reader $annotationReader)
{
$this->annotationReader = $annotationReader;
}
}

$container->registerAspect(new SomeAspect($container->get('aspect.annotation.reader')));

class SomeAspect {
public function __construct(Reader $annotationReader)
{
$this->annotationReader = $annotationReader;
}
}

@lisachenko

This comment has been minimized.

Show comment
Hide comment
@lisachenko

lisachenko Jul 1, 2013

Member

Oh, cool ) Community for framework is the best source for FAQ and support )
@pravednik has already asked me earlier about this issue by email, so he has an answer:

AOP internally uses annotation reader from Doctrine that is available in the container. So you can pass it to an aspect constructor in the following way:

// SomeAspectKernel.php
public function configureAop($container)
{
    //...
    $container->registerAspect(new SomeAspect($container->get('aspect.annotation.reader')));
}

Then, in the concrete aspect just store this dependency in the class field:

class SomeAspect
{
    /**
     * Instance of annotation reader
     * @var Reader
     */ 
    protected $annotationReader = null;

    public function __construct(Reader $annotationReader)
    {
        $this->annotationReader = $annotationReader;
    }
}

Next step is to use Doctrine Annotations API to access a meta-information from reflection points:

// SomeAspect.php

/**
 * @Around("@annotation(Annotation\Cacheable)")
 * @param MethodInvocation $invocation Current joinpoint
 */
public function aroundCacheableAnnotattion(MethodInvocation $invocation)
{
    $cacheable = $this->annotationReader->getMethodAnnotation($invocation->getMethod(), 'Annotation\Cacheable');
    echo "Cached for {$cacheable->ttl}", PHP_EOL, "<br/>"
    // .. logic here
}
Member

lisachenko commented Jul 1, 2013

Oh, cool ) Community for framework is the best source for FAQ and support )
@pravednik has already asked me earlier about this issue by email, so he has an answer:

AOP internally uses annotation reader from Doctrine that is available in the container. So you can pass it to an aspect constructor in the following way:

// SomeAspectKernel.php
public function configureAop($container)
{
    //...
    $container->registerAspect(new SomeAspect($container->get('aspect.annotation.reader')));
}

Then, in the concrete aspect just store this dependency in the class field:

class SomeAspect
{
    /**
     * Instance of annotation reader
     * @var Reader
     */ 
    protected $annotationReader = null;

    public function __construct(Reader $annotationReader)
    {
        $this->annotationReader = $annotationReader;
    }
}

Next step is to use Doctrine Annotations API to access a meta-information from reflection points:

// SomeAspect.php

/**
 * @Around("@annotation(Annotation\Cacheable)")
 * @param MethodInvocation $invocation Current joinpoint
 */
public function aroundCacheableAnnotattion(MethodInvocation $invocation)
{
    $cacheable = $this->annotationReader->getMethodAnnotation($invocation->getMethod(), 'Annotation\Cacheable');
    echo "Cached for {$cacheable->ttl}", PHP_EOL, "<br/>"
    // .. logic here
}
@mpoiriert

This comment has been minimized.

Show comment
Hide comment
@mpoiriert

mpoiriert Jul 5, 2013

The solution is kind of working but I have some issues...

  1. It does complicate the code if we consider that it have been parse earlier, having the Annotation as a parameter or accessible via the method annotation will make it smaller and easier to develop.
  2. Aspect/Annotation are already heavier on the load so being sure that we don't need to parse twice something is better.
  3. What happen if you have 2 of the same annotations for the same method ? A simple (but not perfect) exemple would be

@NeededPermission("admin")
@NeededPermission("user")

Obviously this is not the best exemple but this is impossible to treat easily with the code mention above...

Would is be really hard to add the Annotation like I suggest ?

Thanks !

The solution is kind of working but I have some issues...

  1. It does complicate the code if we consider that it have been parse earlier, having the Annotation as a parameter or accessible via the method annotation will make it smaller and easier to develop.
  2. Aspect/Annotation are already heavier on the load so being sure that we don't need to parse twice something is better.
  3. What happen if you have 2 of the same annotations for the same method ? A simple (but not perfect) exemple would be

@NeededPermission("admin")
@NeededPermission("user")

Obviously this is not the best exemple but this is impossible to treat easily with the code mention above...

Would is be really hard to add the Annotation like I suggest ?

Thanks !

@lisachenko

This comment has been minimized.

Show comment
Hide comment
@lisachenko

lisachenko Jan 19, 2014

Member

Just added an experimental support (in the separate branch) for reading an annotations from MethodInvocation:

use Demo\Annotation\Cacheable;
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Around;

class CacheAspect implements Aspect
{
    /**
     * Cacheable methods
     *
     * @param MethodInvocation $invocation Invocation
     *
     * @Around("@annotation(Demo\Annotation\Cacheable)")
     */
    public function aroundCacheable(MethodInvocation $invocation)
    {
        /** @var Cacheable $cacheable */
        $cacheable = $invocation->getMethod()->getAnnotation(Cacheable::class);
        echo $cacheable->time;
        return $invocation->proceed();
    }
}

Original class:

use Demo\Annotation\Cacheable;

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

However, be aware, that annotation reader is not using cache now, so parsing is expensive and slow.

Member

lisachenko commented Jan 19, 2014

Just added an experimental support (in the separate branch) for reading an annotations from MethodInvocation:

use Demo\Annotation\Cacheable;
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Around;

class CacheAspect implements Aspect
{
    /**
     * Cacheable methods
     *
     * @param MethodInvocation $invocation Invocation
     *
     * @Around("@annotation(Demo\Annotation\Cacheable)")
     */
    public function aroundCacheable(MethodInvocation $invocation)
    {
        /** @var Cacheable $cacheable */
        $cacheable = $invocation->getMethod()->getAnnotation(Cacheable::class);
        echo $cacheable->time;
        return $invocation->proceed();
    }
}

Original class:

use Demo\Annotation\Cacheable;

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

However, be aware, that annotation reader is not using cache now, so parsing is expensive and slow.

genkiroid pushed a commit to genkiroid/framework that referenced this issue Jun 23, 2016

Merge pull request #66 from stefandoorn/master
Replace return with yield when docComments returns Generator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment