diff --git a/.gitignore b/.gitignore index 298f1e8..ecdb4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ ###> smartbooster/standard-bundle ### /.phpcs-cache ###< smartbooster/standard-bundle ### +/.php-cs-fixer.cache +/php_cs_fixer_report.json diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..c654f43 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,47 @@ +in(__DIR__) + ->exclude('var') + ->exclude('vendor') + ->exclude('node_modules') + ->append([ + 'bin/console', + ]) + ->path([ + 'bin/', + 'config/', + 'public/', + 'src/', + 'tests/', + ]) +; + +return (new PhpCsFixer\Config()) + ->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()) + ->setRiskyAllowed(true) + ->setRules([ + '@Symfony' => true, + # rules to keep + 'class_definition' => [ + 'multi_line_extends_each_single_line' => true, # https://cs.symfony.com/doc/rules/class_notation/class_definition.html#example-4 + ], + 'concat_space' => ['spacing' => 'one'], # the @PER like PSR12 preserve space + 'no_null_property_initialization' => false, + # rules to check with sfcs-diff + 'phpdoc_summary' => false, #39 + 'yoda_style' => false, #35 + 'phpdoc_separation' => false, #24 + 'single_quote' => false, #16 + 'ordered_imports' => false, #16 + 'no_superfluous_phpdoc_tags' => false, #12 + 'no_empty_phpdoc' => false, #9 + 'trailing_comma_in_multiline' => false, #8 + 'phpdoc_align' => false, #7 + 'increment_style' => false, #5 + 'blank_line_before_statement' => false, #5 + 'no_extra_blank_lines' => false, #4 + 'single_space_around_construct' => false, #3 + ]) + ->setFinder($finder) +; diff --git a/CHANGELOG.md b/CHANGELOG.md index 4931063..29a2ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ CHANGELOG for 1.x =================== +## v1.16.3 - (2026-02-26) +# Fixed +- QA changes to match standard-bundle related from version ^1.2 : .php-cs-fixer.dist + phpcs security + phpstan lvl10 + ## v1.16.2 - (2025-09-25) ### Added - `ProcessStatusEnum::addQbOrderBy` Add method to add a qb order by on the enum diff --git a/bin/console b/bin/console index 4673200..11ebe70 100755 --- a/bin/console +++ b/bin/console @@ -4,11 +4,11 @@ use Smart\CoreBundle\Kernel; use Symfony\Bundle\FrameworkBundle\Console\Application; -if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { +if (!is_file(dirname(__DIR__) . '/vendor/autoload_runtime.php')) { throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); } -require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; +require_once dirname(__DIR__) . '/vendor/autoload_runtime.php'; return function (array $context) { $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); diff --git a/composer.json b/composer.json index f6835a5..d063f81 100644 --- a/composer.json +++ b/composer.json @@ -45,9 +45,10 @@ }, "config": { "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true, + "php-http/discovery": true, "symfony/flex": true, - "symfony/runtime": true, - "php-http/discovery": true + "symfony/runtime": true }, "sort-packages": true, "optimize-autoloader": true, diff --git a/phpcs.xml b/phpcs.xml index dbcf602..ac96056 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -9,6 +9,12 @@ + + + + + + diff --git a/phpstan.neon b/phpstan.neon index e6db36d..2b11364 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,15 +3,63 @@ includes: - vendor/phpstan/phpstan-doctrine/rules.neon - vendor/phpstan/phpstan-symfony/extension.neon - vendor/phpstan/phpstan-symfony/rules.neon + - ./vendor/yamadashy/phpstan-friendly-formatter/extension.neon + - vendor/spaze/phpstan-disallowed-calls/extension.neon parameters: + disallowedFunctionCalls: + - + function: 'exec()' + message: 'for security reasons. Use Symfony Process component instead.' + - + function: 'eval()' + message: 'for security reasons.' + - + function: 'system()' + message: 'for security reasons.' + - + function: 'passthru()' + message: 'for security reasons.' + - + function: 'shell_exec()' + message: 'for security reasons.' + - + function: 'popen()' + message: 'is risky. Ensure you are handling the process handle correctly.' + friendly: + lineBefore: 0 # Number of lines to display before error line (default: 2) + lineAfter: 0 # Number of lines to display after error line (default: 2) + errorFormat: friendly + reportUnmatchedIgnoredErrors: false symfony: containerXmlPath: var/cache/dev/Smart_CoreBundle_KernelDevDebugContainer.xml ignoreErrors: # Ignore error by identifier - identifier: missingType.generics - identifier: missingType.iterableValue - - identifier: trait.unused - # https://github.com/symfony/symfony/issues/36202#issuecomment-769226149 + - identifier: trait.unused # normal to ignore in the context of the bundle + - + identifier: class.notFound + # False positif post install during github qa + path: tests/bootstrap.php # 3 + - + identifier: function.impossibleType + # False positif post install during github qa + path: tests/bootstrap.php # 1 + # Level 8 rules to unstack + - identifier: argument.type # 44 + - identifier: return.type # 3 + # Level 9 rules to unstack + - identifier: offsetAccess.nonOffsetAccessible # 10 + - identifier: foreach.nonIterable # 4 + - identifier: offsetAccess.invalidOffset # 2 + # Level 10 rules to unstack + - identifier: encapsedStringPart.nonString # 2 + - identifier: binaryOp.invalid # 4 + bootstrapFiles: - vendor/bin/.phpunit/phpunit/vendor/autoload.php + + # définit le répertoire temporaire où PHPStan stocke ses fichiers de cache et de travail pendant l'analyse. + # Par défaut: PHPStan utilise le répertoire temporaire système sys_get_temp_dir() . '/phpstan' + tmpDir: var/cache/phpstan diff --git a/src/Command/EntityCleanupCommand.php b/src/Command/EntityCleanupCommand.php index a6217ed..bf87a09 100644 --- a/src/Command/EntityCleanupCommand.php +++ b/src/Command/EntityCleanupCommand.php @@ -114,7 +114,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $propertiesToClean = $config['properties_to_clean']; $removeEntity = empty($propertiesToClean); foreach ($entities as $entity) { - $this->processMonitor->log(sprintf("%d) #%d début nettoyage ...", $i, $entity->getId())); + $this->processMonitor->log(sprintf("%d) #%d début nettoyage ...", $i, $entity->getId())); // qa: Faux positif @phpstan-ignore method.nonObject if ($removeEntity) { $this->entityManager->remove($entity); } else { @@ -155,9 +155,9 @@ private function getTargetClass(array $config): string return Cron::class; // @phpstan-ignore-line } elseif ($class === 'api_call') { return ApiCall::class; // @phpstan-ignore-line - } else { - return $class; } + + return $class; } public function setCommandConfigs(array $commandConfigs): void diff --git a/src/Controller/MonitoringController.php b/src/Controller/MonitoringController.php index f157cbf..3fd8128 100644 --- a/src/Controller/MonitoringController.php +++ b/src/Controller/MonitoringController.php @@ -23,10 +23,10 @@ public function uptime(): Response ]); } - public function phpinfo(): void + public function phpinfo(): void // phpcs:ignore { - phpinfo(); - die; + phpinfo(); // phpcs:ignore + exit; } public function simulateIniOverride(IniOverrideConfig $config): Response @@ -53,7 +53,7 @@ public function dateFormatting(): Response $content = "CURRENT TIME\n\n"; $content .= "default date function : \n" . date($format) . "\n"; $content .= "Datetime format : \n" . $datetime->format($format) . "\n"; - $content .= "date_format function : \n" . date_format($datetime, ($format)) . "\n"; + $content .= "date_format function : \n" . date_format($datetime, $format) . "\n"; $content .= "IntlDateFormatter format : \n" . $formatter->format($datetime) . "\n"; $content .= "Timezone name : \n" . $datetime->getTimezone()->getName() . "\n"; diff --git a/src/Entity/MailableInterface.php b/src/Entity/MailableInterface.php index 5b940c2..cec0e4a 100644 --- a/src/Entity/MailableInterface.php +++ b/src/Entity/MailableInterface.php @@ -16,6 +16,8 @@ interface MailableInterface extends \Stringable * For example : getEmail() ?? getEmail2() */ public function getRecipientEmail(): ?string; + public function getCc(): ?string; + public function getCci(): ?string; } diff --git a/src/Entity/OrganizationTrait.php b/src/Entity/OrganizationTrait.php index 2192508..2da6f12 100644 --- a/src/Entity/OrganizationTrait.php +++ b/src/Entity/OrganizationTrait.php @@ -3,7 +3,6 @@ namespace Smart\CoreBundle\Entity; use Doctrine\ORM\Mapping as ORM; -use Smart\CoreBundle\Entity\AddressTrait; use Symfony\Component\Validator\Constraints as Assert; trait OrganizationTrait diff --git a/src/Enum/ProcessStatusEnum.php b/src/Enum/ProcessStatusEnum.php index 5b3442f..dd03a1f 100644 --- a/src/Enum/ProcessStatusEnum.php +++ b/src/Enum/ProcessStatusEnum.php @@ -72,6 +72,6 @@ public static function addQbOrderBy(QueryBuilder $qb, string $field, string $ord $qb->setParameter($sortParamName, $case->value); } $sort .= ')'; - $qb->orderBy($sort, $order); + $qb->orderBy($sort, $order); // @phpcs:ignore } } diff --git a/src/EventListener/HistoryLoggerListener.php b/src/EventListener/HistoryLoggerListener.php index 324cb63..760c9bf 100644 --- a/src/EventListener/HistoryLoggerListener.php +++ b/src/EventListener/HistoryLoggerListener.php @@ -39,7 +39,7 @@ public function onKernelController(ControllerEvent $event): void { // Set the context base on the domain $request = $this->requestStack->getCurrentRequest(); - $this->context = RequestUtils::getContextFromHost($request->getHost(), $this->domain); + $this->context = RequestUtils::getContextFromHost($request->getHost(), $this->domain); // qa: Faux positif car dans le context Controller on a bien toujours l'objet request @phpstan-ignore method.nonObject $this->historyLogger->setContext($this->context); // Set the origin based on the action controller diff --git a/src/Form/DataTransformer/ParentFileTransformer.php b/src/Form/DataTransformer/ParentFileTransformer.php index cef915c..3d04583 100644 --- a/src/Form/DataTransformer/ParentFileTransformer.php +++ b/src/Form/DataTransformer/ParentFileTransformer.php @@ -20,8 +20,8 @@ public function transform($value) public function reverseTransform($value) { - if ($value !== null && $value->getParent() === null) { - $value->setParent($this->parent); + if ($value !== null && $value->getParent() === null) { // qa: todo à typer @phpstan-ignore method.nonObject + $value->setParent($this->parent); // qa: todo à typer @phpstan-ignore method.nonObject } return $value; diff --git a/src/Monitoring/ApiCallMonitor.php b/src/Monitoring/ApiCallMonitor.php index 11e2fa4..e23b214 100644 --- a/src/Monitoring/ApiCallMonitor.php +++ b/src/Monitoring/ApiCallMonitor.php @@ -57,8 +57,9 @@ public function restart(ApiCallInterface $apiCall): ResponseInterface { $route = $apiCall->getType(); if (!in_array($route, $this->restartAllowedRoutes)) { - throw new AccessDeniedHttpException("The API route '$route' is not allowed to be restarted. " . - "Add it to the `smart_core.monitoring_api_restart_allowed_routes` config if you want to restart it."); + // phpcs:disable + throw new AccessDeniedHttpException("The API route '$route' is not allowed to be restarted. Add it to the `smart_core.monitoring_api_restart_allowed_routes` config if you want to restart it."); + // phpcs:enable } $options = ['headers' => $apiCall->getHeaders()]; diff --git a/src/Monitoring/ProcessMonitor.php b/src/Monitoring/ProcessMonitor.php index d4b7b2c..04bfedc 100644 --- a/src/Monitoring/ProcessMonitor.php +++ b/src/Monitoring/ProcessMonitor.php @@ -43,7 +43,7 @@ public function end(?ProcessInterface $process, bool $isSuccess = true, bool $fl $endedAt = new \DateTime(); $process->setEndedAt($endedAt); - $process->setDuration((int) $endedAt->format('Uv') - (int) $process->getStartedAt()->format('Uv')); + $process->setDuration((int) $endedAt->format('Uv') - (int) $process->getStartedAt()?->format('Uv')); if ($isSuccess) { $process->setStatus(ProcessStatusEnum::SUCCESS); } else { diff --git a/src/Query/MySQL/GroupConcat.php b/src/Query/MySQL/GroupConcat.php index fb8331a..3177c6c 100644 --- a/src/Query/MySQL/GroupConcat.php +++ b/src/Query/MySQL/GroupConcat.php @@ -72,13 +72,13 @@ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker): string $fields = []; foreach ($this->pathExp as $pathExp) { - $fields[] = $pathExp->dispatch($sqlWalker); + $fields[] = $pathExp->dispatch($sqlWalker); // qa: Faux positif @phpstan-ignore method.nonObject } $result .= sprintf('%s', implode(', ', $fields)); if ($this->orderBy) { - $result .= ' ' . $sqlWalker->walkOrderByClause($this->orderBy); + $result .= ' ' . $sqlWalker->walkOrderByClause($this->orderBy); // phpcs:ignore } if ($this->separator) { diff --git a/src/Utils/DateUtils.php b/src/Utils/DateUtils.php index c7ed9d8..436610c 100644 --- a/src/Utils/DateUtils.php +++ b/src/Utils/DateUtils.php @@ -110,9 +110,9 @@ public static function getDaysBetween(\DateTimeInterface $startedAt, \DateTimeIn $isAfterEndHour = ((int) $endedAt->format('H')) >= $options['end_hour']; if ( - ($startedAt->format($dateFormat) === $endedAt->format($dateFormat) && (!$isBeforeStartHour || !$isAfterEndHour)) || - ($date->format($dateFormat) === $startedAt->format($dateFormat) && !$isBeforeStartHour) || - ($date->format($dateFormat) === $endedAt->format($dateFormat) && !$isAfterEndHour) + ($startedAt->format($dateFormat) === $endedAt->format($dateFormat) && (!$isBeforeStartHour || !$isAfterEndHour)) + || ($date->format($dateFormat) === $startedAt->format($dateFormat) && !$isBeforeStartHour) + || ($date->format($dateFormat) === $endedAt->format($dateFormat) && !$isAfterEndHour) ) { continue; } @@ -136,7 +136,7 @@ public static function getCalendarDays(int $month, int $year): array $firstDayOfCalendar->modify('last Monday'); } - $lastDayOfMonth = clone (new \DateTime())->setDate($year, $month, (int)$fistDayOfMonth->format('t')); + $lastDayOfMonth = clone (new \DateTime())->setDate($year, $month, (int) $fistDayOfMonth->format('t')); $lastDayOfCalendar = clone $lastDayOfMonth; // we will only search for the next Sunday if the day number is different from Sunday (7) if ($lastDayOfMonth->format('N') !== '7') { @@ -154,12 +154,12 @@ public static function getCalendarDays(int $month, int $year): array 'isSameMonth' => (int) $currentDay->format('n') === $month, 'isSameDay' => $today->format('d') === $currentDay->format('d') && (int) $currentDay->format('n') === $month, 'isToday' => $isToday, - 'isWeekend' => (int) $currentDay->format('N') === 6 || (int)$currentDay->format('N') === 7, + 'isWeekend' => (int) $currentDay->format('N') === 6 || (int) $currentDay->format('N') === 7, 'isBeforeToday' => !$isToday && $currentDay < $today, ]; $currentDay->modify('+1 day'); - }; + } return $toReturn; } @@ -426,7 +426,7 @@ public static function getNextBirthdayDateTime(\DateTimeInterface $birthday, ?\D if (!$currentDay instanceof \DateTime) { $currentDay = new \DateTime(); } - $nextBirthday->setDate((int)$currentDay->format('Y'), (int)$birthday->format('m'), (int)$birthday->format('d')); + $nextBirthday->setDate((int) $currentDay->format('Y'), (int) $birthday->format('m'), (int) $birthday->format('d')); if ($nextBirthday <= $currentDay) { $nextBirthday->modify('next year'); } diff --git a/src/Utils/StringUtils.php b/src/Utils/StringUtils.php index 13e3b2f..4ef20e7 100644 --- a/src/Utils/StringUtils.php +++ b/src/Utils/StringUtils.php @@ -117,7 +117,7 @@ public static function intToExcelColumn(int $n): string $n--; // Decrement $n to match Excel's 1-based indexing $remainder = $n % 26; $result = chr(65 + $remainder) . $result; - $n = (int)($n / 26); + $n = (int) ($n / 26); } return $result; diff --git a/src/Validator/Constraints/FileRequiredOnNewValidator.php b/src/Validator/Constraints/FileRequiredOnNewValidator.php index e03e3d2..f1af965 100644 --- a/src/Validator/Constraints/FileRequiredOnNewValidator.php +++ b/src/Validator/Constraints/FileRequiredOnNewValidator.php @@ -21,10 +21,10 @@ public function validate($value, Constraint $constraint): void $object = $this->context->getObject(); // Need symfony/form dependency if ($object instanceof FormInterface) { // MDT adjust if use the contraints from collection - $object = $object->getParent()->getData(); + $object = $object->getParent()->getData(); // qa: Faux positif @phpstan-ignore method.nonObject } - if ($object !== null && $object->getId() === null && $object->getFile() === null) { + if ($object !== null && $object->getId() === null && $object->getFile() === null) { // qa: Faux positif @phpstan-ignore-line $this->context->buildViolation($constraint->message) ->atPath('file') ->addViolation(); diff --git a/tests/Utils/ArrayUtilsTest.php b/tests/Utils/ArrayUtilsTest.php index 6f882d6..d103746 100644 --- a/tests/Utils/ArrayUtilsTest.php +++ b/tests/Utils/ArrayUtilsTest.php @@ -24,7 +24,7 @@ public static function getArrayFromTextareaProvider(): array return [ 'clean textarea' => [ // expected - ["some","text"], + ["some", "text"], // values "some @@ -32,7 +32,7 @@ public static function getArrayFromTextareaProvider(): array ], 'no special char deleted' => [ // expected - ["some,","exemple;", "text*"], + ["some,", "exemple;", "text*"], // values "some, exemple; @@ -448,7 +448,7 @@ public static function getRemoveEmptyProvider(): array 2 => "exemple", 4 => "text" ], - ["", "some","exemple", "", "text", null, ""] + ["", "some", "exemple", "", "text", null, ""] ], ]; } diff --git a/tests/Utils/MathUtilsTest.php b/tests/Utils/MathUtilsTest.php index 978a857..35e88fa 100644 --- a/tests/Utils/MathUtilsTest.php +++ b/tests/Utils/MathUtilsTest.php @@ -2,7 +2,6 @@ namespace Smart\CoreBundle\Tests\Utils; -use App\Utils\NumberUtils; use Smart\CoreBundle\Utils\MathUtils; use PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; diff --git a/tests/Utils/StringUtilsTest.php b/tests/Utils/StringUtilsTest.php index e40e096..7319a2a 100644 --- a/tests/Utils/StringUtilsTest.php +++ b/tests/Utils/StringUtilsTest.php @@ -192,7 +192,7 @@ public static function intToExcelColumnProvider(): array 'lower 0' => ['', -1], '0' => ['', 0], 'lower to 26' => ['A', 1], - 'last letter 26' => ['Z', 26 ], + 'last letter 26' => ['Z', 26], 'after the 26 letter' => ['AA', 27], 'double alphabetical' => ['AZ', 52], 'past double alphabetical' => ['BA', 53], @@ -281,7 +281,6 @@ public static function getFirstNameProvider() ]; } - #[DataProvider('formatSpaceBetweenProvider')] public function testFormatSpaceBetween(string $expected, ?string $first, ?string $last): void {