custom/plugins/b2bsellerscore/src/B2bSellersCore.php line 63

Open in your IDE?
  1. <?php
  2. namespace B2bSellersCore;
  3. use B2bCopperBrassSurcharge\B2bCopperBrassSurcharge;
  4. use B2bCostCenter\B2bCostCenter;
  5. use B2bDiscountRate\B2bDiscountRate;
  6. use B2bEmployeeBudgets\B2bEmployeeBudgets;
  7. use B2bEProcurementCxml\B2bEProcurementCxmlPT;
  8. use B2bEProcurementOci\B2bEProcurementOci;
  9. use B2bEventManager\B2bEventManager;
  10. use B2bMobileSalesPortalApp\B2bMobileSalesPortalApp;
  11. use B2bOffer\B2bOffer;
  12. use B2bPartialAssortments\B2bPartialAssortments;
  13. use B2bProductSubscription\B2bProductSubscription;
  14. use B2bSalesRepresentativeFastOrder\B2bSalesRepresentativeFastOrder;
  15. use B2bSellersCore\Components\Checkout\Payment\PaymentConditionPayment;
  16. use B2bSellersCore\Components\Framework\DependencyInjection\B2bSellersCoreExtension;
  17. use B2bSellersCore\Setup\B2bBonusProgram\B2bBonusProgramCustomFieldInstaller;
  18. use B2bSellersCore\Setup\B2bProductLists\B2bProductListsPlatformMenuInstaller;
  19. use B2bSellersCore\Setup\B2bProductLists\ProductListsConfigSetup;
  20. use B2bSellersCore\Setup\B2bProductRequest\B2bProductRequestCustomFieldInstaller;
  21. use B2bSellersCore\Setup\B2bProductRequest\MailTemplate\B2bProductRequestMailTemplateInstaller;
  22. use B2bSellersCore\Setup\B2bUrlAuthentication\B2bUrlAuthenticationCustomFieldInstaller;
  23. use B2bSellersCore\Setup\CoreConfigSetup;
  24. use B2bSellersCore\Setup\CustomFieldSetup;
  25. use B2bSellersCore\Setup\EventActionSetup;
  26. use B2bSellersCore\Setup\FlowBuilderSetup;
  27. use B2bSellersCore\Setup\InstallationCheck;
  28. use B2bSellersCore\Setup\InstallationConstraints\PluginInstallationViolationException;
  29. use B2bSellersCore\Setup\MailTemplateSetup;
  30. use B2bSellersCore\Setup\MailTemplateUpdater;
  31. use B2bSellersCore\Setup\PlatformMenuAddonFeatureNameMigration;
  32. use B2bSellersCore\Setup\PlatformMenuTranslationsMigration;
  33. use B2bSellersCore\Setup\RuleSetup;
  34. use B2bSpareParts\B2bSpareParts;
  35. use Composer\Autoload\ClassLoader;
  36. use Composer\Autoload\ClassMapGenerator;
  37. use Doctrine\DBAL\Connection;
  38. use Shopware\Core\DevOps\Environment\EnvironmentHelper;
  39. use Shopware\Core\Framework\Bundle;
  40. use Shopware\Core\Framework\Context;
  41. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  42. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  43. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  44. use Shopware\Core\Framework\Parameter\AdditionalBundleParameters;
  45. use Shopware\Core\Framework\Plugin;
  46. use Shopware\Core\Framework\Plugin\Context\InstallContext;
  47. use Shopware\Core\Framework\Plugin\Context\UninstallContext;
  48. use Shopware\Core\Framework\Plugin\Context\UpdateContext;
  49. use Shopware\Core\Framework\Plugin\Util\PluginIdProvider;
  50. use Shopware\Core\Framework\Uuid\Uuid;
  51. use Shopware\Core\Kernel;
  52. use Symfony\Component\Config\FileLocator;
  53. use Symfony\Component\Config\Loader\DelegatingLoader;
  54. use Symfony\Component\Config\Loader\LoaderResolver;
  55. use Symfony\Component\DependencyInjection\ContainerBuilder;
  56. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  57. use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
  58. use Symfony\Component\Filesystem\Filesystem;
  59. use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
  60. class B2bSellersCore extends Plugin
  61. {
  62.     public const AVAILABLE_ADDONS = [
  63.         B2bOffer::class,
  64.         B2bEmployeeBudgets::class,
  65.         B2bCostCenter::class,
  66.         B2bDiscountRate::class,
  67.         B2bProductSubscription::class,
  68.         B2bCopperBrassSurcharge::class,
  69.         B2bSpareParts::class,
  70.         B2bEProcurementOci::class,
  71.         B2bEProcurementCxmlPT::class,
  72.         B2bSalesRepresentativeFastOrder::class,
  73.         B2bPartialAssortments::class,
  74.         B2bMobileSalesPortalApp::class,
  75.         B2bEventManager::class
  76.     ];
  77.     public const AVAILABLE_FEATURES = [
  78.         'B2bBonusProgram''B2bProductRequest''B2bUrlAuthentication''B2bPdpVariantList''B2bProductLists'
  79.     ];
  80.     public const LICENCE_FILE_PATH '/var/b2b-licence.json';
  81.     /**
  82.      * @var Plugin[]
  83.      */
  84.     private array $addonBundles = [];
  85.     public function uninstall(UninstallContext $context): void
  86.     {
  87.         parent::uninstall($context);
  88.         try {
  89.             $this->deleteCustomFieldSets();
  90.         } catch (\Exception $e) {
  91.         }
  92.         if ($context->keepUserData()) {
  93.             return;
  94.         }
  95.         $this->uninstallEventActions($context);
  96.         $this->uninstallFlowBuilder($context);
  97.         $this->uninstallMailTemplates();
  98.         $this->uninstallRules($context);
  99.         $this->uninstallPlatformMenus();
  100.         $connection $this->container->get(Connection::class);
  101.         $connection->executeStatement('SET FOREIGN_KEY_CHECKS = 0');
  102.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_sales_representative_customer`');
  103.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee`');
  104.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_customer`');
  105.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_collection_account`');
  106.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_customer_price`');
  107.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_payment_condition`');
  108.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_cart_extension`');
  109.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_role`');
  110.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_role_translation`');
  111.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_permission`');
  112.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_permission_translation`');
  113.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_permission_group`');
  114.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_permission_group_translation`');
  115.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_passwordless_login`');
  116.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_property_set`');
  117.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_property_set_translation`');
  118.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_customer_activity`');
  119.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_customer_activity_type`');
  120.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_customer_activity_type_translation`');
  121.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_platform_menu_item`');
  122.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_platform_menu_item_translation`');
  123.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_express_checkout_setting`');
  124.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_employee_invitation`');
  125.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_order_extension`');
  126.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_product_list`');
  127.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_product_list_item`');
  128.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_product_list_type`');
  129.         $connection->executeStatement('DROP TABLE IF EXISTS `b2b_product_list_type_translation`');
  130.         $connection->executeStatement('SET FOREIGN_KEY_CHECKS = 1');
  131.         $this->uninstallAddonBundles($context);
  132.     }
  133.     public function install(InstallContext $installContext): void
  134.     {
  135.         $errors = (new InstallationCheck($this->container))->checkConstraints();
  136.         if (count($errors)) {
  137.             throw new PluginInstallationViolationException('Unable to install B2Bsellers Suite:'$errors);
  138.         }
  139.         $this->createMailTemplates();
  140.         if (version_compare($installContext->getCurrentShopwareVersion(), '6.4.6.0''>=')) {
  141.             $this->createFlowBuilder($installContext);
  142.         } else {
  143.             $this->createEventActions($installContext);
  144.         }
  145.         $this->createCustomFieldsets();
  146.         $this->addPaymentMethod($installContext->getContext());
  147.     }
  148.     public function postInstall(InstallContext $installContext): void
  149.     {
  150.         $this->installRules($installContext);
  151.         $this->updatePluginConfig();
  152.         $this->createPlatformMenus();
  153.         $this->installDefaultConfigValues($installContext);
  154.         $this->installAddonBundles($installContext);
  155.         (new PlatformMenuAddonFeatureNameMigration())->install($this->container->get(Connection::class));
  156.         (new PlatformMenuTranslationsMigration())->install($this->container->get(Connection::class));
  157.     }
  158.     public function update(UpdateContext $updateContext): void
  159.     {
  160.         $this->installRules($updateContext);
  161.         $this->createMailTemplates();
  162.         if (version_compare($updateContext->getCurrentShopwareVersion(), '6.4.6.0''>=')) {
  163.             $this->createFlowBuilder($updateContext);
  164.         } else {
  165.             $this->createEventActions($updateContext);
  166.         }
  167.         $this->createCustomFieldsets();
  168.         $this->addPaymentMethod($updateContext->getContext());
  169.     }
  170.     public function postUpdate(UpdateContext $updateContext): void
  171.     {
  172.         $this->updateDefaultConfigValues($updateContext);
  173.         $this->updateAddonBundles($updateContext);
  174.         if (version_compare($updateContext->getCurrentPluginVersion(), '0.9.6''<')) {
  175.             (new MailTemplateSetup($this->container))->update();
  176.             (new PlatformMenuTranslationsMigration())->install($this->container->get(Connection::class));
  177.         }
  178.         if (version_compare($updateContext->getCurrentPluginVersion(), '1.0.2''<')) {
  179.             (new PlatformMenuAddonFeatureNameMigration())->install($this->container->get(Connection::class));
  180.         }
  181.         (new MailTemplateUpdater($updateContext$this->container))->update();
  182.     }
  183.     private function createCustomFieldSets()
  184.     {
  185.         (new CustomFieldSetup($this->container))->install();
  186.         (new B2bBonusProgramCustomFieldInstaller($this->container))->install();
  187.         (new B2bProductRequestCustomFieldInstaller($this->container))->install();
  188.         (new B2bUrlAuthenticationCustomFieldInstaller($this->container))->install();
  189.     }
  190.     private function createMailTemplates()
  191.     {
  192.         (new MailTemplateSetup($this->container))->install();
  193.         (new B2bProductRequestMailTemplateInstaller($this->container))->install();
  194.     }
  195.     private function createPlatformMenus()
  196.     {
  197.         (new B2bProductListsPlatformMenuInstaller())->install($this->container->get(Connection::class));
  198.     }
  199.     private function uninstallPlatformMenus()
  200.     {
  201.         (new B2bProductListsPlatformMenuInstaller())->uninstall($this->container->get(Connection::class));
  202.     }
  203.     private function uninstallMailTemplates()
  204.     {
  205.         (new MailTemplateSetup($this->container))->uninstall();
  206.         (new B2bProductRequestMailTemplateInstaller($this->container))->uninstall();
  207.     }
  208.     private function deleteCustomFieldSets()
  209.     {
  210.         (new CustomFieldSetup($this->container))->uninstall();
  211.         (new B2bBonusProgramCustomFieldInstaller($this->container))->uninstall();
  212.         (new B2bProductRequestCustomFieldInstaller($this->container))->uninstall();
  213.         (new B2bUrlAuthenticationCustomFieldInstaller($this->container))->uninstall();
  214.     }
  215.     private function createEventActions(InstallContext $installContext)
  216.     {
  217.         (new EventActionSetup($this->container$installContext->getContext()))->install();
  218.     }
  219.     private function createFlowBuilder(InstallContext $installContext)
  220.     {
  221.         (new FlowBuilderSetup($this->container$installContext->getContext()))->install();
  222.     }
  223.     private function uninstallEventActions(UninstallContext $uninstallContext)
  224.     {
  225.         (new EventActionSetup($this->container$uninstallContext->getContext()))->uninstall();
  226.     }
  227.     private function uninstallFlowBuilder(InstallContext $installContext)
  228.     {
  229.         (new FlowBuilderSetup($this->container$installContext->getContext()))->uninstall();
  230.     }
  231.     private function installRules(InstallContext $context)
  232.     {
  233.         (new RuleSetup($this->container$context->getContext()))->install();
  234.     }
  235.     private function uninstallRules(UninstallContext $context)
  236.     {
  237.         (new RuleSetup($this->container$context->getContext()))->uninstall();
  238.     }
  239.     private function installDefaultConfigValues(InstallContext $context)
  240.     {
  241.         (new ProductListsConfigSetup($this->container$context->getContext()))->install();
  242.         (new CoreConfigSetup($this->container$context->getContext()))->install();
  243.     }
  244.     private function updateDefaultConfigValues(UpdateContext $context)
  245.     {
  246.         (new ProductListsConfigSetup($this->container$context->getContext()))->update();
  247.         (new CoreConfigSetup($this->container$context->getContext()))->update();
  248.     }
  249.     private function addPaymentMethod(Context $context)
  250.     {
  251.         /** @var EntityRepositoryInterface $paymentRepository */
  252.         $paymentRepository $this->container->get('payment_method.repository');
  253.         /** @var PluginIdProvider $pluginIdProvider */
  254.         $pluginIdProvider $this->container->get(PluginIdProvider::class);
  255.         $pluginId $pluginIdProvider->getPluginIdByBaseClass(self::class, $context);
  256.         $paymentMethod $this->getPaymentMethodId($context);
  257.         $paymentMethodId Uuid::randomHex();
  258.         if (!empty($paymentMethod)) {
  259.             $paymentMethodId $paymentMethod->getId();
  260.         }
  261.         $paymentMethodData = [
  262.             'id' => $paymentMethodId,
  263.             'handlerIdentifier' => PaymentConditionPayment::class,
  264.             'position' => 0,
  265.             'afterOrderEnabled' => false,
  266.             'active' => true,
  267.             'pluginId' => $pluginId,
  268.             'translations' => [
  269.                 'de-DE' => [
  270.                     'name' => 'Zahlungskondition',
  271.                     'description' => 'Kunden Zahlungskonditionen Zahlungsart.',
  272.                 ],
  273.                 'en-GB' => [
  274.                     'name' => 'Payment condition',
  275.                     'description' => 'Customer payment condition payment method.',
  276.                 ],
  277.             ],
  278.         ];
  279.         $paymentRepository->upsert([$paymentMethodData], $context);
  280.     }
  281.     private function getPaymentMethodId(Context $context)
  282.     {
  283.         /** @var EntityRepositoryInterface $paymentRepository */
  284.         $paymentRepository $this->container->get('payment_method.repository');
  285.         $criteria = new Criteria();
  286.         $criteria->addFilter(new EqualsFilter('handlerIdentifier'PaymentConditionPayment::class));
  287.         return $paymentRepository->search($criteria$context)->first();
  288.     }
  289.     public function getContainerExtension()
  290.     {
  291.         if (null === $this->extension) {
  292.             $this->extension = new B2bSellersCoreExtension();
  293.         }
  294.         return $this->extension;
  295.     }
  296.     public function build(ContainerBuilder $container): void
  297.     {
  298.         $loader = new YamlFileLoader($container, new FileLocator(__DIR__ '/Resources/config/packages/'));
  299.         $loader->load('shopware.yaml');
  300.         $loader->load('b2b_sellers_core.yaml');
  301.         parent::build($container);
  302.         $this->registerFeatureContainerFiles($container);
  303.     }
  304.     public function configureRoutes(RoutingConfigurator $routesstring $environment): void
  305.     {
  306.         parent::configureRoutes($routes$environment);
  307.         $this->registerFeatureRoutes($routes);
  308.     }
  309.     public function getAdditionalFeaturePath(): ?string
  310.     {
  311.         if (!$this->getPath()) {
  312.             return null;
  313.         }
  314.         return $this->getPath() . '/Components/AdditionalFeatures';
  315.     }
  316.     private function registerFeatureContainerFiles(ContainerBuilder $container): void
  317.     {
  318.         $fileLocator = new FileLocator($this->getPath());
  319.         $loaderResolver = new LoaderResolver([new XmlFileLoader($container$fileLocator),]);
  320.         $delegatingLoader = new DelegatingLoader($loaderResolver);
  321.         $licenceInformation $this->loadLicenceInformation($this->getProjectDir());
  322.         $features $licenceInformation['features'] ?? [];
  323.         $fileSystem = new Filesystem();
  324.         foreach ($features as $feature) {
  325.             $filePath $this->getAdditionalFeaturePath() . '/' $feature '/DependencyInjection/services.xml';
  326.             if ($fileSystem->exists($filePath)) {
  327.                 $delegatingLoader->load($filePath);
  328.             }
  329.         }
  330.     }
  331.     private function registerFeatureRoutes(RoutingConfigurator $routes): void
  332.     {
  333.         if (!$this->isActive()) {
  334.             return;
  335.         }
  336.         $licenceInformation $this->loadLicenceInformation($this->getProjectDir());
  337.         $features $licenceInformation['features'] ?? [];
  338.         $fileSystem = new Filesystem();
  339.         foreach ($features as $feature) {
  340.             $filePath $this->getAdditionalFeaturePath() . '/' $feature '/DependencyInjection/routes.xml';
  341.             if ($fileSystem->exists($filePath)) {
  342.                 $routes->import($filePath);
  343.             }
  344.         }
  345.     }
  346.     private function updatePluginConfig()
  347.     {
  348.         /** @var Connection $connection */
  349.         $connection $this->container->get(Connection::class);
  350.         $connection->update('system_config',
  351.             ['configuration_value' => json_encode(['_value' => ['customer']])],
  352.             ['configuration_key' => 'B2bSellersCore.config.orderMailRecipients']
  353.         );
  354.     }
  355.     /**
  356.      * @return Bundle[]
  357.      */
  358.     public function getAdditionalBundles(AdditionalBundleParameters $parameters): array
  359.     {
  360.         $this->synchronizeAutoloader(
  361.             $parameters->getClassLoader(),
  362.             $parameters->getKernelParameters()['kernel.project_dir']
  363.         );
  364.         $licence $this->loadLicenceInformation($parameters->getKernelParameters()['kernel.project_dir']);
  365.         if (empty($licence) || empty($licence['addons'])) {
  366.             return [];
  367.         }
  368.         $activeBundles $licence['addons'];
  369.         return array_filter($this->getAddonBundles(), function (Plugin $bundle) use ($activeBundles) {
  370.             return in_array((new \ReflectionClass($bundle))->getShortName(), $activeBundles);
  371.         });
  372.     }
  373.     /**
  374.      * @return Bundle[]
  375.      */
  376.     public function getAllAdditionalBundles(AdditionalBundleParameters $parameters): array
  377.     {
  378.         $this->synchronizeAutoloader(
  379.             $parameters->getClassLoader(),
  380.             $parameters->getKernelParameters()['kernel.project_dir']
  381.         );
  382.         return $this->getAddonBundles();
  383.     }
  384.     /**
  385.      * @return Plugin[]
  386.      */
  387.     private function getAddonBundles(): array
  388.     {
  389.         if (!empty($this->addonBundles)) {
  390.             return $this->addonBundles;
  391.         }
  392.         $projectDir $this->getProjectDir();
  393.         $this->addonBundles = [];
  394.         foreach (self::AVAILABLE_ADDONS as $bundle) {
  395.             $addonBundle = new $bundle(true$this->getBasePath(), $projectDir);
  396.             $addonBundle->setContainer($this->container);
  397.             $this->addonBundles[] = $addonBundle;
  398.         }
  399.         return $this->addonBundles;
  400.     }
  401.     private function installAddonBundles(InstallContext $context): void
  402.     {
  403.         $this->addonBundles = [];
  404.         foreach ($this->getAddonBundles() as $b2bBundle) {
  405.             $b2bBundle->install($context);
  406.         }
  407.     }
  408.     private function updateAddonBundles(UpdateContext $context): void
  409.     {
  410.         $this->addonBundles = [];
  411.         foreach ($this->getAddonBundles() as $b2bBundle) {
  412.             $b2bBundle->update($context);
  413.         }
  414.     }
  415.     private function uninstallAddonBundles(UninstallContext $context): void
  416.     {
  417.         $this->addonBundles = [];
  418.         foreach ($this->getAddonBundles() as $b2bBundle) {
  419.             $b2bBundle->uninstall($context);
  420.         }
  421.     }
  422.     private function loadLicenceInformation($projectDir): array
  423.     {
  424.         $filePath $projectDir '/var/b2b-licence.json';
  425.         if (!file_exists($filePath)) {
  426.             $this->createLicenceFile($projectDir);
  427.             return $this->loadLicenceInformation($projectDir);
  428.         }
  429.         return json_decode(file_get_contents($filePath), true);
  430.     }
  431.     private function getProjectDir(): string
  432.     {
  433.         if (mb_strpos($this->getBasePath(), '/vendor') !== false) {
  434.             return substr($this->getBasePath(), 0mb_strpos($this->getBasePath(), '/vendor')) . '/';
  435.         }
  436.         return substr($this->getBasePath(), 0mb_strpos($this->getBasePath(), '/custom/plugins')) . '/';
  437.     }
  438.     private function createLicenceFile(string $projectDir): void
  439.     {
  440.         $filePath $projectDir self::LICENCE_FILE_PATH;
  441.         if (file_exists($filePath)) {
  442.             return;
  443.         }
  444.         file_put_contents($filePathjson_encode([
  445.             'licenceId' => '''licenceDomain' => '''restrictions' => ['salesRepresentativeLimit' => 5], 'addons' => [], 'features' => []
  446.         ]));
  447.     }
  448.     private function synchronizeAutoloader(ClassLoader $classLoaderstring $projectDir)
  449.     {
  450.         /** @var Kernel $kernelClass */
  451.         $kernelClass EnvironmentHelper::getVariable('KERNEL_CLASS'Kernel::class);
  452.         $connection $kernelClass::getConnection();
  453.         $composerJson json_decode(file_get_contents(__DIR__ '/../composer.json'), true);
  454.         $autoload $composerJson['autoload'];
  455.         $autoloadPsr4 $autoload['psr-4'];
  456.         $synchronized true;
  457.         $missingAutoload = [];
  458.         foreach ($autoloadPsr4 as $namespace => $dir) {
  459.             if (!array_key_exists($namespace$classLoader->getPrefixesPsr4())) {
  460.                 $missingAutoload[$namespace] = $dir;
  461.                 $synchronized false;
  462.             }
  463.         }
  464.         if ($synchronized) {
  465.             return;
  466.         }
  467.         $this->temporarilyRegisterClasses($classLoader$missingAutoload$this->getBasePath(), $projectDir);
  468.         $connection->update(
  469.             'plugin',
  470.             ['autoload' => json_encode($autoload)],
  471.             ['name' => $this->getName()]
  472.         );
  473.     }
  474.     private function temporarilyRegisterClasses(ClassLoader $classLoader, array $psr4string $pluginPathstring $projectDir)
  475.     {
  476.         foreach ($psr4 as $namespace => $paths) {
  477.             if (\is_string($paths)) {
  478.                 $paths = [$paths];
  479.             }
  480.             $mappedPaths $this->mapPsrPaths($paths$projectDir$pluginPath);
  481.             $classLoader->addPsr4($namespace$mappedPaths);
  482.             if ($classLoader->isClassMapAuthoritative()) {
  483.                 foreach ($mappedPaths as $mappedPath) {
  484.                     $classLoader->addClassMap(ClassMapGenerator::createMap($mappedPath));
  485.                 }
  486.             }
  487.         }
  488.     }
  489.     private function mapPsrPaths(array $psrstring $projectDirstring $pluginRootPath): array
  490.     {
  491.         $mappedPaths = [];
  492.         $absolutePluginRootPath $this->getAbsolutePluginRootPath($projectDir$pluginRootPath);
  493.         foreach ($psr as $path) {
  494.             $mappedPaths[] = $absolutePluginRootPath '/' $path;
  495.         }
  496.         return $mappedPaths;
  497.     }
  498.     private function getAbsolutePluginRootPath(string $projectDirstring $pluginRootPath): string
  499.     {
  500.         if (mb_strpos($pluginRootPath'/') !== 0) {
  501.             $pluginRootPath $projectDir '/' $pluginRootPath;
  502.         }
  503.         return $pluginRootPath;
  504.     }
  505. }