vendor/symfony/doctrine-bridge/DependencyInjection/AbstractDoctrineExtension.php line 201

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <[email protected]>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Bridge\Doctrine\DependencyInjection;
  11. use Symfony\Component\Config\Resource\GlobResource;
  12. use Symfony\Component\DependencyInjection\Alias;
  13. use Symfony\Component\DependencyInjection\ContainerBuilder;
  14. use Symfony\Component\DependencyInjection\Definition;
  15. use Symfony\Component\DependencyInjection\Reference;
  16. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  17. /**
  18.  * This abstract classes groups common code that Doctrine Object Manager extensions (ORM, MongoDB, CouchDB) need.
  19.  *
  20.  * @author Benjamin Eberlei <[email protected]>
  21.  */
  22. abstract class AbstractDoctrineExtension extends Extension
  23. {
  24.     /**
  25.      * Used inside metadata driver method to simplify aggregation of data.
  26.      */
  27.     protected $aliasMap = [];
  28.     /**
  29.      * Used inside metadata driver method to simplify aggregation of data.
  30.      */
  31.     protected $drivers = [];
  32.     /**
  33.      * @param array $objectManager A configured object manager
  34.      *
  35.      * @throws \InvalidArgumentException
  36.      */
  37.     protected function loadMappingInformation(array $objectManagerContainerBuilder $container)
  38.     {
  39.         if ($objectManager['auto_mapping']) {
  40.             // automatically register bundle mappings
  41.             foreach (array_keys($container->getParameter('kernel.bundles')) as $bundle) {
  42.                 if (!isset($objectManager['mappings'][$bundle])) {
  43.                     $objectManager['mappings'][$bundle] = [
  44.                         'mapping' => true,
  45.                         'is_bundle' => true,
  46.                     ];
  47.                 }
  48.             }
  49.         }
  50.         foreach ($objectManager['mappings'] as $mappingName => $mappingConfig) {
  51.             if (null !== $mappingConfig && false === $mappingConfig['mapping']) {
  52.                 continue;
  53.             }
  54.             $mappingConfig array_replace([
  55.                 'dir' => false,
  56.                 'type' => false,
  57.                 'prefix' => false,
  58.             ], (array) $mappingConfig);
  59.             $mappingConfig['dir'] = $container->getParameterBag()->resolveValue($mappingConfig['dir']);
  60.             // a bundle configuration is detected by realizing that the specified dir is not absolute and existing
  61.             if (!isset($mappingConfig['is_bundle'])) {
  62.                 $mappingConfig['is_bundle'] = !is_dir($mappingConfig['dir']);
  63.             }
  64.             if ($mappingConfig['is_bundle']) {
  65.                 $bundle null;
  66.                 foreach ($container->getParameter('kernel.bundles') as $name => $class) {
  67.                     if ($mappingName === $name) {
  68.                         $bundle = new \ReflectionClass($class);
  69.                         break;
  70.                     }
  71.                 }
  72.                 if (null === $bundle) {
  73.                     throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled.'$mappingName));
  74.                 }
  75.                 $mappingConfig $this->getMappingDriverBundleConfigDefaults($mappingConfig$bundle$container);
  76.                 if (!$mappingConfig) {
  77.                     continue;
  78.                 }
  79.             } elseif (!$mappingConfig['type'] && \PHP_VERSION_ID 80000) {
  80.                 $mappingConfig['type'] = 'annotation';
  81.             } elseif (!$mappingConfig['type']) {
  82.                 $mappingConfig['type'] = 'attribute';
  83.                 $glob = new GlobResource($mappingConfig['dir'], '*'true);
  84.                 $container->addResource($glob);
  85.                 foreach ($glob as $file) {
  86.                     $content file_get_contents($file);
  87.                     if (preg_match('/^#\[.*Entity\b/m'$content)) {
  88.                         break;
  89.                     }
  90.                     if (preg_match('/^ \* @.*Entity\b/m'$content)) {
  91.                         $mappingConfig['type'] = 'annotation';
  92.                         break;
  93.                     }
  94.                 }
  95.             }
  96.             $this->assertValidMappingConfiguration($mappingConfig$objectManager['name']);
  97.             $this->setMappingDriverConfig($mappingConfig$mappingName);
  98.             $this->setMappingDriverAlias($mappingConfig$mappingName);
  99.         }
  100.     }
  101.     /**
  102.      * Register the alias for this mapping driver.
  103.      *
  104.      * Aliases can be used in the Query languages of all the Doctrine object managers to simplify writing tasks.
  105.      *
  106.      * @param array  $mappingConfig
  107.      * @param string $mappingName
  108.      */
  109.     protected function setMappingDriverAlias($mappingConfig$mappingName)
  110.     {
  111.         if (isset($mappingConfig['alias'])) {
  112.             $this->aliasMap[$mappingConfig['alias']] = $mappingConfig['prefix'];
  113.         } else {
  114.             $this->aliasMap[$mappingName] = $mappingConfig['prefix'];
  115.         }
  116.     }
  117.     /**
  118.      * Register the mapping driver configuration for later use with the object managers metadata driver chain.
  119.      *
  120.      * @param string $mappingName
  121.      *
  122.      * @throws \InvalidArgumentException
  123.      */
  124.     protected function setMappingDriverConfig(array $mappingConfig$mappingName)
  125.     {
  126.         $mappingDirectory $mappingConfig['dir'];
  127.         if (!is_dir($mappingDirectory)) {
  128.             throw new \InvalidArgumentException(sprintf('Invalid Doctrine mapping path given. Cannot load Doctrine mapping/bundle named "%s".'$mappingName));
  129.         }
  130.         $this->drivers[$mappingConfig['type']][$mappingConfig['prefix']] = realpath($mappingDirectory) ?: $mappingDirectory;
  131.     }
  132.     /**
  133.      * If this is a bundle controlled mapping all the missing information can be autodetected by this method.
  134.      *
  135.      * Returns false when autodetection failed, an array of the completed information otherwise.
  136.      *
  137.      * @return array|false
  138.      */
  139.     protected function getMappingDriverBundleConfigDefaults(array $bundleConfig, \ReflectionClass $bundleContainerBuilder $container)
  140.     {
  141.         $bundleDir = \dirname($bundle->getFileName());
  142.         if (!$bundleConfig['type']) {
  143.             $bundleConfig['type'] = $this->detectMetadataDriver($bundleDir$container);
  144.         }
  145.         if (!$bundleConfig['type']) {
  146.             // skip this bundle, no mapping information was found.
  147.             return false;
  148.         }
  149.         if (!$bundleConfig['dir']) {
  150.             if (\in_array($bundleConfig['type'], ['annotation''staticphp''attribute'])) {
  151.                 $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingObjectDefaultName();
  152.             } else {
  153.                 $bundleConfig['dir'] = $bundleDir.'/'.$this->getMappingResourceConfigDirectory();
  154.             }
  155.         } else {
  156.             $bundleConfig['dir'] = $bundleDir.'/'.$bundleConfig['dir'];
  157.         }
  158.         if (!$bundleConfig['prefix']) {
  159.             $bundleConfig['prefix'] = $bundle->getNamespaceName().'\\'.$this->getMappingObjectDefaultName();
  160.         }
  161.         return $bundleConfig;
  162.     }
  163.     /**
  164.      * Register all the collected mapping information with the object manager by registering the appropriate mapping drivers.
  165.      *
  166.      * @param array $objectManager
  167.      */
  168.     protected function registerMappingDrivers($objectManagerContainerBuilder $container)
  169.     {
  170.         // configure metadata driver for each bundle based on the type of mapping files found
  171.         if ($container->hasDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'))) {
  172.             $chainDriverDef $container->getDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'));
  173.         } else {
  174.             $chainDriverDef = new Definition($this->getMetadataDriverClass('driver_chain'));
  175.             $chainDriverDef->setPublic(false);
  176.         }
  177.         foreach ($this->drivers as $driverType => $driverPaths) {
  178.             $mappingService $this->getObjectManagerElementName($objectManager['name'].'_'.$driverType.'_metadata_driver');
  179.             if ($container->hasDefinition($mappingService)) {
  180.                 $mappingDriverDef $container->getDefinition($mappingService);
  181.                 $args $mappingDriverDef->getArguments();
  182.                 if ('annotation' == $driverType) {
  183.                     $args[1] = array_merge(array_values($driverPaths), $args[1]);
  184.                 } else {
  185.                     $args[0] = array_merge(array_values($driverPaths), $args[0]);
  186.                 }
  187.                 $mappingDriverDef->setArguments($args);
  188.             } elseif ('attribute' === $driverType) {
  189.                 $mappingDriverDef = new Definition($this->getMetadataDriverClass($driverType), [
  190.                     array_values($driverPaths),
  191.                 ]);
  192.             } elseif ('annotation' == $driverType) {
  193.                 $mappingDriverDef = new Definition($this->getMetadataDriverClass($driverType), [
  194.                     new Reference($this->getObjectManagerElementName('metadata.annotation_reader')),
  195.                     array_values($driverPaths),
  196.                 ]);
  197.             } else {
  198.                 $mappingDriverDef = new Definition($this->getMetadataDriverClass($driverType), [
  199.                     array_values($driverPaths),
  200.                 ]);
  201.             }
  202.             $mappingDriverDef->setPublic(false);
  203.             if (str_contains($mappingDriverDef->getClass(), 'yml') || str_contains($mappingDriverDef->getClass(), 'xml')) {
  204.                 $mappingDriverDef->setArguments([array_flip($driverPaths)]);
  205.                 $mappingDriverDef->addMethodCall('setGlobalBasename', ['mapping']);
  206.             }
  207.             $container->setDefinition($mappingService$mappingDriverDef);
  208.             foreach ($driverPaths as $prefix => $driverPath) {
  209.                 $chainDriverDef->addMethodCall('addDriver', [new Reference($mappingService), $prefix]);
  210.             }
  211.         }
  212.         $container->setDefinition($this->getObjectManagerElementName($objectManager['name'].'_metadata_driver'), $chainDriverDef);
  213.     }
  214.     /**
  215.      * Assertion if the specified mapping information is valid.
  216.      *
  217.      * @param string $objectManagerName
  218.      *
  219.      * @throws \InvalidArgumentException
  220.      */
  221.     protected function assertValidMappingConfiguration(array $mappingConfig$objectManagerName)
  222.     {
  223.         if (!$mappingConfig['type'] || !$mappingConfig['dir'] || !$mappingConfig['prefix']) {
  224.             throw new \InvalidArgumentException(sprintf('Mapping definitions for Doctrine manager "%s" require at least the "type", "dir" and "prefix" options.'$objectManagerName));
  225.         }
  226.         if (!is_dir($mappingConfig['dir'])) {
  227.             throw new \InvalidArgumentException(sprintf('Specified non-existing directory "%s" as Doctrine mapping source.'$mappingConfig['dir']));
  228.         }
  229.         if (!\in_array($mappingConfig['type'], ['xml''yml''annotation''php''staticphp''attribute'])) {
  230.             throw new \InvalidArgumentException(sprintf('Can only configure "xml", "yml", "annotation", "php", "staticphp" or "attribute" through the DoctrineBundle. Use your own bundle to configure other metadata drivers. You can register them by adding a new driver to the "%s" service definition.'$this->getObjectManagerElementName($objectManagerName.'_metadata_driver')));
  231.         }
  232.     }
  233.     /**
  234.      * Detects what metadata driver to use for the supplied directory.
  235.      *
  236.      * @param string $dir A directory path
  237.      *
  238.      * @return string|null A metadata driver short name, if one can be detected
  239.      */
  240.     protected function detectMetadataDriver($dirContainerBuilder $container)
  241.     {
  242.         $configPath $this->getMappingResourceConfigDirectory();
  243.         $extension $this->getMappingResourceExtension();
  244.         if (glob($dir.'/'.$configPath.'/*.'.$extension.'.xml', \GLOB_NOSORT)) {
  245.             $driver 'xml';
  246.         } elseif (glob($dir.'/'.$configPath.'/*.'.$extension.'.yml', \GLOB_NOSORT)) {
  247.             $driver 'yml';
  248.         } elseif (glob($dir.'/'.$configPath.'/*.'.$extension.'.php', \GLOB_NOSORT)) {
  249.             $driver 'php';
  250.         } else {
  251.             // add the closest existing directory as a resource
  252.             $resource $dir.'/'.$configPath;
  253.             while (!is_dir($resource)) {
  254.                 $resource = \dirname($resource);
  255.             }
  256.             $container->fileExists($resourcefalse);
  257.             return $container->fileExists($dir.'/'.$this->getMappingObjectDefaultName(), false) ? 'annotation' null;
  258.         }
  259.         $container->fileExists($dir.'/'.$configPathfalse);
  260.         return $driver;
  261.     }
  262.     /**
  263.      * Loads a configured object manager metadata, query or result cache driver.
  264.      *
  265.      * @param array  $objectManager A configured object manager
  266.      * @param string $cacheName
  267.      *
  268.      * @throws \InvalidArgumentException in case of unknown driver type
  269.      */
  270.     protected function loadObjectManagerCacheDriver(array $objectManagerContainerBuilder $container$cacheName)
  271.     {
  272.         $this->loadCacheDriver($cacheName$objectManager['name'], $objectManager[$cacheName.'_driver'], $container);
  273.     }
  274.     /**
  275.      * Loads a cache driver.
  276.      *
  277.      * @param string $cacheName         The cache driver name
  278.      * @param string $objectManagerName The object manager name
  279.      * @param array  $cacheDriver       The cache driver mapping
  280.      *
  281.      * @return string
  282.      *
  283.      * @throws \InvalidArgumentException
  284.      */
  285.     protected function loadCacheDriver($cacheName$objectManagerName, array $cacheDriverContainerBuilder $container)
  286.     {
  287.         $cacheDriverServiceId $this->getObjectManagerElementName($objectManagerName.'_'.$cacheName);
  288.         switch ($cacheDriver['type']) {
  289.             case 'service':
  290.                 $container->setAlias($cacheDriverServiceId, new Alias($cacheDriver['id'], false));
  291.                 return $cacheDriverServiceId;
  292.             case 'memcached':
  293.                 $memcachedClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.memcached.class').'%';
  294.                 $memcachedInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.memcached_instance.class').'%';
  295.                 $memcachedHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.memcached_host').'%';
  296.                 $memcachedPort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.memcached_port').'%';
  297.                 $cacheDef = new Definition($memcachedClass);
  298.                 $memcachedInstance = new Definition($memcachedInstanceClass);
  299.                 $memcachedInstance->setPrivate(true);
  300.                 $memcachedInstance->addMethodCall('addServer', [
  301.                     $memcachedHost$memcachedPort,
  302.                 ]);
  303.                 $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_memcached_instance'$objectManagerName)), $memcachedInstance);
  304.                 $cacheDef->addMethodCall('setMemcached', [new Reference($this->getObjectManagerElementName(sprintf('%s_memcached_instance'$objectManagerName)))]);
  305.                 break;
  306.              case 'redis':
  307.                 $redisClass = !empty($cacheDriver['class']) ? $cacheDriver['class'] : '%'.$this->getObjectManagerElementName('cache.redis.class').'%';
  308.                 $redisInstanceClass = !empty($cacheDriver['instance_class']) ? $cacheDriver['instance_class'] : '%'.$this->getObjectManagerElementName('cache.redis_instance.class').'%';
  309.                 $redisHost = !empty($cacheDriver['host']) ? $cacheDriver['host'] : '%'.$this->getObjectManagerElementName('cache.redis_host').'%';
  310.                 $redisPort = !empty($cacheDriver['port']) ? $cacheDriver['port'] : '%'.$this->getObjectManagerElementName('cache.redis_port').'%';
  311.                 $cacheDef = new Definition($redisClass);
  312.                 $redisInstance = new Definition($redisInstanceClass);
  313.                 $redisInstance->setPrivate(true);
  314.                 $redisInstance->addMethodCall('connect', [
  315.                     $redisHost$redisPort,
  316.                 ]);
  317.                 $container->setDefinition($this->getObjectManagerElementName(sprintf('%s_redis_instance'$objectManagerName)), $redisInstance);
  318.                 $cacheDef->addMethodCall('setRedis', [new Reference($this->getObjectManagerElementName(sprintf('%s_redis_instance'$objectManagerName)))]);
  319.                 break;
  320.             case 'apc':
  321.             case 'apcu':
  322.             case 'array':
  323.             case 'xcache':
  324.             case 'wincache':
  325.             case 'zenddata':
  326.                 $cacheDef = new Definition('%'.$this->getObjectManagerElementName(sprintf('cache.%s.class'$cacheDriver['type'])).'%');
  327.                 break;
  328.             default:
  329.                 throw new \InvalidArgumentException(sprintf('"%s" is an unrecognized Doctrine cache driver.'$cacheDriver['type']));
  330.         }
  331.         $cacheDef->setPublic(false);
  332.         if (!isset($cacheDriver['namespace'])) {
  333.             // generate a unique namespace for the given application
  334.             if ($container->hasParameter('cache.prefix.seed')) {
  335.                 $seed '.'.$container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed'));
  336.             } else {
  337.                 $seed '_'.$container->getParameter('kernel.project_dir');
  338.             }
  339.             $seed .= '.'.$container->getParameter('kernel.container_class');
  340.             $namespace 'sf_'.$this->getMappingResourceExtension().'_'.$objectManagerName.'_'.ContainerBuilder::hash($seed);
  341.             $cacheDriver['namespace'] = $namespace;
  342.         }
  343.         $cacheDef->addMethodCall('setNamespace', [$cacheDriver['namespace']]);
  344.         $container->setDefinition($cacheDriverServiceId$cacheDef);
  345.         return $cacheDriverServiceId;
  346.     }
  347.     /**
  348.      * Returns a modified version of $managerConfigs.
  349.      *
  350.      * The manager called $autoMappedManager will map all bundles that are not mapped by other managers.
  351.      *
  352.      * @return array The modified version of $managerConfigs
  353.      */
  354.     protected function fixManagersAutoMappings(array $managerConfigs, array $bundles)
  355.     {
  356.         if ($autoMappedManager $this->validateAutoMapping($managerConfigs)) {
  357.             foreach (array_keys($bundles) as $bundle) {
  358.                 foreach ($managerConfigs as $manager) {
  359.                     if (isset($manager['mappings'][$bundle])) {
  360.                         continue 2;
  361.                     }
  362.                 }
  363.                 $managerConfigs[$autoMappedManager]['mappings'][$bundle] = [
  364.                     'mapping' => true,
  365.                     'is_bundle' => true,
  366.                 ];
  367.             }
  368.             $managerConfigs[$autoMappedManager]['auto_mapping'] = false;
  369.         }
  370.         return $managerConfigs;
  371.     }
  372.     /**
  373.      * Prefixes the relative dependency injection container path with the object manager prefix.
  374.      *
  375.      * @example $name is 'entity_manager' then the result would be 'doctrine.orm.entity_manager'
  376.      *
  377.      * @param string $name
  378.      *
  379.      * @return string
  380.      */
  381.     abstract protected function getObjectManagerElementName($name);
  382.     /**
  383.      * Noun that describes the mapped objects such as Entity or Document.
  384.      *
  385.      * Will be used for autodetection of persistent objects directory.
  386.      *
  387.      * @return string
  388.      */
  389.     abstract protected function getMappingObjectDefaultName();
  390.     /**
  391.      * Relative path from the bundle root to the directory where mapping files reside.
  392.      *
  393.      * @return string
  394.      */
  395.     abstract protected function getMappingResourceConfigDirectory();
  396.     /**
  397.      * Extension used by the mapping files.
  398.      *
  399.      * @return string
  400.      */
  401.     abstract protected function getMappingResourceExtension();
  402.     /**
  403.      * The class name used by the various mapping drivers.
  404.      */
  405.     protected function getMetadataDriverClass(string $driverType): string
  406.     {
  407.         @trigger_error(sprintf('Not declaring the "%s" method in class "%s" is deprecated since Symfony 4.4. This method will be abstract in Symfony 5.0.'__METHOD__, static::class), \E_USER_DEPRECATED);
  408.         return '%'.$this->getObjectManagerElementName('metadata.'.$driverType.'.class').'%';
  409.     }
  410.     /**
  411.      * Search for a manager that is declared as 'auto_mapping' = true.
  412.      *
  413.      * @throws \LogicException
  414.      */
  415.     private function validateAutoMapping(array $managerConfigs): ?string
  416.     {
  417.         $autoMappedManager null;
  418.         foreach ($managerConfigs as $name => $manager) {
  419.             if (!$manager['auto_mapping']) {
  420.                 continue;
  421.             }
  422.             if (null !== $autoMappedManager) {
  423.                 throw new \LogicException(sprintf('You cannot enable "auto_mapping" on more than one manager at the same time (found in "%s" and "%s"").'$autoMappedManager$name));
  424.             }
  425.             $autoMappedManager $name;
  426.         }
  427.         return $autoMappedManager;
  428.     }
  429. }