diff --git a/.github/workflows/code-analyse.yml b/.github/workflows/code-analyse.yml new file mode 100644 index 0000000..9b7ef25 --- /dev/null +++ b/.github/workflows/code-analyse.yml @@ -0,0 +1,30 @@ +name: "Code Analysis" + +on: + - "push" + - "pull_request" + +jobs: + analysis: + + runs-on: "ubuntu-latest" + + steps: + - uses: "actions/checkout@v3" + + - name: "Setup PHP Action" + uses: "shivammathur/setup-php@v2" + with: + php-version: "8.0" + extensions: "intl, xdebug, sodium, gd, pdo, curl, iconv, openssl" + + - name: "Composer Install" + uses: "ramsey/composer-install@v2" + with: + composer-options: "--prefer-dist --no-progress" + + - name: "PHPStan" + run: "vendor/bin/phpstan" + + - name: "PHPCS" + run: "vendor/bin/phpcs" diff --git a/Command/ConfigSynchronizeCommand.php b/Command/ConfigSynchronizeCommand.php index 71932ff..c275743 100644 --- a/Command/ConfigSynchronizeCommand.php +++ b/Command/ConfigSynchronizeCommand.php @@ -7,6 +7,7 @@ use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +use Shopware\Core\System\SalesChannel\SalesChannelEntity; use Shopware\Core\System\SystemConfig\SystemConfigService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -14,53 +15,56 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Yaml\Yaml; +use function array_key_exists; +use function assert; +use function file_exists; +use function implode; +use function is_array; +use function sprintf; + class ConfigSynchronizeCommand extends Command { - protected static $defaultName = 'config:sync'; + // phpcs:ignore + protected static $defaultName = 'config:sync'; private static string $defaultScope = 'global'; - private string $projectDir; - private SystemConfigService $systemConfigService; - private EntityRepositoryInterface $salesChannelRepository; private OutputInterface $output; public function __construct( - string $projectDir, - SystemConfigService $systemConfigService, - EntityRepositoryInterface $salesChannelRepository + private string $projectDir, + private SystemConfigService $systemConfigService, + private EntityRepositoryInterface $salesChannelRepository, ) { - $this->projectDir = $projectDir; - $this->systemConfigService = $systemConfigService; - $this->salesChannelRepository = $salesChannelRepository; parent::__construct(); } protected function configure(): void { parent::configure(); + $this->setDescription('Update system config like defined in file config/config.yaml'); $this->addArgument( 'config_path', InputArgument::OPTIONAL, 'Path to config yaml', - 'config/config.yaml' + 'config/config.yaml', ); } protected function execute( InputInterface $input, - OutputInterface $output + OutputInterface $output, ): int { $this->output = $output; - $configPath = $input->getArgument('config_path'); - $filePath = $this->projectDir . '/' . $configPath; - if (!file_exists($filePath)) { + $configPath = $input->getArgument('config_path'); + $filePath = $this->projectDir . '/' . $configPath; + if (! file_exists($filePath)) { $this->output->writeln(sprintf('%s not found', $filePath)); return self::FAILURE; } $yamlReader = new Yaml(); - $yaml = $yamlReader::parseFile($filePath); + $yaml = $yamlReader::parseFile($filePath); $this->executeGlobalConfigSync($yaml); $this->executeSalesChannelConfigSync($yaml); @@ -68,23 +72,27 @@ protected function execute( return self::SUCCESS; } + /** @param array $yaml */ private function executeGlobalConfigSync(array $yaml): void { - if (isset($yaml[self::$defaultScope])) { - $this->output->writeln('---------------------------------------'); - $this->executeConfigSet($yaml[self::$defaultScope], self::$defaultScope); - $this->output->writeln('---------------------------------------'); + if (! isset($yaml[self::$defaultScope])) { + return; } + + $this->output->writeln('---------------------------------------'); + $this->executeConfigSet($yaml[self::$defaultScope], self::$defaultScope); + $this->output->writeln('---------------------------------------'); } - private function executeConfigSet(array $config, string $name, ?string $salesChannelId = null): void + /** @param array $config */ + private function executeConfigSet(array $config, string $name, string|null $salesChannelId = null): void { $this->output->writeln(sprintf('Current config scope: "%s"', $name)); $this->output->writeln('---------------------------------------'); foreach ($config as $key => $value) { - $currentValue = $this->systemConfigService->get($key, $salesChannelId); + $currentValue = $this->systemConfigService->get($key, $salesChannelId); $currentValueAsString = $this->valueToString($currentValue); - $valueAsString = $this->valueToString($value); + $valueAsString = $this->valueToString($value); $this->output->writeln(sprintf('Current value: "%s" for key: "%s"', $currentValueAsString, $key)); // using string comparison for all values (array|bool|float|int|string|null) simplified if ($currentValueAsString !== $valueAsString) { @@ -96,10 +104,11 @@ private function executeConfigSet(array $config, string $name, ?string $salesCha } } + /** @param array $yaml */ private function executeSalesChannelConfigSync(array $yaml): void { $salesChannelUpdated = $salesChannelNotUpdated = []; - $salesChannels = $this->getSalesChannels(); + $salesChannels = $this->getSalesChannels(); foreach ($salesChannels as $name => $salesChannelId) { if (isset($yaml[$name])) { $this->executeConfigSet($yaml[$name], $name, $salesChannelId); @@ -109,26 +118,31 @@ private function executeSalesChannelConfigSync(array $yaml): void $salesChannelNotUpdated[$salesChannelId] = $name; } } + // put info message only if the salesChannel was totally not updated, // we get a salesChannel for every translation, to work in yaml file with the name of the translation foreach ($salesChannelNotUpdated as $idNotUpdated => $name) { - if (array_key_exists($idNotUpdated, $salesChannelUpdated) === false) { - $this->output->writeln( - sprintf('>>> No config update for SalesChannel with id: "%s" <<<', $idNotUpdated) - ); + if (array_key_exists($idNotUpdated, $salesChannelUpdated) !== false) { + continue; } + + $this->output->writeln( + sprintf('>>> No config update for SalesChannel with id: "%s" <<<', $idNotUpdated), + ); } } + /** @return array */ private function getSalesChannels(): array { $salesChannels = []; - $criteria = new Criteria(); + $criteria = new Criteria(); $criteria->addAssociation('translations'); $salesChannelIds = $this->salesChannelRepository->search($criteria, Context::createDefaultContext()); foreach ($salesChannelIds->getEntities()->getElements() as $salesChannel) { - foreach ($salesChannel->getTranslations()->getElements() as $translation) { + assert($salesChannel instanceof SalesChannelEntity); + foreach ($salesChannel->getTranslations()?->getElements() ?? [] as $translation) { $salesChannels[$translation->getName()] = $translation->getSalesChannelId(); } } @@ -136,7 +150,8 @@ private function getSalesChannels(): array return $salesChannels; } - private function valueToString($value): string + /** @param scalar|list|null $value */ + private function valueToString(string|int|float|bool|array|null $value): string { if (is_array($value)) { return implode(', ', $value); diff --git a/Command/PluginSynchronizeCommand.php b/Command/PluginSynchronizeCommand.php index 0abbb30..13b7e1c 100644 --- a/Command/PluginSynchronizeCommand.php +++ b/Command/PluginSynchronizeCommand.php @@ -5,47 +5,52 @@ namespace Flagbit\Shopware\ShopwareMaintenance\Command; use Psr\Log\LoggerInterface; +use RuntimeException; use Shopware\Core\Framework\Plugin\Exception\PluginBaseClassNotFoundException; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use function array_filter; +use function array_keys; +use function array_sum; +use function file_exists; +use function sprintf; + class PluginSynchronizeCommand extends Command { + // phpcs:ignore protected static $defaultName = 'plugin:sync'; - private string $projectDir; - private LoggerInterface $logger; - public function __construct( - string $projectDir, - LoggerInterface $logger + private string $projectDir, + private LoggerInterface $logger, ) { parent::__construct(); - $this->projectDir = $projectDir; - $this->logger = $logger; } protected function configure(): void { parent::configure(); + $this->setDescription('Install/uninstall plugins as defined in file config/plugins.php'); } protected function execute(InputInterface $input, OutputInterface $output): int { - if (!file_exists($this->projectDir . '/config/plugins.php')) { + if (! file_exists($this->projectDir . '/config/plugins.php')) { $output->writeln(sprintf('%s not found', $this->projectDir . '/config/plugins.php')); return 1; } - $plugins = require $this->projectDir . '/config/plugins.php'; - $disabledPlugins = array_keys(array_filter($plugins, function ($isEnabled) { + $plugins = require $this->projectDir . '/config/plugins.php'; + $disabledPlugins = array_keys(array_filter($plugins, static function ($isEnabled) { return $isEnabled === false; })); - $enabledPlugins = array_keys(array_filter($plugins, function ($isEnabled) { + $enabledPlugins = array_keys(array_filter($plugins, static function ($isEnabled) { return $isEnabled === true; })); @@ -60,6 +65,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($enabledPlugins as $enabledPlugin) { $installFailed[$enabledPlugin] = $this->executePluginInstall($enabledPlugin, $output); } + $errorSum = (int) array_sum($installFailed); if ($errorSum > self::SUCCESS) { return self::FAILURE; @@ -69,17 +75,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int } /** - * @param array $parameters - * @param \Symfony\Component\Console\Output\OutputInterface $output + * @param array $parameters * - * @return int - * @throws \Symfony\Component\Console\Exception\ExceptionInterface + * @throws ExceptionInterface */ private function runCommand(array $parameters, OutputInterface $output): int { $application = $this->getApplication(); if ($application === null) { - throw new \RuntimeException('No application initialised'); + throw new RuntimeException('No application initialised'); } $output->writeln(''); @@ -101,8 +105,9 @@ private function executePluginInstall(string $enabledPlugin, OutputInterface $ou 'plugins' => [$enabledPlugin], '--activate' => true, ], $output); - } catch (PluginBaseClassNotFoundException $baseClassNotFoundException) { + } catch (PluginBaseClassNotFoundException) { $this->logger->error('Execute plugin:refresh before plugin:sync !'); + return self::FAILURE; } diff --git a/DependencyInjection/ShopwareMaintenanceExtension.php b/DependencyInjection/ShopwareMaintenanceExtension.php index 52e90d2..1cfe9d7 100644 --- a/DependencyInjection/ShopwareMaintenanceExtension.php +++ b/DependencyInjection/ShopwareMaintenanceExtension.php @@ -11,9 +11,10 @@ class ShopwareMaintenanceExtension extends Extension { + /** @param array $configs */ public function load(array $configs, ContainerBuilder $container): void { - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.xml'); } } diff --git a/ShopwareMaintenance.php b/ShopwareMaintenance.php index 737828e..434df1f 100644 --- a/ShopwareMaintenance.php +++ b/ShopwareMaintenance.php @@ -1,10 +1,11 @@ + + + + + + + Command + DependencyInjection + ShopwareMaintenance.php + + + + + + + + + + + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..f1faec9 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,7 @@ +parameters: + level: 8 + paths: + - "%currentWorkingDirectory%/" + excludePaths: + - "%currentWorkingDirectory%/vendor/" +