diff --git a/UPGRADE-1.0.md b/UPGRADE-1.0.md index c341d34b..26c1cf8e 100644 --- a/UPGRADE-1.0.md +++ b/UPGRADE-1.0.md @@ -203,6 +203,17 @@ yokai_batch: --- +### Concrete classes are now `final` and/or `readonly` (BREAKING for subclasses) + +As part of the PHP 8.2 modernization, **all concrete classes** in the codebase have been made `final` +and/or `readonly` wherever that was appropriate. Classes that were only marked `@final` in their +docblock now enforce it at the language level. + +**Action required:** If you extend any concrete class from this library, refactor your code to use +composition instead. Extending `final` classes or `readonly` classes is a PHP compile error. + +--- + ### New method: `QueryableJobExecutionStorageInterface::purge()` (BREAKING for custom implementations) A new method has been added to `QueryableJobExecutionStorageInterface`: diff --git a/src/batch-doctrine-dbal/src/DoctrineDBALQueryOffsetReader.php b/src/batch-doctrine-dbal/src/DoctrineDBALQueryOffsetReader.php index ec84ad46..18b06e4a 100644 --- a/src/batch-doctrine-dbal/src/DoctrineDBALQueryOffsetReader.php +++ b/src/batch-doctrine-dbal/src/DoctrineDBALQueryOffsetReader.php @@ -18,32 +18,28 @@ final readonly class DoctrineDBALQueryOffsetReader implements ItemReaderInterface { private Connection $connection; - private string $sql; - private int $batch; public function __construct( ConnectionRegistry $doctrine, - string $sql, + private readonly string $sql, string|null $connection = null, - int $batch = 500, + private readonly int $batch = 500, ) { - if (\mb_strpos($sql, '{limit}') === false || \mb_strpos($sql, '{offset}') === false) { + if (!\str_contains($this->sql, '{limit}') || !\str_contains($this->sql, '{offset}')) { throw new InvalidArgumentException( \sprintf('%s $sql argument must contains "{limit}" and "{offset}" for pagination.', __METHOD__), ); } - if ($batch <= 0) { + if ($this->batch <= 0) { throw new InvalidArgumentException( \sprintf('%s $batch argument must be a positive integer.', __METHOD__), ); } - $connection ??= $doctrine->getDefaultConnectionName(); + $connectionName = $connection ?? $doctrine->getDefaultConnectionName(); /** @var Connection $connection */ - $connection = $doctrine->getConnection($connection); + $connection = $doctrine->getConnection($connectionName); $this->connection = $connection; - $this->sql = $sql; - $this->batch = $batch; } /** diff --git a/src/batch-symfony-messenger/src/MessengerJobsConfiguration.php b/src/batch-symfony-messenger/src/MessengerJobsConfiguration.php index 2d87d879..015147f9 100644 --- a/src/batch-symfony-messenger/src/MessengerJobsConfiguration.php +++ b/src/batch-symfony-messenger/src/MessengerJobsConfiguration.php @@ -7,7 +7,7 @@ /** * Holds the Symfony messenger configuration. */ -final class MessengerJobsConfiguration +final readonly class MessengerJobsConfiguration { public function __construct( /** diff --git a/src/batch-symfony-validator/src/SkipInvalidItemProcessor.php b/src/batch-symfony-validator/src/SkipInvalidItemProcessor.php index 00471ce5..62de099c 100644 --- a/src/batch-symfony-validator/src/SkipInvalidItemProcessor.php +++ b/src/batch-symfony-validator/src/SkipInvalidItemProcessor.php @@ -21,7 +21,7 @@ public function __construct( /** * @var Constraint[]|null */ - private array|null $contraints = null, + private array|null $constraints = null, /** * @var string[]|null */ @@ -31,13 +31,13 @@ public function __construct( public function process(mixed $item): mixed { - $violations = $this->validator->validate($item, $this->contraints, $this->groups); + $violations = $this->validator->validate($item, $this->constraints, $this->groups); if (\count($violations) === 0) { return $item; } throw new SkipItemException($item, new SkipItemOnViolations($violations), [ - 'constraints' => \iterator_to_array($this->normalizeConstraints($this->contraints)), + 'constraints' => \iterator_to_array($this->normalizeConstraints($this->constraints)), 'groups' => $this->groups, ]); } diff --git a/src/batch/src/Finder/FinderInterface.php b/src/batch/src/Finder/FinderInterface.php index f63b3024..ffc84438 100644 --- a/src/batch/src/Finder/FinderInterface.php +++ b/src/batch/src/Finder/FinderInterface.php @@ -17,8 +17,7 @@ interface FinderInterface * * @param mixed $subject The subject that should help to find component * - * @return object The component that matches the subject - * @return T + * @return T The component that matches the subject */ public function find(mixed $subject): object; } diff --git a/src/batch/src/Job/Item/ItemWriterInterface.php b/src/batch/src/Job/Item/ItemWriterInterface.php index b6665b8e..eb66c944 100644 --- a/src/batch/src/Job/Item/ItemWriterInterface.php +++ b/src/batch/src/Job/Item/ItemWriterInterface.php @@ -12,8 +12,7 @@ interface ItemWriterInterface /** * Writes items. * - * @param iterable $items A batch of items to write - * @param iterable $items + * @param iterable $items A batch of items to write */ public function write(iterable $items): void; } diff --git a/src/batch/src/Job/JobWithChildJobs.php b/src/batch/src/Job/JobWithChildJobs.php index aac0c8fa..7a3d6276 100644 --- a/src/batch/src/Job/JobWithChildJobs.php +++ b/src/batch/src/Job/JobWithChildJobs.php @@ -13,10 +13,8 @@ /** * This {@see JobInterface} will execute by triggering child jobs. * If a child job fails, following child jobs won't be executed. - * - * @final use {@see AbstractDecoratedJob} instead. */ -class JobWithChildJobs implements JobInterface +final class JobWithChildJobs implements JobInterface { public function __construct( private readonly JobExecutionStorageInterface $executionStorage, diff --git a/src/batch/src/Job/Parameters/ReplaceWithVariablesParameterAccessor.php b/src/batch/src/Job/Parameters/ReplaceWithVariablesParameterAccessor.php index f729233a..094d1ef3 100644 --- a/src/batch/src/Job/Parameters/ReplaceWithVariablesParameterAccessor.php +++ b/src/batch/src/Job/Parameters/ReplaceWithVariablesParameterAccessor.php @@ -35,7 +35,7 @@ public function get(JobExecution $execution): mixed /** * @return array */ - protected function getVariables(JobExecution $execution): array + private function getVariables(JobExecution $execution): array { return [ '{job}' => $execution->getJobName(), diff --git a/src/batch/src/Trigger/Scheduler/CallbackScheduler.php b/src/batch/src/Trigger/Scheduler/CallbackScheduler.php index 6d4e8cbe..4675ec77 100644 --- a/src/batch/src/Trigger/Scheduler/CallbackScheduler.php +++ b/src/batch/src/Trigger/Scheduler/CallbackScheduler.php @@ -25,21 +25,23 @@ class CallbackScheduler implements SchedulerInterface /** * @var list, 3: string|null}> */ - private array $config = []; + private readonly array $config; /** * @param list|null, 3: string|null}> $config */ public function __construct(array $config) { + $normalized = []; foreach ($config as $entry) { - $this->config[] = [ + $normalized[] = [ $entry[0], $entry[1], $entry[2] ?? [], $entry[3] ?? null, ]; } + $this->config = $normalized; } /** diff --git a/src/batch/src/Trigger/Scheduler/SchedulerInterface.php b/src/batch/src/Trigger/Scheduler/SchedulerInterface.php index 69e55e97..b6e71e6e 100644 --- a/src/batch/src/Trigger/Scheduler/SchedulerInterface.php +++ b/src/batch/src/Trigger/Scheduler/SchedulerInterface.php @@ -14,7 +14,6 @@ interface SchedulerInterface /** * Get list of job to schedule. * - * @return ScheduledJob[] * @return iterable */ public function get(JobExecution $execution): iterable; diff --git a/src/batch/src/Trigger/TriggerScheduledJobsJob.php b/src/batch/src/Trigger/TriggerScheduledJobsJob.php index c2fce95b..90a971a1 100644 --- a/src/batch/src/Trigger/TriggerScheduledJobsJob.php +++ b/src/batch/src/Trigger/TriggerScheduledJobsJob.php @@ -17,12 +17,9 @@ */ final readonly class TriggerScheduledJobsJob implements JobInterface { - /** - * @param iterable $schedulers - */ public function __construct( /** - * @phstan-var iterable + * @var iterable */ private iterable $schedulers, private JobLauncherInterface $jobLauncher,