diff --git a/.gitignore b/.gitignore index 987e2a2..cd26482 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,10 @@ +# System, IDE, ... +.DS_Store +.idea +.vagrant +.bundle + +# Composer +composer.phar composer.lock vendor diff --git a/README.md b/README.md index 3479ada..92a40ab 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,9 @@ Configuration String or array describing result cache implementation. * **hydration_cache** (Default: setting specified by orm.default_cache): String or array describing hydration cache implementation. - * **types** + * **cache_namespace**: + String to define a global namespace for cached keys. + * **types**: An array of custom types in the format of 'typeName' => 'Namespace\To\Type\Class' * **orm.ems.options**: Array of Entity Manager configuration sets indexed by each Entity Manager's @@ -234,6 +236,31 @@ Configuration `Doctrine\ORM\Mapping\EntityListenerResolver`. * **orm.default_cache**: String or array describing default cache implementation. + + Example configuration: + ```php + 'array', + // or + 'orm.default_cache' => array('driver' => 'array'), + + // or redis cache defined as metadata cache on the default em + 'default' => array( + ... + 'metadata_cache' => array( + 'driver' => 'redis', + 'host' => '127.0.0.1', // mandatory redis host + 'port' => 6379, // mandatory redis port + 'password' => 'mypassword', // optional redis password + 'database' => 1, // optional redis database, default: 0 + 'namespace' => 'MYNAMESPACE:' // optional namespace for this cache, otherwise cache_namespace from the current em will be used + ) + ... + ) + ); + ``` * **orm.add_mapping_driver**: Function providing the ability to add a mapping driver to an Entity Manager. diff --git a/composer.json b/composer.json index 31c1f89..4f54665 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,12 @@ "require": { "php": ">=5.3.3", "pimple/pimple": ">=2.1,<4", - "doctrine/orm": "~2.3" + "doctrine/orm": "^2.3", + "symfony/cache": "^5.0|^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.4", + "doctrine/annotations": "^2.0" }, "autoload": { "psr-4": { @@ -28,7 +33,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-master": "2.5.x-dev" } } } diff --git a/src/Dflydev/Provider/DoctrineOrm/DoctrineOrmServiceProvider.php b/src/Dflydev/Provider/DoctrineOrm/DoctrineOrmServiceProvider.php index fe28d94..5a46bbc 100755 --- a/src/Dflydev/Provider/DoctrineOrm/DoctrineOrmServiceProvider.php +++ b/src/Dflydev/Provider/DoctrineOrm/DoctrineOrmServiceProvider.php @@ -11,32 +11,32 @@ namespace Dflydev\Provider\DoctrineOrm; -use Doctrine\Common\Cache\ApcCache; -use Doctrine\Common\Cache\ArrayCache; -use Doctrine\Common\Cache\CacheProvider; -use Doctrine\Common\Cache\FilesystemCache; -use Doctrine\Common\Cache\MemcacheCache; -use Doctrine\Common\Cache\MemcachedCache; -use Doctrine\Common\Cache\CouchbaseCache; -use Doctrine\Common\Cache\XcacheCache; -use Doctrine\Common\Cache\RedisCache; -use Doctrine\Common\Persistence\Mapping\Driver\MappingDriver; -use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain; +use Doctrine\ORM\ORMSetup; +use Doctrine\Persistence\Mapping\Driver\MappingDriver; +use Doctrine\Persistence\Mapping\Driver\MappingDriverChain; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\DefaultEntityListenerResolver; use Doctrine\ORM\Mapping\DefaultNamingStrategy; use Doctrine\ORM\Mapping\DefaultQuoteStrategy; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Mapping\Driver\Driver; use Doctrine\ORM\Mapping\Driver\SimplifiedXmlDriver; use Doctrine\ORM\Mapping\Driver\SimplifiedYamlDriver; use Doctrine\ORM\Mapping\Driver\XmlDriver; use Doctrine\ORM\Mapping\Driver\YamlDriver; -use Doctrine\ORM\Mapping\Driver\StaticPHPDriver; +use Doctrine\Persistence\Mapping\Driver\StaticPHPDriver; use Doctrine\ORM\Repository\DefaultRepositoryFactory; use Pimple\Container; use Pimple\ServiceProviderInterface; +use Symfony\Component\Cache\Adapter\ApcuAdapter; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter; +use Symfony\Component\Cache\Adapter\CouchbaseCollectionAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\MemcachedAdapter; +use Symfony\Component\Cache\Adapter\RedisAdapter; /** * Doctrine ORM Pimple Service Provider. @@ -163,20 +163,16 @@ public function register(Container $container) } switch ($entity['type']) { - case 'annotation': - $useSimpleAnnotationReader = - isset($entity['use_simple_annotation_reader']) - ? $entity['use_simple_annotation_reader'] - : true; - $driver = $config->newDefaultAnnotationDriver((array) $entity['path'], $useSimpleAnnotationReader); - $chain->addDriver($driver, $entity['namespace']); - break; - case 'yml': - $driver = new YamlDriver($entity['path']); + case 'attribute': + $driver = new AttributeDriver(array($entity['path'])); $chain->addDriver($driver, $entity['namespace']); break; - case 'simple_yml': - $driver = new SimplifiedYamlDriver(array($entity['path'] => $entity['namespace'])); + case 'annotation': + $annotationsCache = null; + if (array_key_exists('cache', $entity) && $entity['cache'] !== null) { + $annotationsCache = $entity['cache']; + } + $driver = ORMSetup::createDefaultAnnotationDriver((array) $entity['path'], $annotationsCache); $chain->addDriver($driver, $entity['namespace']); break; case 'xml': @@ -213,10 +209,10 @@ public function register(Container $container) }; $container['orm.cache.configurer'] = $container->protect(function ($name, Configuration $config, $options) use ($container) { - $config->setMetadataCacheImpl($container['orm.cache.locator']($name, 'metadata', $options)); - $config->setQueryCacheImpl($container['orm.cache.locator']($name, 'query', $options)); - $config->setResultCacheImpl($container['orm.cache.locator']($name, 'result', $options)); - $config->setHydrationCacheImpl($container['orm.cache.locator']($name, 'hydration', $options)); + $config->setMetadataCache($container['orm.cache.locator']($name, 'metadata', $options)); + $config->setQueryCache($container['orm.cache.locator']($name, 'query', $options)); + $config->setResultCache($container['orm.cache.locator']($name, 'result', $options)); + $config->setHydrationCache($container['orm.cache.locator']($name, 'hydration', $options)); }); $container['orm.cache.locator'] = $container->protect(function ($name, $cacheName, $options) use ($container) { @@ -245,32 +241,9 @@ public function register(Container $container) $cache = $container['orm.cache.factory']($driver, $options[$cacheNameKey]); - if (isset($options['cache_namespace']) && $cache instanceof CacheProvider) { - $cache->setNamespace($options['cache_namespace']); - } - return $container[$cacheInstanceKey] = $cache; }); - $container['orm.cache.factory.backing_memcache'] = $container->protect(function () { - return new \Memcache; - }); - - $container['orm.cache.factory.memcache'] = $container->protect(function ($cacheOptions) use ($container) { - if (empty($cacheOptions['host']) || empty($cacheOptions['port'])) { - throw new \RuntimeException('Host and port options need to be specified for memcache cache'); - } - - /** @var \Memcache $memcache */ - $memcache = $container['orm.cache.factory.backing_memcache'](); - $memcache->connect($cacheOptions['host'], $cacheOptions['port']); - - $cache = new MemcacheCache; - $cache->setMemcache($memcache); - - return $cache; - }); - $container['orm.cache.factory.backing_memcached'] = $container->protect(function () { return new \Memcached; }); @@ -284,8 +257,7 @@ public function register(Container $container) $memcached = $container['orm.cache.factory.backing_memcached'](); $memcached->addServer($cacheOptions['host'], $cacheOptions['port']); - $cache = new MemcachedCache; - $cache->setMemcached($memcached); + $cache = new MemcachedAdapter($memcached, $cacheOptions['namespace']); return $cache; }); @@ -307,22 +279,21 @@ public function register(Container $container) $redis->auth($cacheOptions['password']); } - $cache = new RedisCache; - $cache->setRedis($redis); + if (isset($cacheOptions['database'])) { + $redis->select($cacheOptions['database']); + } - return $cache; - }); + $cache = new RedisAdapter($redis, $cacheOptions['namespace']); - $container['orm.cache.factory.array'] = $container->protect(function () { - return new ArrayCache; + return $cache; }); - $container['orm.cache.factory.apc'] = $container->protect(function () { - return new ApcCache; + $container['orm.cache.factory.array'] = $container->protect(function ($cacheOptions) { + return new ArrayAdapter(); }); - $container['orm.cache.factory.xcache'] = $container->protect(function () { - return new XcacheCache; + $container['orm.cache.factory.apc'] = $container->protect(function ($cacheOptions) { + return new ApcuAdapter($cacheOptions['namespace']); }); $container['orm.cache.factory.filesystem'] = $container->protect(function ($cacheOptions) { @@ -330,35 +301,7 @@ public function register(Container $container) throw new \RuntimeException('FilesystemCache path not defined'); } - $cacheOptions += array( - 'extension' => FilesystemCache::EXTENSION, - 'umask' => 0002, - ); - return new FilesystemCache($cacheOptions['path'], $cacheOptions['extension'], $cacheOptions['umask']); - }); - - $container['orm.cache.factory.couchbase'] = $container->protect(function($cacheOptions){ - $host=''; - $bucketName=''; - $user=''; - $password=''; - if (empty($cacheOptions['host'])) { - $host='127.0.0.1'; - } - if (empty($cacheOptions['bucket'])) { - $bucketName='default'; - } - if (!empty($cacheOptions['user'])) { - $user=$cacheOptions['user']; - } - if (!empty($cacheOptions['password'])) { - $password=$cacheOptions['password']; - } - - $couchbase = new \Couchbase($host,$user,$password,$bucketName); - $cache = new CouchbaseCache(); - $cache->setCouchbase($couchbase); - return $cache; + return new FilesystemAdapter($cacheOptions['namespace'], 0, $cacheOptions['path']); }); $container['orm.cache.factory'] = $container->protect(function ($driver, $cacheOptions) use ($container) { diff --git a/tests/Dflydev/Tests/Provider/DoctrineOrm/DoctrineOrmServiceProviderTest.php b/tests/Dflydev/Tests/Provider/DoctrineOrm/DoctrineOrmServiceProviderTest.php index 4715c72..e9784b0 100644 --- a/tests/Dflydev/Tests/Provider/DoctrineOrm/DoctrineOrmServiceProviderTest.php +++ b/tests/Dflydev/Tests/Provider/DoctrineOrm/DoctrineOrmServiceProviderTest.php @@ -13,21 +13,22 @@ use Dflydev\Provider\DoctrineOrm\DoctrineOrmServiceProvider; use Pimple\Container; +use PHPUnit\Framework\TestCase; /** * DoctrineOrmServiceProvider Test. * * @author Beau Simensen */ -class DoctrineOrmServiceProviderTest extends \PHPUnit_Framework_TestCase +class DoctrineOrmServiceProviderTest extends TestCase { protected function createMockDefaultAppAndDeps() { $container = new Container(); - $eventManager = $this->getMock('Doctrine\Common\EventManager'); + $eventManager = $this->getMockBuilder(\Doctrine\Common\EventManager::class)->getMock(); $connection = $this - ->getMockBuilder('Doctrine\DBAL\Connection') + ->getMockBuilder(\Doctrine\DBAL\Connection::class) ->disableOriginalConstructor() ->getMock(); @@ -67,11 +68,11 @@ public function testRegisterDefaultImplementations() $container->register(new DoctrineOrmServiceProvider()); $this->assertEquals($container['orm.em'], $container['orm.ems']['default']); - $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getQueryCacheImpl()); - $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getResultCacheImpl()); - $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getMetadataCacheImpl()); - $this->assertInstanceOf('Doctrine\Common\Cache\ArrayCache', $container['orm.em.config']->getHydrationCacheImpl()); - $this->assertInstanceOf('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain', $container['orm.em.config']->getMetadataDriverImpl()); + $this->assertInstanceOf(\Symfony\Component\Cache\Adapter\ArrayAdapter::class, $container['orm.em.config']->getQueryCache()); + $this->assertInstanceOf(\Symfony\Component\Cache\Adapter\ArrayAdapter::class, $container['orm.em.config']->getResultCache()); + $this->assertInstanceOf(\Symfony\Component\Cache\Adapter\ArrayAdapter::class, $container['orm.em.config']->getMetadataCache()); + $this->assertInstanceOf(\Symfony\Component\Cache\Adapter\ArrayAdapter::class, $container['orm.em.config']->getHydrationCache()); + $this->assertInstanceOf(\Doctrine\Persistence\Mapping\Driver\MappingDriverChain::class, $container['orm.em.config']->getMetadataDriverImpl()); } /** @@ -81,11 +82,11 @@ public function testRegisterDefinedImplementations() { $container = $this->createMockDefaultApp(); - $queryCache = $this->getMock('Doctrine\Common\Cache\ArrayCache'); - $resultCache = $this->getMock('Doctrine\Common\Cache\ArrayCache'); - $metadataCache = $this->getMock('Doctrine\Common\Cache\ArrayCache'); + $queryCache = $this->getMockBuilder(\Symfony\Component\Cache\Adapter\ArrayAdapter::class)->getMock(); + $resultCache = $this->getMockBuilder(\Symfony\Component\Cache\Adapter\ArrayAdapter::class)->getMock(); + $metadataCache = $this->getMockBuilder(\Symfony\Component\Cache\Adapter\ArrayAdapter::class)->getMock(); - $mappingDriverChain = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain'); + $mappingDriverChain = $this->getMockBuilder(\Doctrine\Persistence\Mapping\Driver\MappingDriverChain::class)->getMock(); $container['orm.cache.instances.default.query'] = $queryCache; $container['orm.cache.instances.default.result'] = $resultCache; @@ -96,9 +97,9 @@ public function testRegisterDefinedImplementations() $container->register(new DoctrineOrmServiceProvider); $this->assertEquals($container['orm.em'], $container['orm.ems']['default']); - $this->assertEquals($queryCache, $container['orm.em.config']->getQueryCacheImpl()); - $this->assertEquals($resultCache, $container['orm.em.config']->getResultCacheImpl()); - $this->assertEquals($metadataCache, $container['orm.em.config']->getMetadataCacheImpl()); + $this->assertEquals($queryCache, $container['orm.em.config']->getQueryCache()); + $this->assertEquals($resultCache, $container['orm.em.config']->getResultCache()); + $this->assertEquals($metadataCache, $container['orm.em.config']->getMetadataCache()); $this->assertEquals($mappingDriverChain, $container['orm.em.config']->getMetadataDriverImpl()); } @@ -111,7 +112,7 @@ public function testProxyConfigurationDefaults() $container->register(new DoctrineOrmServiceProvider); - $this->assertContains('/../../../../../../../cache/doctrine/proxies', $container['orm.em.config']->getProxyDir()); + $this->assertStringContainsString('/../../../../../../../cache/doctrine/proxies', $container['orm.em.config']->getProxyDir()); $this->assertEquals('DoctrineProxy', $container['orm.em.config']->getProxyNamespace()); $this->assertEquals(1,$container['orm.em.config']->getAutoGenerateProxyClasses()); } @@ -125,11 +126,11 @@ public function testProxyConfigurationDefined() $container->register(new DoctrineOrmServiceProvider); - $entityRepositoryClassName = get_class($this->getMock('Doctrine\Common\Persistence\ObjectRepository')); - $metadataFactoryName = get_class($this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadataFactory')); + $entityRepositoryClassName = get_class($this->getMockBuilder(\Doctrine\Persistence\ObjectRepository::class)->getMock()); + $metadataFactoryName = get_class($this->getMockBuilder(\Doctrine\Persistence\Mapping\ClassMetadataFactory::class)->getMock()); - $entityListenerResolver = $this->getMock('Doctrine\ORM\Mapping\EntityListenerResolver'); - $repositoryFactory = $this->getMock('Doctrine\ORM\Repository\RepositoryFactory'); + $entityListenerResolver = $this->getMockBuilder(\Doctrine\ORM\Mapping\EntityListenerResolver::class)->getMock(); + $repositoryFactory = $this->getMockBuilder(\Doctrine\ORM\Repository\RepositoryFactory::class)->getMock(); $container['orm.proxies_dir'] = '/path/to/proxies'; $container['orm.proxies_namespace'] = 'TestDoctrineOrmProxiesNamespace'; @@ -138,7 +139,7 @@ public function testProxyConfigurationDefined() $container['orm.default_repository_class'] = $entityRepositoryClassName; $container['orm.entity_listener_resolver'] = $entityListenerResolver; $container['orm.repository_factory'] = $repositoryFactory; - $container['orm.custom.hydration_modes'] = array('mymode' => 'Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator'); + $container['orm.custom.hydration_modes'] = array('mymode' => \Doctrine\ORM\Internal\Hydration\SimpleObjectHydrator::class); $this->assertEquals('/path/to/proxies', $container['orm.em.config']->getProxyDir()); $this->assertEquals('TestDoctrineOrmProxiesNamespace', $container['orm.em.config']->getProxyNamespace()); @@ -171,9 +172,9 @@ public function testAddMappingDriverDefault() { $container = $this->createMockDefaultApp(); - $mappingDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); + $mappingDriver = $this->getMockBuilder(\Doctrine\Persistence\Mapping\Driver\MappingDriver::class)->getMock(); - $mappingDriverChain = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain'); + $mappingDriverChain = $this->getMockBuilder(\Doctrine\Persistence\Mapping\Driver\MappingDriverChain::class)->getMock(); $mappingDriverChain ->expects($this->once()) ->method('addDriver') @@ -193,9 +194,9 @@ public function testAddMappingDriverNamedEntityManager() { $container = $this->createMockDefaultApp(); - $mappingDriver = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriver'); + $mappingDriver = $this->getMockBuilder(\Doctrine\Persistence\Mapping\Driver\MappingDriver::class)->getMock(); - $mappingDriverChain = $this->getMock('Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain'); + $mappingDriverChain = $this->getMockBuilder(\Doctrine\Persistence\Mapping\Driver\MappingDriverChain::class)->getMock(); $mappingDriverChain ->expects($this->once()) ->method('addDriver') @@ -286,9 +287,12 @@ public function testInvalidMappingAsOption() $container['orm.em.options'] = array( 'mappings' => array( - 'type' => 'annotation', - 'namespace' => 'Foo\Entities', - 'path' => __DIR__.'/src/Foo/Entities', + array( + 'type' => 'annotation', + 'namespace' => 'Foo\Entities', + 'path' => __DIR__.'/src/Foo/Entities', + 'cache' => null, + ), ), ); @@ -313,7 +317,7 @@ public function testMappingAlias() 'type' => 'annotation', 'namespace' => $namespace, 'path' => __DIR__.'/src/Foo/Entities', - 'alias' => $alias + 'alias' => $alias, ) ), ); @@ -328,8 +332,8 @@ public function testStrategy() $doctrineOrmServiceProvider = new DoctrineOrmServiceProvider; $doctrineOrmServiceProvider->register($app); - $namingStrategy = $this->getMock('Doctrine\ORM\Mapping\DefaultNamingStrategy'); - $quoteStrategy = $this->getMock('Doctrine\ORM\Mapping\DefaultQuoteStrategy'); + $namingStrategy = $this->getMockBuilder(\Doctrine\ORM\Mapping\DefaultNamingStrategy::class)->getMock(); + $quoteStrategy = $this->getMockBuilder(\Doctrine\ORM\Mapping\DefaultQuoteStrategy::class)->getMock(); $app['orm.strategy.naming'] = $namingStrategy; $app['orm.strategy.quote'] = $quoteStrategy; @@ -345,9 +349,9 @@ public function testCustomFunctions() $doctrineOrmServiceProvider = new DoctrineOrmServiceProvider; $doctrineOrmServiceProvider->register($app); - $numericFunction = $this->getMock('Doctrine\ORM\Query\AST\Functions\FunctionNode', array(), array('mynum')); - $stringFunction = $this->getMock('Doctrine\ORM\Query\AST\Functions\FunctionNode', array(), array('mynum')); - $datetimeFunction = $this->getMock('Doctrine\ORM\Query\AST\Functions\FunctionNode', array(), array('mynum')); + $numericFunction = $this->getMockBuilder(\Doctrine\ORM\Query\AST\Functions\FunctionNode::class, array(), array('mynum')); + $stringFunction = $this->getMockBuilder(\Doctrine\ORM\Query\AST\Functions\FunctionNode::class, array(), array('mynum')); + $datetimeFunction = $this->getMockBuilder(\Doctrine\ORM\Query\AST\Functions\FunctionNode::class, array(), array('mynum')); $app['orm.custom.functions.string'] = array('mystring' => $numericFunction); $app['orm.custom.functions.numeric'] = array('mynumeric' => $stringFunction);