From 759f789d82dc40368a393c10467ce7437ffd9b93 Mon Sep 17 00:00:00 2001 From: Nicolas Joubert Date: Tue, 20 Oct 2026 17:25:21 +0200 Subject: [PATCH] #64 - Throw exceptions implementing ProcessExceptionInterface instead of raw SPL exceptions As cleverage/process-bundle#317: new exceptions of the bundle extending the SPL exceptions thrown before and implementing ProcessExceptionInterface (9 throw replaced, same messages). Tests and CHANGELOG updated. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 1 + src/Exception/RuntimeException.php | 23 ++++++++ src/Exception/UnexpectedValueException.php | 23 ++++++++ src/Task/Database/DatabaseReaderTask.php | 3 +- src/Task/Database/DatabaseUpdaterTask.php | 3 +- .../AbstractDoctrineQueryTask.php | 3 +- .../EntityManager/AbstractDoctrineTask.php | 3 +- .../EntityManager/DoctrineCleanerTask.php | 3 +- .../EntityManager/DoctrineDetacherTask.php | 3 +- .../EntityManager/DoctrineRefresherTask.php | 3 +- .../EntityManager/DoctrineRemoverTask.php | 3 +- src/Task/EntityManager/DoctrineWriterTask.php | 3 +- tests/Exception/SplExceptionsTest.php | 53 +++++++++++++++++++ 13 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 src/Exception/RuntimeException.php create mode 100644 src/Exception/UnexpectedValueException.php create mode 100644 tests/Exception/SplExceptionsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f1f83a..9d19e7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Latest ------ ### Changes +* [#64](https://github.com/cleverage/doctrine-process-bundle/issues/64) The bundle throws exceptions implementing `ProcessExceptionInterface` of the process bundle instead of raw SPL exceptions (as [cleverage/process-bundle#317](https://github.com/cleverage/process-bundle/issues/317)): new `Exception\UnexpectedValueException` and `Exception\RuntimeException`, extending the SPL exceptions thrown before (same messages, the existing `catch` keep working). Add tests. * [#62](https://github.com/cleverage/doctrine-process-bundle/issues/62) Composer: require PHP `^8.2` instead of `>=8.2` (PHP 9 is no longer accepted before being tested), remove the authorization of the `symfony/flex` plugin, which is not required (the CI installs it globally), as [cleverage/process-bundle#319](https://github.com/cleverage/process-bundle/issues/319). * [#58](https://github.com/cleverage/doctrine-process-bundle/issues/58) `DoctrineReaderTask` (`order_by` option): pass the `\SortDirection` enum to `QueryBuilder::addOrderBy()` when doctrine/orm accepts it (3.7 or later, where a string direction is deprecated), the string otherwise (same query). Require `psr/log` `^2 || ^3`: process-bundle does not work with psr/log 1. The tests fail on deprecations and boot a kernel with process-bundle and DoctrineBundle. * [#57](https://github.com/cleverage/doctrine-process-bundle/issues/57) Define the public API (Symfony BC promise, as [cleverage/process-bundle#279](https://github.com/cleverage/process-bundle/issues/279)): the concrete tasks (`DatabaseReaderTask`, `DatabaseUpdaterTask`, `DoctrineReaderTask`, `DoctrineWriterTask`, `DoctrineBatchWriterTask`, `DoctrineCleanerTask`, `DoctrineDetacherTask`, `DoctrineRefresherTask`, `DoctrineRemoverTask`, `ClearEntityManagerTask`) and `CleverAgeDoctrineProcessBundle` are marked `@final since 3.3`, they will be `final` in 4.0: extending them triggers a deprecation with the Symfony `DebugClassLoader`. `AbstractDoctrineTask` and `AbstractDoctrineQueryTask` stay open. `CleverAgeDoctrineProcessExtension` is marked `@internal`. diff --git a/src/Exception/RuntimeException.php b/src/Exception/RuntimeException.php new file mode 100644 index 0000000..508e1c6 --- /dev/null +++ b/src/Exception/RuntimeException.php @@ -0,0 +1,23 @@ +getInput(); $params = $options['input_as_params'] ? $inputAsParams : $options['params']; if (!\is_array($params)) { - throw new \UnexpectedValueException('Expecting an array of params'); + throw new UnexpectedValueException('Expecting an array of params'); } /** @var array $params */ diff --git a/src/Task/Database/DatabaseUpdaterTask.php b/src/Task/Database/DatabaseUpdaterTask.php index 155233a..77cb35b 100644 --- a/src/Task/Database/DatabaseUpdaterTask.php +++ b/src/Task/Database/DatabaseUpdaterTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\Database; +use CleverAge\DoctrineProcessBundle\Exception\UnexpectedValueException; use CleverAge\ProcessBundle\Model\AbstractConfigurableTask; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\DBAL\ArrayParameterType; @@ -67,7 +68,7 @@ protected function initializeStatement(ProcessState $state): int $inputAsParams = $state->getInput(); $params = $options['input_as_params'] ? $inputAsParams : $options['params']; if (!\is_array($params)) { - throw new \UnexpectedValueException('Expecting an array of params'); + throw new UnexpectedValueException('Expecting an array of params'); } /** @var array $params */ diff --git a/src/Task/EntityManager/AbstractDoctrineQueryTask.php b/src/Task/EntityManager/AbstractDoctrineQueryTask.php index e433c35..0b10b8e 100644 --- a/src/Task/EntityManager/AbstractDoctrineQueryTask.php +++ b/src/Task/EntityManager/AbstractDoctrineQueryTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\UnexpectedValueException; use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; use Psr\Log\LogLevel; @@ -83,7 +84,7 @@ protected function getQueryBuilder( $qb = $repository->createQueryBuilder('e'); foreach ($criteria as $field => $value) { if (preg_match('/[^a-zA-Z0-9]/', $field)) { - throw new \UnexpectedValueException("Forbidden field name '{$field}'"); + throw new UnexpectedValueException("Forbidden field name '{$field}'"); } $parameterName = 'param_'.bin2hex(random_bytes(4)); if (null === $value) { diff --git a/src/Task/EntityManager/AbstractDoctrineTask.php b/src/Task/EntityManager/AbstractDoctrineTask.php index 79e84c6..ee534b9 100644 --- a/src/Task/EntityManager/AbstractDoctrineTask.php +++ b/src/Task/EntityManager/AbstractDoctrineTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\UnexpectedValueException; use CleverAge\ProcessBundle\Model\AbstractConfigurableTask; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\ORM\EntityManagerInterface; @@ -59,7 +60,7 @@ protected function getEntityManager(ProcessState $state, string $class): EntityM ? $this->doctrine->getManagerForClass($class) : $this->doctrine->getManager($entityManagerName); if (!$entityManager instanceof EntityManagerInterface) { - throw new \UnexpectedValueException("No manager found for class {$class}"); + throw new UnexpectedValueException("No manager found for class {$class}"); } return $entityManager; diff --git a/src/Task/EntityManager/DoctrineCleanerTask.php b/src/Task/EntityManager/DoctrineCleanerTask.php index 7396beb..fa9bff1 100644 --- a/src/Task/EntityManager/DoctrineCleanerTask.php +++ b/src/Task/EntityManager/DoctrineCleanerTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\RuntimeException; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\Common\Util\ClassUtils; @@ -27,7 +28,7 @@ public function execute(ProcessState $state): void { $entity = $state->getInput(); if (null === $entity) { - throw new \RuntimeException('DoctrineCleanerTask does not allow null input'); + throw new RuntimeException('DoctrineCleanerTask does not allow null input'); } /** @var object $entity */ $class = ClassUtils::getClass($entity); diff --git a/src/Task/EntityManager/DoctrineDetacherTask.php b/src/Task/EntityManager/DoctrineDetacherTask.php index 446ab66..8c383db 100644 --- a/src/Task/EntityManager/DoctrineDetacherTask.php +++ b/src/Task/EntityManager/DoctrineDetacherTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\RuntimeException; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\Common\Util\ClassUtils; @@ -27,7 +28,7 @@ public function execute(ProcessState $state): void { $entity = $state->getInput(); if (null === $entity) { - throw new \RuntimeException('DoctrineDetacherTask does not allow null input'); + throw new RuntimeException('DoctrineDetacherTask does not allow null input'); } /** @var object $entity */ $class = ClassUtils::getClass($entity); diff --git a/src/Task/EntityManager/DoctrineRefresherTask.php b/src/Task/EntityManager/DoctrineRefresherTask.php index 7783611..e54f3c7 100644 --- a/src/Task/EntityManager/DoctrineRefresherTask.php +++ b/src/Task/EntityManager/DoctrineRefresherTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\RuntimeException; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\Common\Util\ClassUtils; @@ -27,7 +28,7 @@ public function execute(ProcessState $state): void { $entity = $state->getInput(); if (null === $entity) { - throw new \RuntimeException('DoctrineRefresherTask does not allow null input'); + throw new RuntimeException('DoctrineRefresherTask does not allow null input'); } /** @var object $entity */ $class = ClassUtils::getClass($entity); diff --git a/src/Task/EntityManager/DoctrineRemoverTask.php b/src/Task/EntityManager/DoctrineRemoverTask.php index b7d37d6..5379caa 100644 --- a/src/Task/EntityManager/DoctrineRemoverTask.php +++ b/src/Task/EntityManager/DoctrineRemoverTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\RuntimeException; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\Common\Util\ClassUtils; @@ -27,7 +28,7 @@ public function execute(ProcessState $state): void { $entity = $state->getInput(); if (null === $entity) { - throw new \RuntimeException('DoctrineRemoverTask does not allow null input'); + throw new RuntimeException('DoctrineRemoverTask does not allow null input'); } /** @var object $entity */ $class = ClassUtils::getClass($entity); diff --git a/src/Task/EntityManager/DoctrineWriterTask.php b/src/Task/EntityManager/DoctrineWriterTask.php index 22c642c..0ee50fd 100644 --- a/src/Task/EntityManager/DoctrineWriterTask.php +++ b/src/Task/EntityManager/DoctrineWriterTask.php @@ -13,6 +13,7 @@ namespace CleverAge\DoctrineProcessBundle\Task\EntityManager; +use CleverAge\DoctrineProcessBundle\Exception\RuntimeException; use CleverAge\ProcessBundle\Model\ProcessState; use Doctrine\Common\Util\ClassUtils; @@ -35,7 +36,7 @@ protected function writeEntity(ProcessState $state): mixed $entity = $state->getInput(); if (null === $entity) { - throw new \RuntimeException('DoctrineWriterTask does not allow null input'); + throw new RuntimeException('DoctrineWriterTask does not allow null input'); } $class = ClassUtils::getClass($entity); $entityManager = $this->getEntityManager($state, $class); diff --git a/tests/Exception/SplExceptionsTest.php b/tests/Exception/SplExceptionsTest.php new file mode 100644 index 0000000..27bc992 --- /dev/null +++ b/tests/Exception/SplExceptionsTest.php @@ -0,0 +1,53 @@ +, class-string<\Throwable>}> + */ + public static function provideExceptions(): iterable + { + yield 'UnexpectedValueException' => [UnexpectedValueException::class, \UnexpectedValueException::class]; + yield 'RuntimeException' => [RuntimeException::class, \RuntimeException::class]; + } + + /** + * @param class-string<\Throwable> $class + * @param class-string<\Throwable> $splClass + */ + #[DataProvider('provideExceptions')] + public function testExtendsTheSplExceptionAndImplementsTheInterface(string $class, string $splClass): void + { + $exception = new $class('message', 12); + + self::assertInstanceOf($splClass, $exception); + self::assertInstanceOf(ProcessExceptionInterface::class, $exception); + self::assertSame(['message', 12], [$exception->getMessage(), $exception->getCode()]); + } +}