schmittjoh / JMSDiExtraBundle

Provides Advanced Dependency Injection Features for Symfony2
http://jmsyst.com/bundles/JMSDiExtraBundle
330 stars 129 forks source link

[2.x] Cannot redeclare class #96

Open swis opened 11 years ago

swis commented 11 years ago

I just added some ACL stuff to my project and now, when clearing the cache with

php app/console cache:clear --env=dev_frontend

immediately I got the following error:

PHP Fatal error:  Cannot redeclare class EnhancedProxy_bc38afde4093145355d8d5cbefd3edaa1f79fdce\__CG__\Acme\Bundle\BlogBundle\Controller\BackendController in /var/www/myProject/app/cache/dev_fronten_/jms_diextra/proxies/Acme-Bundle-BlogBundle-Controller-BackendController.php on line 55
PHP Stack trace:
PHP   1. {main}() /var/www/myProject/app/console:0
PHP   2. Symfony\Component\Console\Application->run() /var/www/myProject/app/console:22
PHP   3. Symfony\Bundle\FrameworkBundle\Console\Application->doRun() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:106
PHP   4. Symfony\Component\Console\Application->doRun() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Console/Application.php:78
PHP   5. Symfony\Component\Console\Command\Command->run() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Component/Console/Application.php:193
PHP   6. Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand->execute() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Component/Console/Command/Command.php:240
PHP   7. Symfony\Bundle\FrameworkBundle\Command\CacheClearCommand->warmup() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php:83
PHP   8. Symfony\Component\HttpKernel\Kernel->boot() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/CacheClearCommand.php:110
PHP   9. Symfony\Component\HttpKernel\Kernel->initializeContainer() /var/www/myProject/app/bootstrap.php.cache:378
PHP  10. Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate->warmUp() /var/www/myProject/app/bootstrap.php.cache:601
PHP  11. JMS\DiExtraBundle\HttpKernel\ControllerInjectorsWarmer->warmUp() /var/www/myProject/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php:47
PHP  12. JMS\DiExtraBundle\HttpKernel\ControllerResolver->createInjector() /var/www/myProject/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/HttpKernel/ControllerInjectorsWarmer.php:37 
PHP  13. JMS\DiExtraBundle\HttpKernel\ControllerResolver->prepareContainer() /var/www/myProject/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/HttpKernel/ControllerResolver.php:98
PHP  14. JMS\DiExtraBundle\Generator\DefinitionInjectorGenerator->generate() /var/www/myProject/vendor/jms/di-extra-bundle/JMS/DiExtraBundle/HttpKernel/ControllerResolver.php:157

I already tried different versions of Symfony and DiExtraBundle (and different combinations), but the error remains even when I downgrade Symfony to 2.1. I don't know, but I suppose, this is somehow related to #95 - especially because of this CacheWarmer issue in the Stack. But I'm pretty sure, to not compile any Controller manually. Here's my current composer.json:

{
    "name": "symfony/framework-standard-edition",
    "description": "The \"Symfony Standard Edition\" distribution",
    "autoload": {
        "psr-0": { "": "src/" }
    },
    "require": {
        "php": ">=5.3.3",
        "symfony/symfony": "2.2.*",
        "doctrine/orm": "2.3.*",
        "doctrine/doctrine-bundle": "*",
        "twig/extensions": "1.0.*",
        "symfony/assetic-bundle": "2.1.*",
        "symfony/swiftmailer-bundle": "2.2.*",
        "symfony/monolog-bundle": "2.2.*",
        "sensio/distribution-bundle": "2.2.*",
        "sensio/framework-extra-bundle": "2.2.*",
        "sensio/generator-bundle": "2.2.*",
        "jms/security-extra-bundle": "dev-master",

        "mopa/bootstrap-bundle": "dev-master",
        "twitter/bootstrap": "v2.2.2",

        "sonata-project/block-bundle": "*",

        "dannytrue/phpthumb": "dev-master",
        "stof/doctrine-extensions-bundle": "dev-master",
        "doctrine/doctrine-fixtures-bundle": "dev-master",
        "doctrine/doctrine-migrations-bundle": "dev-master",
        "friendsofsymfony/user-bundle": "dev-master",
        "knplabs/knp-menu-bundle":"dev-master",
        "knplabs/knp-paginator-bundle": "dev-master",
        "stfalcon/tinymce-bundle": "dev-master"
    },
    "scripts": {
        "post-install-cmd": [
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Acme\\Bundle\\LogicBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Mopa\\Bundle\\BootstrapBundle\\Composer\\ScriptHandler::postInstallSymlinkTwitterBootstrap"
        ],
        "post-update-cmd": [
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
            "Acme\\Bundle\\LogicBundle\\Composer\\ScriptHandler::clearCache",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
            "Mopa\\Bundle\\BootstrapBundle\\Composer\\ScriptHandler::postInstallSymlinkTwitterBootstrap"
        ]
    },
    "minimum-stability": "dev",
    "extra": {
        "symfony-app-dir": "app",
        "symfony-web-dir": "web",
        "symfony-assets-install": "symlink"
    }
}

I'm looking for a workaround, since clearing the cache manually by removing the files is not appropriate in my case (especially for the CI stuff).

It may be of note that I'm using @SecureParam as well as @Secure annotations (the error occurs even if I use only one of them in the BackendController).

craue commented 11 years ago

I guess it's related to symfony/symfony#7360.

dkorsak commented 11 years ago

+1

schmittjoh commented 11 years ago

Could someone reproduce this on a fresh fork of the symfony-standard edition version 2.1?

dkorsak commented 11 years ago

When I have controller action with @secure annotation and I'm trying to clear cache for prod env (cache folder is empty - no files inside) this exception appears.

craue commented 11 years ago

I just tried upgrading again, from Symfony 2.1.8 to 2.1.9, but this error is still present using

Would be nice to have this fixed soon.

fesja commented 11 years ago

same here. It was giving an error since several days ago. I've added "minimum-stability": "dev" and now works. So maybe it's fixed.

symfony (2.1.9)
jms/di-extra-bundle (1.1.1)
jms/security-extra-bundle (1.2.0)
craue commented 11 years ago

I tried again using the following sets with Symfony 2.1.9:

 - jms/aop-bundle (1.0.0)
 - jms/cg (1.0.0)
 - jms/di-extra-bundle (1.1.1)
 - jms/metadata (1.1.1)
 - jms/security-extra-bundle (1.2.0)
 - jms/aop-bundle (dev-master 7018357)
 - jms/cg (dev-master 75a519d)
 - jms/di-extra-bundle (dev-master a153677)
 - jms/metadata (dev-master f2ab788)
 - jms/parser-lib (dev-master 4d469a7)
 - jms/security-extra-bundle (dev-master f2a345d)

Both don't work.

fesja commented 11 years ago

Christian, are you using Twig? I've just found out that if I do:

php app/console cache:clear --env=prod

if I have loaded Twig in AppKernel I have this error:

PHP Fatal error:  Allowed memory size of 536870912 bytes exhausted (tried to allocate 130968 bytes) in /vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Compiler/AnalyzeServiceReferencesPass.php on line 97

if I haven't loaded Twig then I have the redeclare error:

PHP Fatal error:  Cannot redeclare class
EnhancedProxy_5f83e70703f016ed810ebb73fecd0ecb3bdf2351\__CG__\TE\ApiBundle\Controller\FlagController in .../app/cache/pro_/jms_diextra/proxies/TE-ApiBundle-Controller-FlagController.php on line 46

Totally blocked right now :S.

craue commented 11 years ago

@fesja: Yes, I'm using Twig. But I don't think that it has to do with this issue directly. I have no idea which component is causing it, though.

@schmittjoh: Can you reproduce it?

vicb commented 11 years ago

@craue you'll see the root cause of this bug in the PR link above. I think the fix is on Sf2 side.

marcoleong commented 11 years ago

It happens in my case is that if I do:

...
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;

use Symfony\Component\HttpFoundation\Request;

use JMS\SecurityExtraBundle\Annotation\Secure;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

/**
 *
 */
class OrganizationController extends Controller
{
    /**
     * @Route("/organizations/new")
     * @Method({"GET","POST"})
     * @Secure(roles="ROLE_ORG_ADMIN")
     */
    public function newAction(Request $request)
    {

instead of

...
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;

use Symfony\Component\HttpFoundation\Request;

use JMS\SecurityExtraBundle\Annotation\Secure;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
 *
 */
class OrganizationController extends Controller
{
    /**
     * @Route("/organizations/new")
     * @Method({"GET","POST"})
     * @Secure(roles="ROLE_ORG_ADMIN")
     */
    public function newAction(Request $request)
    {

It works !

See the different ? there is an extra return between this line

use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

and the comment block

/**
 *
 */
class OrganizationController extends Controller
craue commented 11 years ago

In my case, @PreAuthorize seems to cause this issue. If I remove them from all controllers, the error doesn't show up.

craue commented 11 years ago

@schmittjoh: Here's how to trigger this error with a clean 2.1.9 standard edition: In src/Acme/DemoBundle/Controller/SecuredController.php, change

/**
 * @Route("/hello/admin/{name}", name="_demo_secured_hello_admin")
 * @Secure(roles="ROLE_ADMIN")
 * @Template()
 */
public function helloadminAction($name)
{
    return array('name' => $name);
}

from

public function helloadminAction($name)
{

to

public function helloadminAction($name) {

and try clearing the cache with php app\console ca:cl.

alanbem commented 11 years ago

What is the status of this ticket?

ihortymoshenko commented 11 years ago

Any updates to this?

respinoza commented 11 years ago

I just stumbled into this bug when upgrading to 2.3 but then even downgrading to 2.2 results in the same error. No fix for this yet?

respinoza commented 11 years ago

I discovered if I remove the cache folder manually (rm -Rf) and then visit a URL with a controller using a JMS bundle (in this case, the JMS Security for a @Secure annotation) everything works as expected, in this case, if I go and do a cache:clear I get no errors (with or without warmup).

A bit annoying but at least that works.

i4got10 commented 11 years ago

+1 Meet error on tests at first run after clearing the cache. It`s a Heisenbug, possible that it rise only with >= 2 environments at empty cache. Trying to reproduce it on fresh fork.

Yw0ke commented 11 years ago

+1 here, Exact same error when trying to cache:clear. I've upgraded from 2.2 to 2.3 too.

emgiezet commented 11 years ago

+1

robocoder commented 11 years ago

MacOS without APC:

git clone git@github.com:symfony/symfony-standard.git
cd symfony-standard
git checkout v2.2.3
composer.phar install
app/console cache:clear -e test

Running cache:clear a second time works unless cache doesn't exist, e.g.,

rm -rf app/cache/*
app/console cache:clear -e test
tomat commented 11 years ago

This code seems related (in JMS\DiExtraBundle\HttpKernel\ControllerInjectorsWarmer):

public function warmUp($cacheDir)
{
    // This avoids class-being-declared twice errors when the cache:clear
    // command is called. The controllers are not pre-generated in that case.
    $suffix = defined('Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate::NEW_CACHE_FOLDER_SUFFIX')
        ? CacheWarmerAggregate::NEW_CACHE_FOLDER_SUFFIX
        : '_new';

    if (basename($cacheDir) === $this->kernel->getEnvironment().$suffix) {
        return;
    }

    $classes = $this->findControllerClasses();
    foreach ($classes as $class) {
        $this->controllerResolver->createInjector($class);
    }
}

This probably needs to be adjusted to detect the 2.3 way of naming the temporary cache directory (dev becomes de_ instead of dev_new).

EDIT: Tested and working with this replacement code:

public function warmUp($cacheDir)
{
    // This avoids class-being-declared twice errors when the cache:clear
    // command is called. The controllers are not pre-generated in that case.
    if (basename($cacheDir) === preg_replace('#.$#', '_', $this->kernel->getEnvironment())) {
        return;
    }

    $classes = $this->findControllerClasses();
    foreach ($classes as $class) {
        $this->controllerResolver->createInjector($class);
    }
}
swis commented 11 years ago

Unfortunately, the workaround from @tomat doesn't work for me in symfony 2.3 :/

tomat commented 11 years ago

Yeah, just noticed that this bug was older than Symfony 2.3, but my PR will solve it for some users at least.

@swis: Do you have an updated composer.json for your project? I'm getting "could not be resolved to an installable set of packages" on the one you posted 4 months ago.

verschoof commented 11 years ago

Same problem found in symfony 2.1.11.

PHP Fatal error: Cannot redeclare class EnhancedProxyed2f3cb92db5d961490e60a593d20b156b460b0dCG\MV\CaravanBundle\Controller\AdminLogController in app/cache/pro/jms_diextra/proxies/MV-CaravanBundle-Controller-AdminLogController.php on line 37

swis commented 11 years ago

@tomat: Here you go: https://gist.github.com/swis/5941281 (Although it's in the file: Obviously it's not the symfony standard edition)

EDIT: I'm going to try the commits of @robocoder asap in my setup at sf2.3

EDIT2: I just saw, that the contributed composer.json may be kinda useless, since the is no version information. The installed versions are:

jms/aop-bundle: 1.0.0,
jms/cg: 1.0.0,
jms/di-extra-bundle: 1.4.0,
jms/i18n-routing-bundle: 1.1.0,
jms/metadata: 1.3.0,
jms/parser-lib: 1.0.0,
jms/security-extra-bundle: 1.5.1,
jms/serializer: 0.12.0,
jms/serializer-bundle: dev-master (66d2fe197bacb82f7b476099144f03596719b24d),
jms/translation-bundle: 1.1.0
Maxwell2022 commented 11 years ago

The fix from @tomat is working for me symfony 2.2.3

I'm using:

    "jms/security-extra-bundle": "1.5.*",
    "jms/di-extra-bundle": "1.4.*",
swis commented 11 years ago

Unfortunately, 3daedc9 by @robocoder still does not fix the error for my forementioned setup: Cannot redeclare class EnhancedProxy_73b88e6bf5989f9e4785cea753cad509a15376f1\__CG__\JMS\SecurityExtraBundle\Tests\Functional\TestBundle\Controller\PostController in /my/project/app/cache/backend_test/jms_diextra/lookup_method_classes/JMS-SecurityExtraBundle-Tests-Functional-TestBundle-Controller-PostController.php on line 11

But 22e8d41 seems to fix the issue .. I couldn't reproduce the error so far. I will give some further feedback in a few days.

Edit: GNAH! 22e8d41 doesn't fix the issue.

swis commented 11 years ago

Okay guys, I'll leave here.

Since we're planning to go live in August, I have no time to invest so much (more) time in this issue. I've written my own @Secure annotation (this is the only one I need from the bundle). If someone is interested in the code, please write me a message.

robocoder commented 11 years ago

Don't use 22e8d41 ... It's rubbish. I'm not surprised 3daedc9 doesn't work for some since it only guards the kernel loading twice, ie there's probably another edge case.

schmittjoh commented 11 years ago

Given the fragility of the cache warming system, I think the only viable solution moving forward is to convert the current cache warmer (HttpFoundation\ControllerCacheWarmer) class to a separate command which needs to be invoked manually during deployment.

If someone has time to work on this (it should be fairly simple), please let me know or better yet, send a PR right away :)

i4got10 commented 11 years ago

Reproduce this bug on clear symfony-standard. @schmittjoh Please try it, maybe it will help you detect problem

    "jms/security-extra-bundle": "1.5.*",
    "jms/di-extra-bundle": "1.4.*"

https://github.com/i4got10/symfony-standard/blob/issue96/src/Acme/DemoBundle/Tests/Controller/BugTest.php

clone https://github.com/i4got10/symfony-standard.git && checkout issue96 run rm -r app/cache && phpunit -c app

AlmogBaku commented 11 years ago

Its seems that re-enabling the APC for the PHP-CLI fixes the problem for me... wired :|

tomat commented 11 years ago

apc.enable_cli=on/off gives the same result here.

@i4got10: Successfully reproduced and fixed, not sure if this breaks other stuff though, so please test it before I make a PR.

Fix is in https://github.com/tomat/JMSDiExtraBundle/commit/59eafd96707873d8001e01a97eadd8c66ca38bcc

Also forked @i4got10:s symfony-standard so that it uses my fix: https://github.com/tomat/symfony-standard/tree/patch-1 (git clone, git checkout patch-1, composer install, rm -r app/cache && phpunit -c app)

Edit: Forgot to update composer.lock ;-)

i4got10 commented 11 years ago

@tomat nice one. Solve my problem with 2 test environments. Also work with

    "jms/security-extra-bundle": "1.4.*",
    "jms/di-extra-bundle": "1.3.*",
tkleinhakisa commented 10 years ago

Hi everyone, another guy annoyed by this "cannot redeclare class" error here !

I tryied the differents workaround and patches here but none fully solved the problem so I digged into sf and jms code to try to understand what happens and i think i found the root but no fix for now ...

  1. At first everything works fine, the cache is warm and i can do a cache:clear without error
  2. Then i make a change to a controller using security-extra or aop or anything that makes jms creates a proxy for that controller in the _jmsdiextra cache folder
  3. Then i run cache:clear and i have the "cannot redeclare class" error
  4. The i run cache:clear and everything works fine

Following the workflow of the cache:clear command, here is what happens in case 2:

Here we go ! The class is loaded for the first time

The cache is then cleared and warmed up again by the command but in a temporary directory (de_ for dev) so when the DefinitionInjectorGenerator make a require_once again, it is not the same file so it is evaluated, but the class inside is the same and so the error is raised.

In the ControllerInjectorWarmer there is a condition based on the directory beiing warmed to determine if the warming up is actualy done. There is a comment in the code saying :

This avoids class-being-declared twice errors when the cache:clear command is called. The controllers are not pre-generated in that case.

In the case of the cache:clear command as described above, the warming up is done at first on the dev folder and then on the de_ folder (which will be renamed as dev later)

So we can prevent the first warmup with a condition like

if (basename($cacheDir) !== basename(substr($this->kernel->getCacheDir(), 0, -1).'_'))

the error goes away and the proxy files are created in dev

But another issue raises !! If you reload a page in your browser after modifying your controller, the cache is not updated anymore because in that case the warmup is done on the dev folder, which was excluded with the previous condition

So in one case we want warmup to be done for de_ and not dev, in the other case we want it to be done for dev

The "cannot redeclare class" error can also happen in a different situtation : when you reload a page from a controller using jms capabilities (when the controller is declared as a service)

  1. At first everything works fine
  2. Make a change in your controller
  3. Reload the page => "cannot redeclare class" error
  4. Reload the page => it's fine

The same pattern applies here:

I think one solution would be to provide an autoloader for all the jms-generated classes (i think doctrine does this for the entity proxies) and then avoid the problematic require statements.

Any tought or help on this one would be greatly appreciated as it is really messing in the developpement workflow (even if non blocking for production)

ftassi commented 10 years ago

Seems to be solved by #117 on Symfony 2.3.4 (2.3.x I guess)

tkleinhakisa commented 10 years ago

It fixes the probleme with the cache:clear command but not when reloading a page from browser (after modifying its controller)

tkleinhakisa commented 10 years ago

In fact it does not solve the problem as the warmup is not done anymore during the command, the injectors are not created !

Maxwell2022 commented 10 years ago

Upgrading Symfony to 2.3.4 I have the same issue again:

Cannot redeclare class EnhancedProxy_...

sharpner commented 10 years ago

+1

svscorp commented 10 years ago

+1

omissis commented 10 years ago

+1

i4got10 commented 10 years ago

Check out this PR, was very helpful for me

sebastianreloaded commented 10 years ago

+1

tkleinhakisa commented 10 years ago

@i4got10 Could not test #131 with the cache:clear command as it is broken in my project now but it does not solve the issue when refreshing a page

@vkhramtsov : same thing

linaori commented 10 years ago

I'm having this issue as well with Symfony 2.3. Could it be as I use my controller as a service?

CristianSitov commented 10 years ago

The bad part with this bug is that

1) there's no fix for last 3 subversions / last 8 months 2) most of the JMS bundles that use DI/AOP/CG, like Translation, a.s.o. are useless; if I take them out everything works smooth 3) does not work on neither Symf 2.1/2.2 or any other combination of versions.

After more than 12 hours I stumbled on same issues as listed before. I see no option now than leaving all these bundles behind and probably get back and contribute with ideas if by that time it won't be fixed.

linaori commented 10 years ago

I was already wondering why I'd need 3 bundles just to be able to add an @Secure to my action... Some other things I've noticed while developing with this bundle:

tkleinhakisa commented 10 years ago

@CristianSitov in our project the bundles causes issues in dev environnement and cache clearing but it has no impact in our production, everything works fine and as expected, maybe you don't have to leave the bundles ...

@iltar That's called decoupling ! yes you need several bundle for the @secure annotation but you may not need the whole security bundle if you're only looking at AOP capabilities, etc ... the "hard" refresh or "smooth" refresh makes no difference server side (it's only a matter of browser caching)