From 0aef4a9d90b68b3ee8d7df8d525454bf399f785d Mon Sep 17 00:00:00 2001 From: Shyim Date: Fri, 10 Nov 2023 22:36:56 +0100 Subject: [PATCH] feat: add phpstan (#231) --- .github/workflows/code-style.yml | 2 + composer.json | 85 ++++++++++--------- src/Command/ChangeUserPasswordCommand.php | 4 + src/Command/DevRobotsTxtCommand.php | 9 +- src/Command/MonitorCommand.php | 8 +- src/Components/CacheAdapter.php | 3 +- src/Components/CacheRegistry.php | 3 + .../Elasticsearch/ElasticsearchManager.php | 19 ++++- .../Environment/EnvironmentFile.php | 8 +- .../Environment/EnvironmentManager.php | 5 +- .../Checker/HealthChecker/MysqlChecker.php | 3 + .../Checker/HealthChecker/PhpChecker.php | 12 --- .../Checker/HealthChecker/TaskChecker.php | 1 + .../PerformanceChecker/MySQL8Checker.php | 5 +- src/Components/LineReader.php | 28 ++++-- .../SystemConfig/ConfigSystemConfigLoader.php | 14 ++- src/Controller/LogController.php | 6 +- src/Controller/QueueController.php | 10 +++ src/Controller/ScheduledTaskController.php | 15 +++- src/Controller/ShopwareFilesController.php | 18 ++-- src/Controller/StateMachineController.php | 26 +++--- src/DependencyInjection/Configuration.php | 1 + .../FroshToolsExtension.php | 10 +++ 23 files changed, 200 insertions(+), 95 deletions(-) diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml index e78d6fe..d91e2a7 100644 --- a/.github/workflows/code-style.yml +++ b/.github/workflows/code-style.yml @@ -8,3 +8,5 @@ on: jobs: php-cs-fixer: uses: FriendsOfShopware/actions/.github/workflows/php-cs-fixer.yml@main + phpstan: + uses: FriendsOfShopware/actions/.github/workflows/phpstan.yml@main diff --git a/composer.json b/composer.json index fa13a41..96e2f9c 100644 --- a/composer.json +++ b/composer.json @@ -1,48 +1,49 @@ { - "name": "frosh/tools", - "version": "1.2.3", - "description": "Provides some basic things for managing the Shopware Installation", - "type": "shopware-platform-plugin", - "license": "MIT", - "autoload": { - "psr-4": { - "Frosh\\Tools\\": "src/" - } - }, - "authors": [ - { - "name": "FriendsOfShopware", - "homepage": "https://friendsofshopware.com" - } - ], - "extra": { - "shopware-plugin-class": "Frosh\\Tools\\FroshTools", - "label": { - "de-DE": "Tools", - "en-GB": "Tools" + "name": "frosh/tools", + "version": "1.2.3", + "description": "Provides some basic things for managing the Shopware Installation", + "type": "shopware-platform-plugin", + "license": "MIT", + "autoload": { + "psr-4": { + "Frosh\\Tools\\": "src/" + } }, - "description": { - "de-DE": "Die kostenlose Shopware 6 App beinhaltet mehrere Werkzeuge um den Shop besser zu verwalten. Optimierter Cache Manager, Log Viewer um die Logeinträge zu sehen oder Tasks auszuführen.", - "en-GB": "The free Shopware 6 app include several tools to manage the store much better like a cache manager, log viewer to see the log entries, run the tasks or see the system status." + "authors": [ + { + "name": "FriendsOfShopware", + "homepage": "https://friendsofshopware.com" + } + ], + "extra": { + "shopware-plugin-class": "Frosh\\Tools\\FroshTools", + "label": { + "de-DE": "Tools", + "en-GB": "Tools" + }, + "description": { + "de-DE": "Die kostenlose Shopware 6 App beinhaltet mehrere Werkzeuge um den Shop besser zu verwalten. Optimierter Cache Manager, Log Viewer um die Logeinträge zu sehen oder Tasks auszuführen.", + "en-GB": "The free Shopware 6 app include several tools to manage the store much better like a cache manager, log viewer to see the log entries, run the tasks or see the system status." + }, + "manufacturerLink": { + "de-DE": "https://github.com/FriendsOfShopware/FroshTools", + "en-GB": "https://github.com/FriendsOfShopware/FroshTools" + }, + "supportLink": { + "de-DE": "https://github.com/FriendsOfShopware/FroshTools/issues", + "en-GB": "https://github.com/FriendsOfShopware/FroshTools/issues" + } }, - "manufacturerLink": { - "de-DE": "https://github.com/FriendsOfShopware/FroshTools", - "en-GB": "https://github.com/FriendsOfShopware/FroshTools" + "require": { + "shopware/core": "~6.5.2" }, - "supportLink": { - "de-DE": "https://github.com/FriendsOfShopware/FroshTools/issues", - "en-GB": "https://github.com/FriendsOfShopware/FroshTools/issues" - } - }, - "require": { - "shopware/core": "~6.5.2" - }, - "config": { - "allow-plugins": { - "symfony/runtime": true + "config": { + "allow-plugins": { + "symfony/runtime": true + } + }, + "scripts": { + "cs-fix": "docker run --rm -v $(pwd):$(pwd) -w $(pwd) oskarstark/php-cs-fixer-ga --rules @PER-CS2.0,@PER-CS2.0:risky --allow-risky=yes .", + "phpstan": "docker run --rm -v $(pwd):/app aragon999/phpstan-shopware:v6.5.6 analyse --level max ." } - }, - "scripts": { - "cs-fix": "docker run --rm -v $(pwd):$(pwd) -w $(pwd) oskarstark/php-cs-fixer-ga --rules @PER-CS2.0,@PER-CS2.0:risky --allow-risky=yes ." - } } diff --git a/src/Command/ChangeUserPasswordCommand.php b/src/Command/ChangeUserPasswordCommand.php index e41c2d1..f27c801 100644 --- a/src/Command/ChangeUserPasswordCommand.php +++ b/src/Command/ChangeUserPasswordCommand.php @@ -8,6 +8,7 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +use Shopware\Core\System\User\UserCollection; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -18,6 +19,9 @@ #[AsCommand('frosh:user:change:password', 'Change user password')] class ChangeUserPasswordCommand extends Command { + /** + * @param EntityRepository $userRepository + */ public function __construct(private readonly EntityRepository $userRepository) { parent::__construct(); diff --git a/src/Command/DevRobotsTxtCommand.php b/src/Command/DevRobotsTxtCommand.php index 6b7c15f..539d47b 100644 --- a/src/Command/DevRobotsTxtCommand.php +++ b/src/Command/DevRobotsTxtCommand.php @@ -51,6 +51,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $robotsFile = fopen($robotsPath, 'wb'); + \assert($robotsFile !== false); + $robotsContent = "#soc\nUser-agent: *\nDisallow: /\n#eoc"; fwrite($robotsFile, $robotsContent); fclose($robotsFile); @@ -60,11 +62,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int return self::SUCCESS; } - private function revertToOriginal($input, $output, $robotsPath): int + private function revertToOriginal(InputInterface $input, OutputInterface $output, string $robotsPath): int { // returns robots.txt to original state $io = new SymfonyStyle($input, $output); - $file = file_get_contents($robotsPath); + + $file = (string) file_get_contents($robotsPath); $createdString = "#soc\nUser-agent: *\nDisallow: /\n#eoc"; // If only input from command is present @@ -89,7 +92,7 @@ private function changeRobotsTxt(InputInterface $input, OutputInterface $output, // change robots.txt to disable crawlers $io = new SymfonyStyle($input, $output); - $file = file_get_contents($robotsPath); + $file = (string) file_get_contents($robotsPath); $commandString = "#soc\nUser-agent: *\nDisallow: /\n#eoc"; // If command is called multiple times diff --git a/src/Command/MonitorCommand.php b/src/Command/MonitorCommand.php index 79169fc..d99237e 100644 --- a/src/Command/MonitorCommand.php +++ b/src/Command/MonitorCommand.php @@ -13,6 +13,7 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter; +use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskCollection; use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskDefinition; use Shopware\Core\System\SystemConfig\SystemConfigService; use Symfony\Component\Console\Attribute\AsCommand; @@ -30,6 +31,9 @@ class MonitorCommand extends Command private const MONITOR_EMAIL_OPTION = 'email'; private const MONITOR_SALESCHANNEL_ARG = 'sales-channel'; + /** + * @param EntityRepository $scheduledTaskRepository + */ public function __construct( #[Autowire(service: MailService::class)] private readonly AbstractMailService $mailService, @@ -103,7 +107,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int private function queueFailed(): bool { - $oldestMessage = (int) $this->connection->fetchOne('SELECT IFNULL(MIN(created_at), 0) FROM messenger_messages'); + /** @var string $createdAt */ + $createdAt = $this->connection->fetchOne('SELECT IFNULL(MIN(created_at), 0) FROM messenger_messages'); + $oldestMessage = (int)$createdAt; $oldestMessage /= 10000; $minutes = $this->configService->getInt( 'FroshTools.config.monitorQueueGraceTime' diff --git a/src/Components/CacheAdapter.php b/src/Components/CacheAdapter.php index a56f0b6..30eb001 100644 --- a/src/Components/CacheAdapter.php +++ b/src/Components/CacheAdapter.php @@ -121,11 +121,12 @@ private function getCacheAdapter(AdapterInterface $adapter): AdapterInterface return $adapter; } - private function getRedis(AdapterInterface $adapter): ?\Redis + private function getRedis(AdapterInterface $adapter): \Redis { if ($adapter instanceof RedisTagAwareAdapter) { $redisProxyGetter = \Closure::bind(fn() => $adapter->redis, $adapter, RedisTagAwareAdapter::class); } else { + // @phpstan-ignore-next-line $redisProxyGetter = \Closure::bind(fn() => $adapter->redis, $adapter, RedisAdapter::class); } diff --git a/src/Components/CacheRegistry.php b/src/Components/CacheRegistry.php index 6310037..5c67fc3 100644 --- a/src/Components/CacheRegistry.php +++ b/src/Components/CacheRegistry.php @@ -16,6 +16,9 @@ public function addAdapter(string $name, CacheAdapter $adapter): void $this->adapters[$name] = $adapter; } + /** + * @return CacheAdapter[] + */ public function all(): array { return $this->adapters; diff --git a/src/Components/Elasticsearch/ElasticsearchManager.php b/src/Components/Elasticsearch/ElasticsearchManager.php index 22aab2a..0745f70 100644 --- a/src/Components/Elasticsearch/ElasticsearchManager.php +++ b/src/Components/Elasticsearch/ElasticsearchManager.php @@ -35,6 +35,9 @@ public function isEnabled(): bool return $this->enabled; } + /** + * @return array + */ public function info(): array { return [ @@ -43,6 +46,9 @@ public function info(): array ]; } + /** + * @return array + */ public function indices(): array { $indices = $this->client->indices()->get(['index' => '*']); @@ -64,12 +70,21 @@ public function indices(): array return $list; } + /** + * @return array + */ public function deleteIndex(string $name): array { return $this->client->indices()->delete(['index' => $name]); } - public function proxy(string $method, string $path, array $params, array $body): array + /** + * @param array $body + * @param array $params + * + * @return array|callable + */ + public function proxy(string $method, string $path, array $params, array $body): array|callable { if ($body === []) { $body = null; @@ -101,7 +116,7 @@ public function switchAlias(): void public function deleteUnusedIndices(): void { - $indices = $this->outdatedIndexDetector->get(); + $indices = $this->outdatedIndexDetector->get() ?? []; foreach ($indices as $index) { $this->client->indices()->delete(['index' => $index]); diff --git a/src/Components/Environment/EnvironmentFile.php b/src/Components/Environment/EnvironmentFile.php index f34d37a..90ee898 100644 --- a/src/Components/Environment/EnvironmentFile.php +++ b/src/Components/Environment/EnvironmentFile.php @@ -7,7 +7,7 @@ class EnvironmentFile implements \Stringable { /** - * @param array $items + * @param list $items */ public function __construct(private array $items) {} @@ -60,6 +60,9 @@ public function delete(string $key): void } } + /** + * @return array + */ public function keys(): array { $keys = []; @@ -73,6 +76,9 @@ public function keys(): array return $keys; } + /** + * @return array + */ public function values(): array { $values = []; diff --git a/src/Components/Environment/EnvironmentManager.php b/src/Components/Environment/EnvironmentManager.php index 1d48fdd..b10dc07 100644 --- a/src/Components/Environment/EnvironmentManager.php +++ b/src/Components/Environment/EnvironmentManager.php @@ -13,11 +13,12 @@ public function read(string $path): EnvironmentFile throw new \RuntimeException(sprintf('Cannot read file %s', $path)); } + /** @var array $lines */ $lines = preg_split('/\r\n|\r|\n/', $content); $parsedLines = []; - $lineCount = (is_countable($lines) ? \count($lines) : 0) - 1; + $lineCount = \count($lines) - 1; foreach ($lines as $line) { - $line = trim((string) $line); + $line = trim($line); if ($line === '' || $line[0] === '#') { $parsedLines[] = EnvironmentCommentLine::parse($line); diff --git a/src/Components/Health/Checker/HealthChecker/MysqlChecker.php b/src/Components/Health/Checker/HealthChecker/MysqlChecker.php index de1b952..79675f5 100644 --- a/src/Components/Health/Checker/HealthChecker/MysqlChecker.php +++ b/src/Components/Health/Checker/HealthChecker/MysqlChecker.php @@ -94,6 +94,9 @@ private function checkMysqlVersion(HealthCollection $collection, string $version )); } + /** + * @return array{mysql?: string, mariadb?: string} + */ private function extract(string $versionString): array { if (mb_stripos($versionString, 'mariadb') === false) { diff --git a/src/Components/Health/Checker/HealthChecker/PhpChecker.php b/src/Components/Health/Checker/HealthChecker/PhpChecker.php index df43772..1640873 100644 --- a/src/Components/Health/Checker/HealthChecker/PhpChecker.php +++ b/src/Components/Health/Checker/HealthChecker/PhpChecker.php @@ -83,18 +83,6 @@ private function checkMemoryLimit(HealthCollection $collection): void { $minMemoryLimit = $this->parseQuantity('512m'); $currentMemoryLimit = \ini_get('memory_limit'); - if ($currentMemoryLimit === false) { - $collection->add( - SettingsResult::error( - 'php-memory-limit', - 'Memory-Limit', - 'unknown', - 'min ' . $this->formatSize($minMemoryLimit) - ) - ); - - return; - } $currentMemoryLimit = $this->parseQuantity($currentMemoryLimit); if ($currentMemoryLimit < $minMemoryLimit) { diff --git a/src/Components/Health/Checker/HealthChecker/TaskChecker.php b/src/Components/Health/Checker/HealthChecker/TaskChecker.php index 62c2bec..3b7a9e4 100644 --- a/src/Components/Health/Checker/HealthChecker/TaskChecker.php +++ b/src/Components/Health/Checker/HealthChecker/TaskChecker.php @@ -19,6 +19,7 @@ public function __construct( public function collect(HealthCollection $collection): void { + /** @var array{scheduled_task_class: class-string, next_execution_time: string}[] $data */ $data = $this->connection->createQueryBuilder() ->select('s.scheduled_task_class', 's.next_execution_time') ->from('scheduled_task', 's') diff --git a/src/Components/Health/Checker/PerformanceChecker/MySQL8Checker.php b/src/Components/Health/Checker/PerformanceChecker/MySQL8Checker.php index e877daf..91f7bff 100644 --- a/src/Components/Health/Checker/PerformanceChecker/MySQL8Checker.php +++ b/src/Components/Health/Checker/PerformanceChecker/MySQL8Checker.php @@ -30,7 +30,7 @@ public function collect(HealthCollection $collection): void return; } - if (version_compare($extractedVersion['mysql'], '8.0.0', '>=')) { + if (version_compare($extractedVersion['mysql'] ?? '', '8.0.0', '>=')) { return; } @@ -39,6 +39,9 @@ public function collect(HealthCollection $collection): void ); } + /** + * @return array{mysql?: string, mariadb?: string} + */ private function extract(string $versionString): array { if (mb_stripos($versionString, 'mariadb') === false) { diff --git a/src/Components/LineReader.php b/src/Components/LineReader.php index cf8ae51..0544655 100644 --- a/src/Components/LineReader.php +++ b/src/Components/LineReader.php @@ -16,11 +16,13 @@ final class LineReader private function __construct() {} /** + * @return \Generator + * * @throws \InvalidArgumentException if $filePath is not readable */ public static function readLines(string $filePath): \Generator { - if (!$fh = @fopen($filePath, 'rb')) { + if (!$fh = @fopen($filePath, 'r')) { throw new \InvalidArgumentException('Cannot open file for reading: ' . $filePath); } @@ -28,17 +30,19 @@ public static function readLines(string $filePath): \Generator } /** + * @return \Generator + * * @throws \InvalidArgumentException if $filePath is not readable */ public static function readLinesBackwards(string $filePath): \Generator { - if (!$fh = @fopen($filePath, 'rb')) { + if (!$fh = @fopen($filePath, 'r')) { throw new \InvalidArgumentException('Cannot open file for reading: ' . $filePath); } $size = filesize($filePath); - if (!$size) { - throw new \InvalidArgumentException('Cannot read file size: ' . $filePath); + if (!\is_int($size)) { + throw new \RuntimeException('Could not get file size'); } return self::readBackwards($fh, $size); @@ -66,6 +70,8 @@ private static function read($fh): \Generator * @see http://stackoverflow.com/a/10494801/147634 * * @param resource $fh + * + * @return \Generator */ private static function readBackwards($fh, int $pos): \Generator { @@ -79,13 +85,11 @@ private static function readBackwards($fh, int $pos): \Generator while (true) { if (isset($buffer[1])) { // faster than count($buffer) > 1 yield array_pop($buffer); - continue; } - if ($pos === 0) { + if ($pos === 0 && \is_array($buffer)) { yield array_pop($buffer); - break; } @@ -96,11 +100,17 @@ private static function readBackwards($fh, int $pos): \Generator $pos -= $bufferSize; } fseek($fh, $pos); + if ($bufferSize < 0) { + throw new \RuntimeException('Buffer size cannot be negative'); + } $chunk = fread($fh, $bufferSize); + if (!\is_string($chunk)) { + throw new \RuntimeException('Could not read file'); + } if ($buffer === null) { // remove single trailing newline, rtrim cannot be used here - if (mb_substr($chunk, -1) === "\n") { - $chunk = mb_substr($chunk, 0, -1); + if (substr($chunk, -1) === "\n") { + $chunk = substr($chunk, 0, -1); } $buffer = explode("\n", $chunk); } else { diff --git a/src/Components/SystemConfig/ConfigSystemConfigLoader.php b/src/Components/SystemConfig/ConfigSystemConfigLoader.php index 7f3234a..e0f8ce6 100644 --- a/src/Components/SystemConfig/ConfigSystemConfigLoader.php +++ b/src/Components/SystemConfig/ConfigSystemConfigLoader.php @@ -12,6 +12,9 @@ )] class ConfigSystemConfigLoader extends AbstractSystemConfigLoader { + /** + * @param array|bool|float|int|string|null>> $config + */ public function __construct(private readonly AbstractSystemConfigLoader $decorated, #[Autowire('%frosh_tools.system_config%')] private readonly array $config) {} public function getDecorated(): AbstractSystemConfigLoader @@ -19,6 +22,9 @@ public function getDecorated(): AbstractSystemConfigLoader return $this->decorated; } + /** + * @return array + */ public function load(?string $salesChannelId): array { $config = $this->decorated->load($salesChannelId); @@ -40,10 +46,15 @@ public function load(?string $salesChannelId): array } /** - * @param array|bool|float|int|string|null $value + * @param array $configValues + * @param array $keys + * @param array|bool|float|int|string|null $value + * + * @return array */ private function getSubArray(array $configValues, array $keys, mixed $value): array { + /** @var string $key */ $key = \array_shift($keys); if (empty($keys)) { @@ -61,6 +72,7 @@ private function getSubArray(array $configValues, array $keys, mixed $value): ar $configValues[$key] = []; } + // @phpstan-ignore-next-line $configValues[$key] = $this->getSubArray($configValues[$key], $keys, $value); } diff --git a/src/Controller/LogController.php b/src/Controller/LogController.php index 3d0f6ce..2a5a9bb 100644 --- a/src/Controller/LogController.php +++ b/src/Controller/LogController.php @@ -51,8 +51,9 @@ public function getLog(Request $request): Response $result = []; + /** @var string $item */ foreach ($reader as $item) { - if (preg_match(self::LINE_MATCH, (string) $item, $matches) === false) { + if (preg_match(self::LINE_MATCH, $item, $matches) === false) { $result[] = [ 'message' => $item, 'channel' => 'unknown', @@ -91,6 +92,9 @@ private function getFilePathByBag(Request $request): string return $this->logDir . $fileName; } + /** + * @return array{name: string}[] + */ private function getFiles(): array { $finder = new Finder(); diff --git a/src/Controller/QueueController.php b/src/Controller/QueueController.php index e007796..2e6c5be 100644 --- a/src/Controller/QueueController.php +++ b/src/Controller/QueueController.php @@ -12,11 +12,15 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface; +use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; use Symfony\Component\Routing\Annotation\Route; #[Route(path: '/api/_action/frosh-tools', defaults: ['_routeScope' => ['api'], '_acl' => ['frosh_tools:read']])] class QueueController extends AbstractController { + /** + * @param ServiceLocator $transportLocator + */ public function __construct( private readonly Connection $connection, #[Autowire(service: 'shopware.increment.gateway.registry')] @@ -53,6 +57,9 @@ public function resetQueue(): JsonResponse return new JsonResponse(null, Response::HTTP_NO_CONTENT); } + /** + * @param array $queueData + */ private function getMessengerStats(array &$queueData): void { foreach ($this->getTransportNames() as $transportName) { @@ -79,6 +86,9 @@ private function getMessengerStats(array &$queueData): void usort($queueData, static fn(array $a, array $b) => $b['size'] <=> $a['size']); } + /** + * @return array + */ private function getTransportNames(): array { $transportNames = array_keys($this->transportLocator->getProvidedServices()); diff --git a/src/Controller/ScheduledTaskController.php b/src/Controller/ScheduledTaskController.php index 84e0402..634deaa 100644 --- a/src/Controller/ScheduledTaskController.php +++ b/src/Controller/ScheduledTaskController.php @@ -8,6 +8,8 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\MessageQueue\ScheduledTask\Registry\TaskRegistry; +use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTask; +use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskCollection; use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskDefinition; use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskEntity; use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskHandler; @@ -20,6 +22,10 @@ #[Route(path: '/api/_action/frosh-tools', defaults: ['_routeScope' => ['api'], '_acl' => ['frosh_tools:read']])] class ScheduledTaskController extends AbstractController { + /** + * @param iterable $taskHandler + * @param EntityRepository $scheduledTaskRepository + */ public function __construct( #[TaggedIterator('messenger.message_handler')] private readonly iterable $taskHandler, @@ -32,6 +38,10 @@ public function runTask(string $id, Context $context): JsonResponse { $scheduledTask = $this->fetchTask($id, $context); + if (!$scheduledTask instanceof ScheduledTaskEntity) { + return new JsonResponse(null, Response::HTTP_NOT_FOUND); + } + // Set status to allow running it $this->scheduledTaskRepository->update([ [ @@ -42,6 +52,7 @@ public function runTask(string $id, Context $context): JsonResponse ], $context); $className = $scheduledTask->getScheduledTaskClass(); + /** @var ScheduledTask $task */ $task = new $className(); $task->setTaskId($id); @@ -73,10 +84,10 @@ public function registerTasks(): JsonResponse return new JsonResponse(null, Response::HTTP_NO_CONTENT); } - private function fetchTask(string $id, Context $context): ScheduledTaskEntity + private function fetchTask(string $id, Context $context): ?ScheduledTaskEntity { $criteria = new Criteria([$id]); - return $this->scheduledTaskRepository->search($criteria, $context)->first(); + return $this->scheduledTaskRepository->search($criteria, $context)->getEntities()->first(); } } diff --git a/src/Controller/ShopwareFilesController.php b/src/Controller/ShopwareFilesController.php index 1eb55e4..a5b139d 100644 --- a/src/Controller/ShopwareFilesController.php +++ b/src/Controller/ShopwareFilesController.php @@ -10,7 +10,9 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Kernel; +use Shopware\Core\System\Integration\IntegrationCollection; use Shopware\Core\System\Integration\IntegrationEntity; +use Shopware\Core\System\User\UserCollection; use Shopware\Core\System\User\UserEntity; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Attribute\Autowire; @@ -27,6 +29,11 @@ class ShopwareFilesController extends AbstractController private readonly bool $isPlatform; + /** + * @param array $projectExcludeFiles + * @param EntityRepository $userRepository + * @param EntityRepository $integrationRepository + */ public function __construct( #[Autowire('%kernel.shopware_version%')] private readonly string $shopwareVersion, @@ -51,7 +58,7 @@ public function listShopwareFiles(): JsonResponse $url = sprintf('https://swagger.docs.fos.gg/version/%s/Files.xxhsums', $this->shopwareVersion); - $data = trim(@file_get_contents($url)); + $data = trim((string) @file_get_contents($url)); if (empty($data)) { return new JsonResponse(['error' => 'No file information for this Shopware version']); @@ -65,7 +72,7 @@ public function listShopwareFiles(): JsonResponse $row = preg_replace_callback('/vendor\/shopware\/(.)/', fn($matches): string => 'src/' . strtoupper($matches[1]), $row); } - [$expectedMd5Sum, $file] = explode(' ', trim($row)); + [$expectedMd5Sum, $file] = explode(' ', trim((string) $row)); $path = $this->projectDir . '/' . $file; @@ -112,7 +119,7 @@ public function getFileContents(Request $request): JsonResponse return new JsonResponse(['error' => 'Git version is not supported']); } - $file = $request->query->get('file'); + $file = $request->query->getString('file'); if (!$file) { return new JsonResponse(['error' => 'no file provided']); } @@ -137,7 +144,7 @@ public function restoreShopwareFile(Request $request, Context $context): JsonRes return new JsonResponse(['error' => 'Git version is not supported']); } - $file = $request->query->get('file'); + $file = $request->query->getString('file'); if (!$file) { return new JsonResponse(['error' => 'no file provided']); } @@ -167,9 +174,10 @@ public function restoreShopwareFile(Request $request, Context $context): JsonRes return new JsonResponse(['status' => $message]); } - private function getShopwareUrl(string $name): ?string + private function getShopwareUrl(string $name): string { $name = $this->isPlatform ? preg_replace('/^src\//', '', $name) : preg_replace('/^vendor\/shopware\//', '', $name); + \assert($name !== null); $pathParts = \explode('/', $name); $repo = $pathParts[0]; diff --git a/src/Controller/StateMachineController.php b/src/Controller/StateMachineController.php index a36fe59..ef72375 100644 --- a/src/Controller/StateMachineController.php +++ b/src/Controller/StateMachineController.php @@ -11,6 +11,8 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +use Shopware\Core\System\StateMachine\StateMachineCollection; +use Shopware\Core\System\StateMachine\StateMachineDefinition; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -21,10 +23,13 @@ #[Route(path: '/api/_action/frosh-tools', defaults: ['_routeScope' => ['api'], '_acl' => ['frosh_tools:read']])] final class StateMachineController extends AbstractController { + /** + * @param EntityRepository $stateMachineRepository + */ public function __construct(private readonly EntityRepository $stateMachineRepository) {} #[Route(path: '/state-machines/load/{stateMachineId}', name: 'api.frosh.tools.state-machines.load', methods: ['GET'])] - public function load(string $stateMachineId, Request $request): JsonResponse + public function load(string $stateMachineId, Context $context): JsonResponse { if (!Uuid::isValid($stateMachineId)) { return new JsonResponse(); @@ -36,7 +41,7 @@ public function load(string $stateMachineId, Request $request): JsonResponse 'transitions', ]); - $stateMachine = $this->stateMachineRepository->search($criteria, Context::createDefaultContext())->first(); + $stateMachine = $this->stateMachineRepository->search($criteria, $context)->first(); if (!$stateMachine instanceof StateMachineEntity) { return new JsonResponse(); } @@ -47,17 +52,15 @@ public function load(string $stateMachineId, Request $request): JsonResponse $exporter = new Plantuml(); $generatedPlantuml = $exporter->export($stateMachine, $title); - $response = new JsonResponse(); $encode = Planuml::encodep($generatedPlantuml); - $response->setData(['svg' => '//www.plantuml.com/plantuml/svg/' . $encode]); - return $response; + return new JsonResponse(['svg' => '//www.plantuml.com/plantuml/svg/' . $encode]); } #[Route(path: '/state-machines/load-mermaid', name: 'api.frosh.tools.state-machines.load-mermaid', methods: ['GET'])] - public function loadMermaid(Request $request): JsonResponse + public function loadMermaid(Request $request, Context $context): JsonResponse { - $stateMachineType = $request->query->get('stateMachine'); + $stateMachineType = $request->query->getString('stateMachine'); if (empty($stateMachineType)) { return new JsonResponse(); @@ -73,14 +76,13 @@ public function loadMermaid(Request $request): JsonResponse 'transitions', ]); - $stateMachine = $this->stateMachineRepository->search($criteria, Context::createDefaultContext())->first(); + $stateMachine = $this->stateMachineRepository->search($criteria, $context)->getEntities()->first(); + + \assert($stateMachine instanceof StateMachineEntity); $exporter = new Mermaid(); $generatedMermaid = $exporter->export($stateMachine, $title); - $response = new JsonResponse($generatedMermaid); - $response->setData(['data' => $generatedMermaid]); - - return $response; + return new JsonResponse(['data' => $generatedMermaid]); } } diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index f40d08f..57ee569 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -14,6 +14,7 @@ public function getConfigTreeBuilder(): TreeBuilder $treeBuilder = new TreeBuilder('frosh_tools'); $rootNode = $treeBuilder->getRootNode(); + // @phpstan-ignore-next-line $rootNode ->children() ->arrayNode('file_checker') diff --git a/src/DependencyInjection/FroshToolsExtension.php b/src/DependencyInjection/FroshToolsExtension.php index 5717c49..c16008f 100644 --- a/src/DependencyInjection/FroshToolsExtension.php +++ b/src/DependencyInjection/FroshToolsExtension.php @@ -10,20 +10,30 @@ class FroshToolsExtension extends Extension { + /** + * @param array $configs + */ public function load(array $configs, ContainerBuilder $container): void { $config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs); $this->addConfig($container, $this->getAlias(), $config); } + /** + * @param array $config + */ public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface { return new Configuration(); } + /** + * @param array $options + */ private function addConfig(ContainerBuilder $container, string $alias, array $options): void { foreach ($options as $key => $option) { + // @phpstan-ignore-next-line $container->setParameter($alias . '.' . $key, $option); if (\is_array($option)) {