Doctrine is freaking out when I turn on php opcach

2019-07-01 13:47发布

问题:

I'm having a really strange problem with Doctrine and PHP 5.5.6 Opcache. Everything is working just fine when the opcache module is turned off. Once I turn it on, I begin getting the following exception:

Fatal error: Uncaught exception 'Doctrine\ORM\Mapping\MappingException' with message 'Class "Admin\Models\Users\Role" is not a valid entity or mapped super class.' in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php on line 336
( ! ) Doctrine\ORM\Mapping\MappingException: Class "Admin\Models\Users\Role" is not a valid entity or mapped super class. in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php on line 336
Call Stack
#   Time    Memory  Function    Location
1   0.0000  128016  {main}( )   ../app.php:0
2   0.0185  615020  Core\Bootstrap->handle( )   ../app.php:53
3   0.0210  695744  call_user_func_array ( )    ../Bootstrap.php:111
4   0.0210  695976  Admin\Controllers\DashboardController->indexAction( )   ../Bootstrap.php:111
5   0.0210  696028  Doctrine\ORM\EntityManager->getRepository( )    ../DashboardController.php:25
6   0.0210  696072  Doctrine\ORM\Repository\DefaultRepositoryFactory->getRepository( )  ../EntityManager.php:759
7   0.0210  696176  Doctrine\ORM\Repository\DefaultRepositoryFactory->createRepository( )   ../DefaultRepositoryFactory.php:50
8   0.0210  696200  Doctrine\ORM\EntityManager->getClassMetadata( ) ../DefaultRepositoryFactory.php:67
9   0.0210  696420  Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor( ) ../EntityManager.php:295
10  0.0213  699628  Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory->loadMetadata( )   ../AbstractClassMetadataFactory.php:211
11  0.0224  781128  Doctrine\ORM\Mapping\ClassMetadataFactory->doLoadMetadata( )    ../AbstractClassMetadataFactory.php:318
12  0.0224  782824  Doctrine\ORM\Mapping\Driver\AnnotationDriver->loadMetadataForClass( )   ../ClassMetadataFactory.php:117

I believe my entities are defined properly - as I mentioned in the beginning, once I disable opcache everything works as expected. To initialize Doctrine I'm using this piece of code:

// $this->getEntityPaths()   returns an array of existing absolute paths, each of which is checked with realpath().
// $this->getProxyPath()     returns a string with the absolute path, checked with realpath()
// $this->getInDevelopment() returns boolean and is set to TRUE


$config = Setup::createAnnotationMetadataConfiguration($this->getEntityPaths(), $this->getInDevelopment(), $this->getProxyPath());
$config->setAutoGenerateProxyClasses(true);

$dbParams = array(
    'driver'   => 'pdo_pgsql',
    'host'     => $this->getHost(),
    'port'     => $this->getPort(),
    'user'     => $this->getUser(),
    'password' => $this->getPass(),
    'dbname'   => $this->getName()
);

$this->db = EntityManager::create($dbParams, $config);

I've also tried this configuration:

$config = new Configuration;
$config->setProxyDir($this->getProxyPath());
$config->setProxyNamespace('DoctrineProxies');
$config->setAutoGenerateProxyClasses(true);

$driverImpl = $config->newDefaultAnnotationDriver($this->getEntityPaths());

$config->setMetadataDriverImpl($driverImpl);
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache());
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache());

// the connection configuration
$dbParams = array(
    'driver'   => 'pdo_pgsql',
    'host'     => $this->host,
    'port'     => $this->port,
    'user'     => $this->user,
    'password' => $this->pass,
    'dbname'   => $this->name
);

$this->db = EntityManager::create($dbParams, $config);

Here is the entity

/**
 * Roles
 *
 * @Table(name="roles")
 * @Entity
 */
class Role
{
    /**
     * @Id
     * @Column(name="role_id", type="integer", nullable=false)
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $role_id;


    /**
     * @Column(name="name", type="string", length=100, nullable=false)
     */
    private $name;
}

For the sake of simplicity I have AutoGenerateProxyClasses on, but just to be sure I've also tested it by generating the proxies. Unfortunately I get the same exception. I'm not sure how to pursue this problem and what exactly IS the problem. Is it a Doctrine bug? Why having OpCache turned on results in Doctrine exceptions and having it off works fine? Have I missed something in the configuration part or the entity?

回答1:

After a lot of debugging, I finally managed to understand why Doctrine was not working right with OpCache set on. Apparently by default opcache does not store or load any comments you have in your php file. As you can imagine, this results in tons of unexplained exceptions in any php library which relies on Annotations. So just go to your php.ini, change the lines below and you'll be as good as new. I guess this should go into the Doctrine help as well.

opcache.save_comments=1
opcache.load_comments=1