vendor/pimcore/pimcore/lib/Extension/Bundle/PimcoreBundleManager.php line 576

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4.  * Pimcore
  5.  *
  6.  * This source file is available under two different licenses:
  7.  * - GNU General Public License version 3 (GPLv3)
  8.  * - Pimcore Commercial License (PCL)
  9.  * Full copyright and license information is available in
  10.  * LICENSE.md which is distributed with this source code.
  11.  *
  12.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  13.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  14.  */
  15. namespace Pimcore\Extension\Bundle;
  16. use Pimcore\Event\BundleManager\PathsEvent;
  17. use Pimcore\Event\BundleManagerEvents;
  18. use Pimcore\Extension\Bundle\Config\StateConfig;
  19. use Pimcore\Extension\Bundle\Exception\BundleNotFoundException;
  20. use Pimcore\Extension\Bundle\Installer\Exception\InstallationException;
  21. use Pimcore\HttpKernel\BundleCollection\ItemInterface;
  22. use Pimcore\Kernel;
  23. use Pimcore\Routing\RouteReferenceInterface;
  24. use Symfony\Component\Routing\RouterInterface;
  25. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  26. /**
  27.  * @internal
  28.  */
  29. class PimcoreBundleManager
  30. {
  31.     /**
  32.      * @deprecated
  33.      *
  34.      * @var StateConfig
  35.      */
  36.     protected $stateConfig;
  37.     /**
  38.      * @var PimcoreBundleLocator
  39.      */
  40.     protected $bundleLocator;
  41.     /**
  42.      * @var Kernel
  43.      */
  44.     protected $kernel;
  45.     /**
  46.      * @var EventDispatcherInterface
  47.      */
  48.     protected $dispatcher;
  49.     /**
  50.      * @var RouterInterface
  51.      */
  52.     protected $router;
  53.     /**
  54.      * @var array
  55.      */
  56.     protected $availableBundles;
  57.     /**
  58.      * @deprecated
  59.      *
  60.      * @var array
  61.      */
  62.     protected $enabledBundles;
  63.     /**
  64.      * @var array
  65.      */
  66.     protected $manuallyRegisteredBundleState;
  67.     /**
  68.      * @param StateConfig $stateConfig
  69.      * @param PimcoreBundleLocator $bundleLocator
  70.      * @param Kernel $kernel
  71.      * @param EventDispatcherInterface $dispatcher
  72.      * @param RouterInterface $router
  73.      */
  74.     public function __construct(
  75.         StateConfig $stateConfig,
  76.         PimcoreBundleLocator $bundleLocator,
  77.         Kernel $kernel,
  78.         EventDispatcherInterface $dispatcher,
  79.         RouterInterface $router
  80.     ) {
  81.         $this->stateConfig $stateConfig;
  82.         $this->bundleLocator $bundleLocator;
  83.         $this->kernel $kernel;
  84.         $this->dispatcher $dispatcher;
  85.         $this->router $router;
  86.     }
  87.     /**
  88.      * List of currently active bundles from kernel. A bundle can be in this list without being enabled via extensions
  89.      * config file if it is registered manually on the kernel.
  90.      *
  91.      * @param bool $onlyInstalled
  92.      *
  93.      * @return PimcoreBundleInterface[]
  94.      */
  95.     public function getActiveBundles(bool $onlyInstalled true): array
  96.     {
  97.         $bundles = [];
  98.         foreach ($this->kernel->getBundles() as $bundle) {
  99.             if ($bundle instanceof PimcoreBundleInterface) {
  100.                 if ($onlyInstalled && !$this->isInstalled($bundle)) {
  101.                     continue;
  102.                 }
  103.                 $bundles[get_class($bundle)] = $bundle;
  104.             }
  105.         }
  106.         return $bundles;
  107.     }
  108.     /**
  109.      * @param string $id
  110.      * @param bool $onlyInstalled
  111.      *
  112.      * @return PimcoreBundleInterface
  113.      */
  114.     public function getActiveBundle(string $idbool $onlyInstalled true): PimcoreBundleInterface
  115.     {
  116.         foreach ($this->getActiveBundles($onlyInstalled) as $bundle) {
  117.             if ($this->getBundleIdentifier($bundle) === $id) {
  118.                 return $bundle;
  119.             }
  120.         }
  121.         throw new BundleNotFoundException(sprintf('Bundle %s was not found'$id));
  122.     }
  123.     /**
  124.      * List of available bundles from a defined set of paths
  125.      *
  126.      * @return array
  127.      */
  128.     public function getAvailableBundles(): array
  129.     {
  130.         if (null === $this->availableBundles) {
  131.             $bundles $this->getManuallyRegisteredBundleNames(false);
  132.             foreach ($this->bundleLocator->findBundles() as $locatedBundle) {
  133.                 if (!in_array($locatedBundle$bundles)) {
  134.                     $bundles[] = $locatedBundle;
  135.                 }
  136.             }
  137.             sort($bundles);
  138.             $this->availableBundles $bundles;
  139.         }
  140.         return $this->availableBundles;
  141.     }
  142.     /**
  143.      * @deprecated
  144.      *
  145.      * Lists enabled bundle names
  146.      *
  147.      * @return array
  148.      */
  149.     public function getEnabledBundleNames(): array
  150.     {
  151.         $bundleNames array_merge(
  152.             $this->getManuallyRegisteredBundleNames(true),
  153.             $this->stateConfig->getEnabledBundleNames()
  154.         );
  155.         $bundleNames array_unique($bundleNames);
  156.         sort($bundleNames);
  157.         return $bundleNames;
  158.     }
  159.     /**
  160.      * Returns names of manually registered bundles (not registered via extension manager)
  161.      *
  162.      * @param bool $onlyEnabled
  163.      *
  164.      * @return array
  165.      */
  166.     private function getManuallyRegisteredBundleNames(bool $onlyEnabled false): array
  167.     {
  168.         $state $this->getManuallyRegisteredBundleState();
  169.         if (!$onlyEnabled) {
  170.             return array_keys($state);
  171.         }
  172.         $bundleNames = [];
  173.         foreach ($state as $bundleName => $options) {
  174.             if ($options['enabled']) {
  175.                 $bundleNames[] = $bundleName;
  176.             }
  177.         }
  178.         return $bundleNames;
  179.     }
  180.     /**
  181.      * Builds state infos about manually configured bundles (not registered via extension manager)
  182.      *
  183.      * @return array
  184.      */
  185.     private function getManuallyRegisteredBundleState()
  186.     {
  187.         if (null === $this->manuallyRegisteredBundleState) {
  188.             $collection $this->kernel->getBundleCollection();
  189.             $enabledBundles array_keys($this->getActiveBundles(false));
  190.             $bundles = [];
  191.             foreach ($collection->getItems() as $item) {
  192.                 if (!$item->isPimcoreBundle()) {
  193.                     continue;
  194.                 }
  195.                 if ($item->getSource() === ItemInterface::SOURCE_EXTENSION_MANAGER_CONFIG) {
  196.                     continue;
  197.                 }
  198.                 $bundles[$item->getBundleIdentifier()] = $this->stateConfig->normalizeOptions([
  199.                     'enabled' => in_array($item->getBundleIdentifier(), $enabledBundles),
  200.                     'priority' => $item->getPriority(),
  201.                     'environments' => $item->getEnvironments(),
  202.                 ]);
  203.             }
  204.             $this->manuallyRegisteredBundleState $bundles;
  205.         }
  206.         return $this->manuallyRegisteredBundleState;
  207.     }
  208.     /**
  209.      * Determines if a bundle exists
  210.      *
  211.      * @param string|PimcoreBundleInterface $bundle
  212.      *
  213.      * @return bool
  214.      */
  215.     public function exists($bundle): bool
  216.     {
  217.         $identifier $this->getBundleIdentifier($bundle);
  218.         return $this->isValidBundleIdentifier($identifier);
  219.     }
  220.     /**
  221.      * @param string|PimcoreBundleInterface $bundle
  222.      *
  223.      * @return string
  224.      */
  225.     public function getBundleIdentifier($bundle): string
  226.     {
  227.         $identifier $bundle;
  228.         if ($bundle instanceof PimcoreBundleInterface) {
  229.             $identifier get_class($bundle);
  230.         }
  231.         return $identifier;
  232.     }
  233.     /**
  234.      * @param string $identifier
  235.      *
  236.      * @return bool
  237.      */
  238.     protected function isValidBundleIdentifier(string $identifier): bool
  239.     {
  240.         $validNames array_merge(
  241.             array_keys($this->getActiveBundles(false)),
  242.             $this->getAvailableBundles()
  243.         );
  244.         return in_array($identifier$validNames);
  245.     }
  246.     /**
  247.      * Validates bundle name against list if available and active bundles
  248.      *
  249.      * @param string $identifier
  250.      */
  251.     protected function validateBundleIdentifier(string $identifier)
  252.     {
  253.         if (!$this->isValidBundleIdentifier($identifier)) {
  254.             throw new BundleNotFoundException(sprintf('Bundle "%s" is no valid bundle identifier'$identifier));
  255.         }
  256.     }
  257.     /**
  258.      * Determines if the bundle was programatically registered (not via extension manager)
  259.      *
  260.      * @param string|PimcoreBundleInterface $bundle
  261.      *
  262.      * @return bool
  263.      */
  264.     public function isManuallyRegistered($bundle): bool
  265.     {
  266.         $identifier $this->getBundleIdentifier($bundle);
  267.         $this->validateBundleIdentifier($identifier);
  268.         return in_array($identifier$this->getManuallyRegisteredBundleNames(false));
  269.     }
  270.     /**
  271.      * @deprecated
  272.      *
  273.      * Checks if a state change (enable/disable, priority, environments) is possible
  274.      *
  275.      * @param string $identifier
  276.      */
  277.     protected function validateStateChange(string $identifier)
  278.     {
  279.         if ($this->isManuallyRegistered($identifier)) {
  280.             throw new \LogicException(sprintf(
  281.                 'Can\'t change state for bundle "%s" as it is programatically registered',
  282.                 $identifier
  283.             ));
  284.         }
  285.     }
  286.     /**
  287.      * @deprecated
  288.      *
  289.      * Determines if bundle is allowed to change state (can be enabled/disabled)
  290.      *
  291.      * @param string|PimcoreBundleInterface $bundle
  292.      *
  293.      * @return bool
  294.      */
  295.     public function canChangeState($bundle): bool
  296.     {
  297.         $identifier $this->getBundleIdentifier($bundle);
  298.         $this->validateBundleIdentifier($identifier);
  299.         return !$this->isManuallyRegistered($bundle);
  300.     }
  301.     /**
  302.      * @deprecated
  303.      *
  304.      * Reads bundle state from config
  305.      *
  306.      * @param string|PimcoreBundleInterface $bundle
  307.      *
  308.      * @return array
  309.      */
  310.     public function getState($bundle): array
  311.     {
  312.         $identifier $this->getBundleIdentifier($bundle);
  313.         $this->validateBundleIdentifier($identifier);
  314.         if ($this->isManuallyRegistered($identifier)) {
  315.             return $this->getManuallyRegisteredBundleState()[$identifier];
  316.         }
  317.         return $this->stateConfig->getState($identifier);
  318.     }
  319.     /**
  320.      * @deprecated
  321.      *
  322.      * Updates state for a bundle and writes it to config
  323.      *
  324.      * @param string|PimcoreBundleInterface $bundle
  325.      * @param array $options
  326.      */
  327.     public function setState($bundle, array $options)
  328.     {
  329.         $identifier $this->getBundleIdentifier($bundle);
  330.         $this->validateBundleIdentifier($identifier);
  331.         $this->validateStateChange($identifier);
  332.         $this->stateConfig->setState($identifier$options);
  333.     }
  334.     /**
  335.      * @deprecated
  336.      *
  337.      * Batch updates bundle states
  338.      *
  339.      * @param array $states
  340.      */
  341.     public function setStates(array $states)
  342.     {
  343.         $updates = [];
  344.         foreach ($states as $bundle => $options) {
  345.             $identifier $this->getBundleIdentifier($bundle);
  346.             $this->validateBundleIdentifier($identifier);
  347.             $this->validateStateChange($identifier);
  348.             $updates[$identifier] = $options;
  349.         }
  350.         $this->stateConfig->setStates($updates);
  351.     }
  352.     /**
  353.      * @deprecated
  354.      *
  355.      * Enables a bundle
  356.      *
  357.      * @param string|PimcoreBundleInterface $bundle
  358.      * @param array $state Optional additional state config (see StateConfig)
  359.      */
  360.     public function enable($bundle, array $state = [])
  361.     {
  362.         $state array_merge($state, [
  363.             'enabled' => true,
  364.         ]);
  365.         $this->setState($bundle$state);
  366.     }
  367.     /**
  368.      * @deprecated
  369.      *
  370.      * Disables a bundle
  371.      *
  372.      * @param string|PimcoreBundleInterface $bundle
  373.      */
  374.     public function disable($bundle)
  375.     {
  376.         $this->setState($bundle, ['enabled' => false]);
  377.     }
  378.     /**
  379.      * @deprecated
  380.      *
  381.      * Determines if a bundle is enabled
  382.      *
  383.      * @param string|PimcoreBundleInterface $bundle
  384.      *
  385.      * @return bool
  386.      */
  387.     public function isEnabled($bundle): bool
  388.     {
  389.         $identifier $this->getBundleIdentifier($bundle);
  390.         $this->validateBundleIdentifier($identifier);
  391.         return in_array($identifier$this->getEnabledBundleNames());
  392.     }
  393.     /**
  394.      * @param PimcoreBundleInterface $bundle
  395.      * @param bool $throwException
  396.      *
  397.      * @return null|Installer\InstallerInterface
  398.      */
  399.     protected function loadBundleInstaller(PimcoreBundleInterface $bundlebool $throwException false)
  400.     {
  401.         if (null === $installer $bundle->getInstaller()) {
  402.             if ($throwException) {
  403.                 throw new InstallationException(sprintf('Bundle %s does not a define an installer'$bundle->getName()));
  404.             }
  405.             return null;
  406.         }
  407.         return $installer;
  408.     }
  409.     /**
  410.      * Returns the bundle installer if configured
  411.      *
  412.      * @param PimcoreBundleInterface $bundle
  413.      * @param bool $throwException
  414.      *
  415.      * @return null|Installer\InstallerInterface
  416.      */
  417.     public function getInstaller(PimcoreBundleInterface $bundlebool $throwException false)
  418.     {
  419.         return $this->loadBundleInstaller($bundle$throwException);
  420.     }
  421.     /**
  422.      * Runs install routine for a bundle
  423.      *
  424.      * @param PimcoreBundleInterface $bundle
  425.      *
  426.      * @throws InstallationException If the bundle can not be installed or doesn't define an installer
  427.      */
  428.     public function install(PimcoreBundleInterface $bundle)
  429.     {
  430.         $installer $this->loadBundleInstaller($bundletrue);
  431.         if (!$this->canBeInstalled($bundle)) {
  432.             throw new InstallationException(sprintf('Bundle %s can not be installed'$bundle->getName()));
  433.         }
  434.         $installer->install();
  435.     }
  436.     /**
  437.      * Runs uninstall routine for a bundle
  438.      *
  439.      * @param PimcoreBundleInterface $bundle
  440.      *
  441.      * @throws InstallationException If the bundle can not be uninstalled or doesn't define an installer
  442.      */
  443.     public function uninstall(PimcoreBundleInterface $bundle)
  444.     {
  445.         $installer $this->loadBundleInstaller($bundletrue);
  446.         if (!$this->canBeUninstalled($bundle)) {
  447.             throw new InstallationException(sprintf('Bundle %s can not be uninstalled'$bundle->getName()));
  448.         }
  449.         $installer->uninstall();
  450.     }
  451.     /**
  452.      * Determines if a bundle can be installed
  453.      *
  454.      * @param PimcoreBundleInterface $bundle
  455.      *
  456.      * @return bool
  457.      */
  458.     public function canBeInstalled(PimcoreBundleInterface $bundle): bool
  459.     {
  460.         if (!$this->isEnabled($bundle)) {
  461.             return false;
  462.         }
  463.         if (null === $installer $this->loadBundleInstaller($bundle)) {
  464.             return false;
  465.         }
  466.         return $installer->canBeInstalled();
  467.     }
  468.     /**
  469.      * Determines if a bundle can be uninstalled
  470.      *
  471.      * @param PimcoreBundleInterface $bundle
  472.      *
  473.      * @return bool
  474.      */
  475.     public function canBeUninstalled(PimcoreBundleInterface $bundle): bool
  476.     {
  477.         if (!$this->isEnabled($bundle)) {
  478.             return false;
  479.         }
  480.         if (null === $installer $this->loadBundleInstaller($bundle)) {
  481.             return false;
  482.         }
  483.         return $installer->canBeUninstalled();
  484.     }
  485.     /**
  486.      * Determines if a bundle is installed
  487.      *
  488.      * @param PimcoreBundleInterface $bundle
  489.      *
  490.      * @return bool
  491.      */
  492.     public function isInstalled(PimcoreBundleInterface $bundle): bool
  493.     {
  494.         if (null === $installer $bundle->getInstaller()) {
  495.             // bundle has no dedicated installer, so we can treat it as installed
  496.             return true;
  497.         }
  498.         return $installer->isInstalled();
  499.     }
  500.     /**
  501.      * Determines if a reload is needed after installation
  502.      *
  503.      * @param PimcoreBundleInterface $bundle
  504.      *
  505.      * @return bool
  506.      */
  507.     public function needsReloadAfterInstall(PimcoreBundleInterface $bundle): bool
  508.     {
  509.         if (null === $installer $bundle->getInstaller()) {
  510.             // bundle has no dedicated installer
  511.             return false;
  512.         }
  513.         return $installer->needsReloadAfterInstall();
  514.     }
  515.     /**
  516.      * Resolves all admin javascripts to load
  517.      *
  518.      * @return array
  519.      */
  520.     public function getJsPaths(): array
  521.     {
  522.         $paths $this->resolvePaths('js');
  523.         return $this->resolveEventPaths($pathsBundleManagerEvents::JS_PATHS);
  524.     }
  525.     /**
  526.      * Resolves all admin stylesheets to load
  527.      *
  528.      * @return array
  529.      */
  530.     public function getCssPaths(): array
  531.     {
  532.         $paths $this->resolvePaths('css');
  533.         return $this->resolveEventPaths($pathsBundleManagerEvents::CSS_PATHS);
  534.     }
  535.     /**
  536.      * Resolves all editmode javascripts to load
  537.      *
  538.      * @return array
  539.      */
  540.     public function getEditmodeJsPaths(): array
  541.     {
  542.         $paths $this->resolvePaths('js''editmode');
  543.         return $this->resolveEventPaths($pathsBundleManagerEvents::EDITMODE_JS_PATHS);
  544.     }
  545.     /**
  546.      * Resolves all editmode stylesheets to load
  547.      *
  548.      * @return array
  549.      */
  550.     public function getEditmodeCssPaths(): array
  551.     {
  552.         $paths $this->resolvePaths('css''editmode');
  553.         return $this->resolveEventPaths($pathsBundleManagerEvents::EDITMODE_CSS_PATHS);
  554.     }
  555.     /**
  556.      * Iterates installed bundles and fetches asset paths
  557.      *
  558.      * @param string $type
  559.      * @param string|null $mode
  560.      *
  561.      * @return array
  562.      */
  563.     protected function resolvePaths(string $typestring $mode null): array
  564.     {
  565.         $type ucfirst($type);
  566.         if (null !== $mode) {
  567.             $mode ucfirst($mode);
  568.         } else {
  569.             $mode '';
  570.         }
  571.         // getJsPaths, getEditmodeJsPaths
  572.         $getter sprintf('get%s%sPaths'$mode$type);
  573.         $result = [];
  574.         foreach ($this->getActiveBundles() as $bundle) {
  575.             $paths $bundle->$getter();
  576.             if (!empty($paths) && !$bundle instanceof PimcoreBundleAdminClassicInterface) {
  577.                 trigger_deprecation(
  578.                     'pimcore/pimcore',
  579.                     '10.6',
  580.                     sprintf(
  581.                         'Calling %s::%s without implementing the %s is deprecated and won\'t be possible in Pimcore 11.',
  582.                         get_class($bundle),
  583.                         $getter,
  584.                         PimcoreBundleAdminClassicInterface::class
  585.                     )
  586.                 );
  587.             }
  588.             foreach ($paths as $path) {
  589.                 if ($path instanceof RouteReferenceInterface) {
  590.                     $result[] = $this->router->generate(
  591.                         $path->getRoute(),
  592.                         $path->getParameters(),
  593.                         $path->getType()
  594.                     );
  595.                 } else {
  596.                     $result[] = $path;
  597.                 }
  598.             }
  599.         }
  600.         return $result;
  601.     }
  602.     /**
  603.      * Emits given path event
  604.      *
  605.      * @param array  $paths
  606.      * @param string $eventName
  607.      *
  608.      * @return array
  609.      */
  610.     protected function resolveEventPaths(array $pathsstring $eventName): array
  611.     {
  612.         $event = new PathsEvent($paths);
  613.         $this->dispatcher->dispatch($event$eventName);
  614.         return $event->getPaths();
  615.     }
  616. }