vendor/pimcore/pimcore/bundles/CoreBundle/DependencyInjection/Compiler/AreabrickPass.php line 243

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Enterprise License (PEL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  13.  */
  14. namespace Pimcore\Bundle\CoreBundle\DependencyInjection\Compiler;
  15. use Doctrine\Common\Inflector\Inflector;
  16. use Pimcore\Extension\Document\Areabrick\AreabrickInterface;
  17. use Pimcore\Extension\Document\Areabrick\AreabrickManager;
  18. use Pimcore\Extension\Document\Areabrick\Exception\ConfigurationException;
  19. use Symfony\Component\Config\Resource\DirectoryResource;
  20. use Symfony\Component\Config\Resource\FileExistenceResource;
  21. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  22. use Symfony\Component\DependencyInjection\ContainerAwareInterface;
  23. use Symfony\Component\DependencyInjection\ContainerBuilder;
  24. use Symfony\Component\DependencyInjection\Definition;
  25. use Symfony\Component\DependencyInjection\Reference;
  26. use Symfony\Component\Finder\Finder;
  27. class AreabrickPass implements CompilerPassInterface
  28. {
  29.     /**
  30.      * {@inheritdoc}
  31.      */
  32.     public function process(ContainerBuilder $container)
  33.     {
  34.         $config $container->getParameter('pimcore.config');
  35.         $areabrickManager $container->getDefinition(AreabrickManager::class);
  36.         $areabrickLocator $container->getDefinition('pimcore.document.areabrick.brick_locator');
  37.         $taggedServices $container->findTaggedServiceIds('pimcore.area.brick');
  38.         // keep a list of areas loaded via tags - those classes won't be autoloaded
  39.         $taggedAreas = [];
  40.         // the service mapping for the service locator
  41.         $locatorMapping = [];
  42.         foreach ($taggedServices as $id => $tags) {
  43.             $definition $container->getDefinition($id);
  44.             $taggedAreas[] = $definition->getClass();
  45.             // tags must define the id attribute which will be used to register the brick
  46.             // e.g. { name: pimcore.area.brick, id: blockquote }
  47.             foreach ($tags as $tag) {
  48.                 if (!array_key_exists('id'$tag)) {
  49.                     throw new ConfigurationException(sprintf('Missing "id" attribute on areabrick DI tag for service %s'$id));
  50.                 }
  51.                 // add the service to the locator
  52.                 $locatorMapping[$tag['id']] = new Reference($id);
  53.                 // register the brick with its ID on the areabrick manager
  54.                 $areabrickManager->addMethodCall('registerService', [$tag['id'], $id]);
  55.             }
  56.             // handle bricks implementing ContainerAwareInterface
  57.             $this->handleContainerAwareDefinition($container$definition);
  58.         }
  59.         // autoload areas from bundles if not yet defined via service config
  60.         if ($config['documents']['areas']['autoload']) {
  61.             $locatorMapping $this->autoloadAreabricks($container$areabrickManager$locatorMapping$taggedAreas);
  62.         }
  63.         $areabrickLocator->setArgument(0$locatorMapping);
  64.     }
  65.     /**
  66.      * To be autoloaded, an area must fulfill the following conditions:
  67.      *
  68.      *  - implement AreabrickInterface
  69.      *  - be situated in a bundle in the sub-namespace Document\Areabrick (can be nested into a deeper namespace)
  70.      *  - the class is not already defined as areabrick through manual config (not included in the tagged results above)
  71.      *
  72.      * Valid examples:
  73.      *
  74.      *  - AppBundle\Document\Areabrick\Foo
  75.      *  - AppBundle\Document\Areabrick\Foo\Bar\Baz
  76.      *
  77.      * @param ContainerBuilder $container
  78.      * @param Definition $areaManagerDefinition
  79.      * @param array $locatorMapping
  80.      * @param array $excludedClasses
  81.      *
  82.      * @return array
  83.      */
  84.     protected function autoloadAreabricks(
  85.         ContainerBuilder $container,
  86.         Definition $areaManagerDefinition,
  87.         array $locatorMapping,
  88.         array $excludedClasses
  89.     ) {
  90.         $bundles $container->getParameter('kernel.bundles_metadata');
  91.         foreach ($bundles as $bundleName => $bundleMetadata) {
  92.             $bundleAreas $this->findBundleBricks($container$bundleName$bundleMetadata$excludedClasses);
  93.             foreach ($bundleAreas as $bundleArea) {
  94.                 /** @var \ReflectionClass $reflector */
  95.                 $reflector $bundleArea['reflector'];
  96.                 $definition = new Definition($reflector->getName());
  97.                 $definition
  98.                     ->setPublic(false)
  99.                     ->setAutowired(true)
  100.                     ->setAutoconfigured(true);
  101.                 // add brick definition to container
  102.                 $container->setDefinition($bundleArea['serviceId'], $definition);
  103.                 // add the service to the locator
  104.                 $locatorMapping[$bundleArea['brickId']] = new Reference($bundleArea['serviceId']);
  105.                 // register brick on the areabrick manager
  106.                 $areaManagerDefinition->addMethodCall('registerService', [
  107.                     $bundleArea['brickId'],
  108.                     $bundleArea['serviceId'],
  109.                 ]);
  110.                 // handle bricks implementing ContainerAwareInterface
  111.                 $this->handleContainerAwareDefinition($container$definition$reflector);
  112.             }
  113.         }
  114.         return $locatorMapping;
  115.     }
  116.     /**
  117.      * Adds setContainer() call to bricks implementing ContainerAwareInterface
  118.      *
  119.      * @param ContainerBuilder $container
  120.      * @param Definition $definition
  121.      * @param \ReflectionClass|null $reflector
  122.      */
  123.     protected function handleContainerAwareDefinition(ContainerBuilder $containerDefinition $definition, \ReflectionClass $reflector null)
  124.     {
  125.         if (null === $reflector) {
  126.             $reflector = new \ReflectionClass($definition->getClass());
  127.         }
  128.         if ($reflector->implementsInterface(ContainerAwareInterface::class)) {
  129.             $definition->addMethodCall('setContainer', [new Reference('service_container')]);
  130.         }
  131.     }
  132.     /**
  133.      * Look for classes implementing AreabrickInterface in each bundle's Document\Areabrick sub-namespace
  134.      *
  135.      * @param ContainerBuilder $container
  136.      * @param string $name
  137.      * @param array $metadata
  138.      * @param array $excludedClasses
  139.      *
  140.      * @return array
  141.      */
  142.     protected function findBundleBricks(ContainerBuilder $containerstring $name, array $metadata, array $excludedClasses = []): array
  143.     {
  144.         $directory implode(DIRECTORY_SEPARATOR, [
  145.             $metadata['path'],
  146.             'Document',
  147.             'Areabrick',
  148.         ]);
  149.         // update cache when directory is added/removed
  150.         $container->addResource(new FileExistenceResource($directory));
  151.         if (!file_exists($directory) || !is_dir($directory)) {
  152.             return [];
  153.         } else {
  154.             // update container cache when areabricks are added/changed
  155.             $container->addResource(new DirectoryResource($directory'/\.php$/'));
  156.         }
  157.         $finder = new Finder();
  158.         $finder
  159.             ->files()
  160.             ->in($directory)
  161.             ->name('*.php');
  162.         $areas = [];
  163.         foreach ($finder as $classPath) {
  164.             $shortClassName $classPath->getBasename('.php');
  165.             // relative path in bundle path
  166.             $relativePath str_replace($metadata['path'], ''$classPath->getPathInfo());
  167.             $relativePath trim($relativePathDIRECTORY_SEPARATOR);
  168.             // namespace starting from bundle path
  169.             $relativeNamespace str_replace(DIRECTORY_SEPARATOR'\\'$relativePath);
  170.             // sub-namespace in Document\Areabrick
  171.             $subNamespace str_replace('Document\\Areabrick'''$relativeNamespace);
  172.             $subNamespace trim($subNamespace'\\');
  173.             // fully qualified class name
  174.             $className $metadata['namespace'] . '\\' $relativeNamespace '\\' $shortClassName;
  175.             // do not autoload areas which were already defined as service via config
  176.             if (in_array($className$excludedClasses)) {
  177.                 continue;
  178.             }
  179.             if (class_exists($className)) {
  180.                 $reflector = new \ReflectionClass($className);
  181.                 if ($reflector->isInstantiable() && $reflector->implementsInterface(AreabrickInterface::class)) {
  182.                     $brickId $this->generateBrickId($reflector);
  183.                     $serviceId $this->generateServiceId($name$subNamespace$shortClassName);
  184.                     $areas[] = [
  185.                         'brickId' => $brickId,
  186.                         'serviceId' => $serviceId,
  187.                         'bundleName' => $name,
  188.                         'bundleMetadata' => $metadata,
  189.                         'reflector' => $reflector,
  190.                     ];
  191.                 }
  192.             }
  193.         }
  194.         return $areas;
  195.     }
  196.     /**
  197.      * GalleryTeaserRow -> gallery-teaser-row
  198.      *
  199.      * @param \ReflectionClass $reflector
  200.      *
  201.      * @return string
  202.      */
  203.     protected function generateBrickId(\ReflectionClass $reflector)
  204.     {
  205.         $id Inflector::tableize($reflector->getShortName());
  206.         $id str_replace('_''-'$id);
  207.         return $id;
  208.     }
  209.     /**
  210.      * Generate service ID from bundle name and sub-namespace
  211.      *
  212.      *  - AppBundle\Document\Areabrick\Foo         -> app.area.brick.foo
  213.      *  - AppBundle\Document\Areabrick\Foo\Bar\Baz -> app.area.brick.foo.bar.baz
  214.      *
  215.      * @param string $bundleName
  216.      * @param string $subNamespace
  217.      * @param string $className
  218.      *
  219.      * @return string
  220.      */
  221.     protected function generateServiceId($bundleName$subNamespace$className)
  222.     {
  223.         $bundleName str_replace('Bundle'''$bundleName);
  224.         $bundleName Inflector::tableize($bundleName);
  225.         if (!empty($subNamespace)) {
  226.             $subNamespaceParts = [];
  227.             foreach (explode('\\'$subNamespace) as $subNamespacePart) {
  228.                 $subNamespaceParts[] = Inflector::tableize($subNamespacePart);
  229.             }
  230.             $subNamespace implode('.'$subNamespaceParts) . '.';
  231.         } else {
  232.             $subNamespace '';
  233.         }
  234.         $brickName Inflector::tableize($className);
  235.         return sprintf('%s.area.brick.%s%s'$bundleName$subNamespace$brickName);
  236.     }
  237. }