diff --git a/CHANGELOG.md b/CHANGELOG.md index 185954a1..0ba97445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Latest * [#194](https://github.com/cleverage/process-bundle/issues/194) Fix ProcessLauncherTask: the `process_options` normalizer returned an array despite its scalar return type, so the task always failed with a `TypeError`. Update documentation, add tests. * [#189](https://github.com/cleverage/process-bundle/issues/189) Fix EventDispatcherTask: dispatch the event under the `event_name` option (regression since v4.0). `event_name` is now optional: when `null` (default), the event is dispatched under its class name. Update documentation, add tests. * [#198](https://github.com/cleverage/process-bundle/issues/198) Fix DateParserTransformer: a `\DateTimeImmutable` input is converted to a `\DateTime` instead of throwing a `TypeError`. Update documentation, add tests. +* [#199](https://github.com/cleverage/process-bundle/issues/199) Fix PregFilterTransformer: an array `replacement` is no longer cast to the string `"Array"`, and requires an array `pattern`. Update documentation, add tests. ## Deprecated * [#189](https://github.com/cleverage/process-bundle/issues/189) EventDispatcherTask: when `event_name` is set, listening to `CleverAge\ProcessBundle\Event\EventDispatcherTaskEvent` is deprecated (the event is still dispatched under its class name, with an `E_USER_DEPRECATED` error, if it has listeners). Listen to the configured `event_name` instead: the BC layer will be removed in v6.0. diff --git a/docs/reference/transformers/preg_filter_transformer.md b/docs/reference/transformers/preg_filter_transformer.md index 29a7f70a..90984880 100644 --- a/docs/reference/transformers/preg_filter_transformer.md +++ b/docs/reference/transformers/preg_filter_transformer.md @@ -27,7 +27,7 @@ Options | Code | Type | Required | Default | Description | |---------------|-----------------|:--------:|---------|-------------------------------------------------------------------------------------------------------| | `pattern` | `string\|array` | **X** | | Pattern, or list of patterns, to search | -| `replacement` | `string\|array` | **X** | | Replacement string. The value is cast to string, so an array is not supported in practice (see Notes) | +| `replacement` | `string\|array` | **X** | | Replacement string, or list of replacements (only when `pattern` is an array) | Examples -------- @@ -39,6 +39,15 @@ preg_filter: replacement: '' ``` +* Replace several patterns at once: each pattern is replaced by the replacement at the same position + +```yaml +# Transformer options level +preg_filter: + pattern: ['/a/', '/b/'] + replacement: ['1', '2'] +``` + * Reformat a date, `null` if the input does not match ```yaml @@ -51,5 +60,6 @@ preg_filter: Notes ----- -Although `replacement` accepts an `array`, it is cast to string before calling `preg_filter()`, which results in the -literal `Array` (and a PHP warning). +An array `replacement` requires an array `pattern` (as in `preg_filter()`): otherwise an `InvalidOptionsException` is +thrown when resolving the options. If `replacement` has fewer elements than `pattern`, the missing replacements are +empty strings. diff --git a/src/Transformer/PregFilterTransformer.php b/src/Transformer/PregFilterTransformer.php index 361dd119..7b046878 100644 --- a/src/Transformer/PregFilterTransformer.php +++ b/src/Transformer/PregFilterTransformer.php @@ -13,6 +13,8 @@ namespace CleverAge\ProcessBundle\Transformer; +use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class PregFilterTransformer implements ConfigurableTransformerInterface @@ -22,7 +24,7 @@ public function transform(mixed $value, array $options = []): array|string|null $pattern = $options['pattern']; $replacement = $options['replacement']; - return preg_filter($pattern, (string) $replacement, (string) $value); + return preg_filter($pattern, $replacement, (string) $value); } /** @@ -38,5 +40,15 @@ public function configureOptions(OptionsResolver $resolver): void $resolver->setRequired(['pattern', 'replacement']); $resolver->setAllowedTypes('pattern', ['string', 'array']); $resolver->setAllowedTypes('replacement', ['string', 'array']); + $resolver->setNormalizer( + 'replacement', + static function (Options $options, string|array $value): string|array { + if (\is_array($value) && !\is_array($options['pattern'])) { + throw new InvalidOptionsException('The "replacement" option can only be an array when the "pattern" option is an array'); + } + + return $value; + } + ); } } diff --git a/tests/Transformer/PregFilterTransformerTest.php b/tests/Transformer/PregFilterTransformerTest.php new file mode 100644 index 00000000..746f5dc9 --- /dev/null +++ b/tests/Transformer/PregFilterTransformerTest.php @@ -0,0 +1,89 @@ +resolveOptions($transformer, [ + 'pattern' => '/^(\d{2})\/(\d{2})\/(\d{4})$/', + 'replacement' => '$3-$2-$1', + ]); + + $this->assertSame('2019-12-02', $transformer->transform('02/12/2019', $options)); + } + + public function testTransformReturnsNullWhenPatternDoesNotMatch(): void + { + $transformer = new PregFilterTransformer(); + $options = $this->resolveOptions($transformer, [ + 'pattern' => '/^\d+$/', + 'replacement' => 'number', + ]); + + $this->assertNull($transformer->transform('abc', $options)); + } + + public function testTransformWithArrayReplacement(): void + { + $transformer = new PregFilterTransformer(); + $options = $this->resolveOptions($transformer, [ + 'pattern' => ['/a/', '/b/'], + 'replacement' => ['1', '2'], + ]); + + $this->assertSame('12c', $transformer->transform('abc', $options)); + } + + public function testArrayReplacementRequiresArrayPattern(): void + { + $transformer = new PregFilterTransformer(); + + $this->expectException(InvalidOptionsException::class); + + $this->resolveOptions($transformer, [ + 'pattern' => '/a/', + 'replacement' => ['1', '2'], + ]); + } + + public function testGetCode(): void + { + $this->assertSame('preg_filter', (new PregFilterTransformer())->getCode()); + } + + /** + * @param array $options + * + * @return array + */ + private function resolveOptions(PregFilterTransformer $transformer, array $options): array + { + $resolver = new OptionsResolver(); + $transformer->configureOptions($resolver); + + return $resolver->resolve($options); + } +}