Closed AlexKovalevych closed 9 years ago
ok, after changing kernel parameters to
require_once __DIR__.'/../app/AppAspectKernel.php';
$applicationAspectKernel = AppAspectKernel::getInstance();
$applicationAspectKernel->init([
'debug' => true,
'cacheDir' => __DIR__ . '/../app/cache/aspect/',
]);
its another error:
2015/04/03 16:08:36 [error] 11226#0: *6752 FastCGI sent in stderr: "PHP message: PHP Warning: filemtime(): stat failed for php://filter/read=go.source.transforming.loader/resource=php://filter/read=go.source.transforming.loader/resource=php://filter/read=go.source.transforming.loader/resource=/var/www/vendor/symfony/symfony/src/Symfony/Component/Debug/Exception/ContextErrorException.php in /var/www/vendor/lisachenko/go-aop-php/src/Instrument/Transformer/CachingTransformer.php on line 108
Hello! Glad to see you here )
Could you please tell, which version of framework do you use? And next one advice is to try experimental branch feature/simple-transforming
that can give you better result (this will be merged for 1.0.0 soon)
Unfortuantely, old versions of framework can have glitches with symfony, because there are a lot of tricks in both frameworks. However, 1.0.0 version should be very lightweight and heavily use composer for better performance and significant simplification. I'm expecting that it will work nicely with symfony
@lisachenko, thank you for fast response. I use sf 2.6.4 version. I'd really like to use this library at out project, but can't make it work in any way.
Here is what i'm getting after switching to feature/simple-transforming
:
Fatal error: Class 'TokenReflection\Broker' not found in /var/www/vendor/lisachenko/go-aop-php/src/Core/AspectKernel.php on line 251
Call Stack
# Time Memory Function Location
1 0.0001 239216 {main}( ) ../app.php:0
2 0.0034 734096 Go\Core\AspectKernel->init( ) ../app.php:26
3 0.0054 804856 AppAspectKernel->configureAop( ) ../AspectKernel.php:116
4 0.0054 805128 spl_autoload_call ( ) ../AspectKernel.php:19
5 0.0054 805264 Go\Instrument\ClassLoading\AopComposerLoader->loadClass( ) ../AspectKernel.php:19
6 0.0061 824704 Go\Instrument\ClassLoading\SourceTransformingLoader->filter( ) ../AspectKernel.php:90
7 0.0065 837152 Go\Instrument\ClassLoading\SourceTransformingLoader->transformCode( ) ../SourceTransformingLoader.php:102
8 0.0065 837440 Go\Instrument\Transformer\CachingTransformer->transform( ) ../SourceTransformingLoader.php:146
9 0.0071 838088 Go\Instrument\Transformer\CachingTransformer->processTransformers( ) ../CachingTransformer.php:117
10 0.0071 838232 Go\Core\AspectKernel->Go\Core\{closure}( ) ../CachingTransformer.php:138
11 0.0072 843592 spl_autoload_call ( ) ../CachingTransformer.php:251
12 0.0072 843632 Go\Instrument\ClassLoading\AopComposerLoader->loadClass( ) ../CachingTransformer.php:251
13 0.0098 873672 Go\Instrument\ClassLoading\SourceTransformingLoader->filter( ) ../CachingTransformer.php:90
14 0.0099 875864 Go\Instrument\ClassLoading\SourceTransformingLoader->transformCode( ) ../SourceTransformingLoader.php:102
15 0.0099 876168 Go\Instrument\Transformer\CachingTransformer->transform( ) ../SourceTransformingLoader.php:146
16 0.0107 876672 Go\Instrument\Transformer\CachingTransformer->processTransformers( ) ../CachingTransformer.php:117
17 0.0107 876672 Go\Core\AspectKernel->Go\Core\{closure}( ) ../CachingTransformer.php:138
Seems like an issue with autoloader (in the aspect kernel?), class is there of course and available in the aspect kernel, outside the closure i mentioned above.
Ugh, weird trace... It starts to process AOP for internal classes, however it shouldn't do this. I need a time to make this working for symfony. Try to use includePaths
and excludePaths
options to include only src
directory and exclude vendor and cache from processing. Probably, this can help.
I'll check sf2 compatibility in nearest time
I have a question, did you load a composer autoloader before aspect initialization? I see direct
require_once __DIR__.'/../app/AppAspectKernel.php';
and no include '/../app/autoload.php'
or '/../vendor/autoload.php'
Autoloader is included in the app/bootstrap.php.cache
file which is loaded before all the kernels. Ok, i'll try to play with include/exclude options, maybe that will work somehow.
Posting here since my issue also relates to Symfony.
I can't get it to work in Symfony 2.6.6; no errors but no aspects work either. The loading code I have is near identical to what is outlined here.
I can see my test aspect being registered in AppAspectKernel->configureAop() and the aspect itself just has a single error_log("") call. There are no files in the configured cache directory that was created (I assume there should be).
I tried messing with the warmup script, but can't figure out what to do with it - all I keep getting is errors. I looked at its code and it seems like it wants the AppAspectKernel.php file, but that results in:
$./bin/warmup goaop:warmup /www/app/AppAspectKernel.php
Loading aspect kernel for warmup...
PHP Fatal error: Cannot instantiate abstract class Go\Core\AspectKernel in /www/vendor/lisachenko/go-aop-php/src/Core/AspectKernel.php on line 80
PHP Stack trace:
PHP 1. {main}() /www/vendor/lisachenko/go-aop-php/bin/warmup:0
PHP 2. Symfony\Component\Console\Application->run() /www/vendor/lisachenko/go-aop-php/bin/warmup:39
PHP 3. Symfony\Component\Console\Application->doRun() /www/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:126
PHP 4. Symfony\Component\Console\Application->doRunCommand() /www/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:195
PHP 5. Symfony\Component\Console\Command\Command->run() /www/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:874
PHP 6. Go\Console\Command\WarmupCommand->execute() /www/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:257
PHP 7. Go\Core\AspectKernel::getInstance() /www/vendor/lisachenko/go-aop-php/src/Console/Command/WarmupCommand.php:64
For reference, here is what I have configured:
In composer.json I have "lisachenko/go-aop-php: 1.0.*@dev"
app/AppAspectKernel.php
<?php
use Go\Core\AspectKernel;
use Go\Core\AspectContainer;
use Traklight\WebBundle\Aspect as TraklightAspect;
/**
* Application Aspect Kernel
*/
class AppAspectKernel extends AspectKernel
{
/**
* Configure an AspectContainer with advisors, aspects and pointcuts
*
* @param AspectContainer $container
*
* @return void
*/
protected function configureAop(AspectContainer $container)
{
$container->registerAspect(new TraklightAspect\TestAspect());
}
}
in web/app_dev.php
require_once __DIR__.'/../app/AppAspectKernel.php';
$applicationAspectKernel = AppAspectKernel::getInstance();
$applicationAspectKernel->init(array(
'debug' => true,
'cacheDir' => __DIR__."/../var/cache/$environment/go_aop",
'appDir' => __DIR__."/../app/",
'includePaths' => [__DIR__.'/../src/']
));
As noted, there is the following line before Symfony's AppKernel is loaded:
$loader = require_once __DIR__.'/../var/bootstrap.php.cache';
This goes through a winding tour to eventually get to the autoloader.php
TestAspect.php
<?php
namespace Traklight\WebBundle\Aspect;
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;
/**
*
*/
class TestAspect implements Aspect
{
public function __construct()
{
error_log("FFFFFFF");
}
/**
* Method that will be called before real method
*
* @param MethodInvocation $invocation Invocation
*
* @Before("within(Traklight\WebBundle\Controller\Ecommerce\*")
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
error_log("WOOOOO");
}
}
I can see this class being created via the error_log in the contructor. I tried all sorts of configurations to get beforeMethodExecution() to run based on examples from https://github.com/lisachenko/go-aop-php/blob/master/tests/Go/Aop/Pointcut/PointcutParserTest.php#L55
The class and method I was targeting is in a controller: Traklight\WebBundle\Controller\Ecommerce\CheckoutController->reviewAction(Request $request)
Hi, I have the same problem. With the command-line warmup script it always result in:
Cannot instantiate abstract class Go\Core\AspectKernel in
The command line was:
php -f vendor/bin/warmup goaop:warmup module/Api/src/Aspect/ApplicationAspectKernel.php
The AspectKernel has no Instance and is trying to create itself. Greetings Jan
Hello @jg-development, and @kellewic sorry for the late response.
I'm working now on extension for phpStorm for upcoming version 1.0.0, so was a little busy. As I said before, Symfony and Go! AOP framework integration can be cumbersome, because of tricks in both frameworks. As a result, I decided to stabilize it with SF2 after 1.0 will be ready, as I know, no issues with other frameworks like Laravel, ZF2, Yii, FLOW3, etc
However, I'm looking forward to version 1.0.0 which should work more transparent. To use cache warmer, you should specify a file that will prepare a kernel, not the kernel class definition itself, so for @kellewic example, cache warmer should get a path to the web/app_dev.php
file where kernel is initialized. Could you please try it with that file?
I also thinking about the configuration of paths and misc options with composer extra section - is this way more suitable for you?
I tried it with web/app_dev.php but it seems I have to give it a full path or it won't read the file. So I gave it /www/web/app_dev.php and got errors related to $_SERVER['HTTP_X'] variables I use since they don't exist outside the web context (undefined index).
For Symfony, a better solution is probably a bundle. Could then set those configurations inside the app/config/config.yml (or XML) files and create the AOP kernel instead of needing the AppAspectKernel.php. Not sure where in Symfony this could happen, it would have to be early - maybe as a compiler pass? I don't know enough about that aspect of Symfony yet.
No worries from me on putting this off until you have time. I am posting here to provide as much information as I can to assist in getting it to work. I'll be poking around the code to figure out how it works and see if I can come back with additional information. I already have plans for Go AOP in my project so it must work with Symfony :)
More information...
I got it to work to where the FilterInjectorTransformer returns:
php://filter/read=go.source.transforming.loader/resource=/path/to/MyClass.php
I can see the MyClass.php contents from Instrument\ClassLoading\AopComposerLoader::loadClass() and the call to 'require' there at the end, but MyClass is unchanged - I checked by putting a file_put_contents after the FilterInjectorTransformer::rewrite() call.
EDIT: Dug a bit deeper and see the only transformer processing on MyClass is Go\Instrument\Transformer\CachingTransformer
The problem with putting this in app_dev.php is the Debug::enable() call. Symfony uses this to switch from the normal ClassLoader to Symfony\Component\Debug\DebugClassLoader which unregisters all current autoloaders and then wraps them in a new instance of itself and does not subclass from ClassLoader, which is what AopComposerLoader is looking for to wrap them.
It was a long winding road, but I finally got it to work. It does not play well at all with the Debug::enable() call from Symfony in the app_dev.php file. Even after disabling that, I had to trace the AOP code down to the actual transforms to make sure my pointcut syntax was correct - it was, but I had to put the namespace in as well; I might have followed the example too closely and didn't realize the namespace was needed. So it ended up as:
@Before("execution(public Traklight\WebBundle\Controller\Ecommerce\CheckoutController->reviewAction(*))")
One odd thing I noticed is that if I clear the Symfony cache (so there is no Go AOP cache directory yet) and then load my app, the Go AOP cache directory is created, but none of my source files are parsed and saved. If I then reload my app, it all works as expected and I see the cached output of my files.
Could the above be due to how the cache is determined when no cache file exists? I saw that it uses the HTTP request time (if I recall) as the cache file mod time when no cache file exists - could this throw it off in the above scenario?
If I run the cache warmer first it works just fine. I do get the following error at the end of the warmer run though:
HP Fatal error: Class 'PHPUnit_Framework_TestCase' not found in /www/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Test/KernelTestCase.php on line 23
I don't have PHP Unit installed on my staging or production servers.
Here is my setup that is working:
_web/app_dev.php_ (note this is not the entire file)
if (isset($_SERVER['HTTP_HOST']) || isset($_SERVER['REMOTE_ADDR']))
{
... more code not applicable to Go AOP ...
}
//Debug::enable();
$environment = 'dev';
require_once __DIR__.'/../app/AppAspectKernel.php';
$applicationAspectKernel = AppAspectKernel::getInstance();
$applicationAspectKernel->init(array(
'debug' => true,
'cacheDir' => __DIR__."/../var/cache/$environment/go_aop",
'appDir' => __DIR__."/../src/",
));
Debug::enable() must be commented out or nothing will work.
If you use any $_SERVER[] variables in app_dev.php, you will need to do something like I did and wrap them in an if{ isset() } since they won't be set when you run the Go AOP cache warmer.
I set 'appDir' to where I have my application code since Go AOP won't process any files outside that "root" directory - this may be what the original poster is running into. If you're wanting to monkey patch vendor code, this won't work and you'll have to mess with the excludes and includes.
_app/AppAspectKernel.php_
<?php
use Go\Core\AspectKernel;
use Go\Core\AspectContainer;
use Traklight\WebBundle\Aspect as TraklightAspect;
/**
* Application Aspect Kernel
*/
class AppAspectKernel extends AspectKernel
{
/**
* Configure an AspectContainer with advisors, aspects and pointcuts
*
* @param AspectContainer $container
*
* @return void
*/
protected function configureAop(AspectContainer $container)
{
$container->registerAspect(new TraklightAspect\TestAspect());
}
}
_src/Traklight/WebBundle/Aspect/TestAspect.php_
<?php
namespace Traklight\WebBundle\Aspect;
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;
/**
*
*/
class TestAspect implements Aspect
{
/**
* Method that will be called before real method
*
* @param MethodInvocation $invocation Invocation
*
* @Before("execution(public Traklight\WebBundle\Controller\Ecommerce\CheckoutController->reviewAction(*))")
*/
public function beforeMethodExecution(MethodInvocation $invocation)
{
error_log("WOOOOO");
}
}
When I run my CheckoutController->reviewAction(), I get the error log output as expected.
Hopefully this helps the others out as well.
Hi, with a prepared file, it works like a charm ... thanks. From my side, ticket can be closed. Greetings Jan
Congratulations for you! I'm happy that you managed to run it with Symfony, however, it wasn't an easy task due to DebugClassLoader, bootstrap files and many other things.
I want to keep this task open for a while until I have a working from the box solution for SF2 framework. I'm thinking about the possible bundle for SF2.
A bundle would be nice especially if it handled the cache warming on a Symfony clear cache command. Being able to specify Go AOP config in Symfony's configs would be awesome, but not sure how to do that since Symfony needs to process those before Go AOP might have processed the files (a cache warm might help this) - not sure what the earliest kernel event is in Symfony; maybe hook into it with a high priority?
I was playing with initializing Go AOP inside of app/AppKernel.php instead of app_dev.php and app.php by using the constructor and just calling parent::__construct at the end of it to hook into Symfony init. Running into problems and ran out of time this week to keep looking at it. Wouldn't need the app/AppAspectKernel.php either if hooked into AppKernel.php.
I've been working on integrating goaop w/ sf2 recently as well, and came up with this solution, which is still a bit thorny (especially for cache warming)
Was able to accomplish this with an compiler pass:
// AspectCompilerPass.php
/**
* Lets us add Aspect classes to the AspectKernel by tagging them with `aspect`
*
* example:
*
* ```
* <service id="my.aspect"
* class="ExampleBundle\Aspect\MyAspect">
* <argument type="service" id="my.important.depenency" />
* <tag name="aspect"/>
* </service>
* ```
*
* @see src/ExampleBundle/Resources/config/services/aspects.xml
*
*/
class AspectCompilerPass implements CompilerPassInterface
{
/**
* {@inheritDoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has('aspect_container')) {
return;
}
$definition = $container->findDefinition(
'aspect_container'
);
$taggedServices = $container->findTaggedServiceIds(
'aspect'
);
foreach ($taggedServices as $id => $tags) {
$definition->addMethodCall(
'registerAspect',
array(new Reference($id))
);
}
}
}
Register the AspectKernel and Container as services
<!-- aspects.xml -->
<service id="my.aspect"
class="ExampleBundle\Aspect\MyAspect">
<argument type="service" id="my.important.depenency" />
<tag name="aspect"/>
</service>
<service id="aspect_kernel" class="ExampleBundle\Aspect\AspectKernel">
<factory class="ExampleBundle\Aspect\AspectKernel" method="getInstance" />
<call method="init">
<argument type="collection">
<argument key="debug">%kernel.debug%</argument>
<argument key="cacheDir">%kernel.cache_dir%/aspect</argument>
<argument key="appDir">%kernel.root_dir%/../</argument>
<argument type="collection" key="includePaths">
<argument>%kernel.root_dir%/../vendor/somevendorpath</argument>
</argument>
</argument>
</call>
</service>
<service id="aspect_container" class="Go\Core\GoAspectContainer">
<factory service="aspect_kernel" method="getContainer" />
</service>
Now we need to make sure that we're initializing the kernel early, the bundles boot method is an early point
// ExampleBundle
public function boot()
{
// We need to early initialize the AspectKernel (via DI when grabbing the AspectContainer)
try {
// If this resource ID exists, then the AspectKernel is already booted
SourceTransformingLoader::getId();
} catch (\RuntimeException $e) {
// If not, it chucks this Exception, and we can safely boot the AspectKernel
$this->container->get('aspect_container');
}
}
While this is not idea, it's one way to get sf2 dependencies into the aspect code.
Again, I haven't figured out how to wrap goaop cache warmup into a sf2-compatible warmer, namely because the warmer boots the bundle, and that initializes the AspectKernel with (at this time) the wrong cachedir (the one that's about to be deleted). I would monkey with the cachedir, but it's stored in a constant, making it untouchable.
A bundle would likely be a better solution :)
Ok, I want to publish an initial version of symfony bundle soon at goaop/symfony-bundle. However, there are still a lot of tricks and issues to solve:
Fatal error: Class 'PHPUnit_Framework_TestCase'
during warming up for standard application). This is because there is a mess with class autoloading and inheritance analysis in the framework. Workaround is needed.TokenReflection\Dummy\ReflectionClass
can be given to the AdviceMatcher, however, this should not be possible). Need to investigate, why cache warmer can affect this code.FilterTransformerInjector
replaces includes even in the prepared proxies. I'll put it there: https://github.com/goaop/goaop-symfony-bundle (just first draft, but working)
AppAspectKernel.php
:loading kernel in the
web/app.php
:the aspect itself:
getting error:
i figured out that it can't find that class only in the closure here https://github.com/lisachenko/go-aop-php/blob/master/src/Core/AspectKernel.php#L243-L260. Outside the closure it is able to find that class. Any ideas? (PHP 5.5.20)