Skip to content

Commit ea89bd2

Browse files
#475 - Generic transformers: restore, deprecated, the former names of the dashed transformer codes and contextual options
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 133d9fe commit ea89bd2

6 files changed

Lines changed: 373 additions & 10 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Latest
66
* [#483](https://github.com/cleverage/process-bundle/issues/483) Documentation: an exception thrown by `IterableTaskInterface::next()` makes the process fail right away with this exception, the `error_strategy` and the `error_outputs` of the task do not apply.
77

88
## Fixes
9+
* [#475](https://github.com/cleverage/process-bundle/issues/475) Generic transformers: restore, deprecated, the former names (with underscores instead of dashes) of their transformer codes and contextual options, which no longer worked since 5.9 ([#438](https://github.com/cleverage/process-bundle/issues/438)): a transformer code with a dash that is only registered with underscores (`array-first` for `array_first`), and a contextual option with a dash given or used by its former name (`the_prefix`, `{{ the_prefix }}` for `the-prefix`). Update documentation, add tests.
910
* [#477](https://github.com/cleverage/process-bundle/issues/477) "Stop the process on the first invalid item" (`error_strategy: skip` and an error branch ending on a `StopTask`) ends as stopped when the failing task is finalizable (`csv_reader`, `xml_stream_reader`, `stat_counter`...): the error of the skipped item, kept by its state, was taken for a finalization failure and the process ended as failed. Add tests.
1011
* [#473](https://github.com/cleverage/process-bundle/issues/473) A task failing with the `stop` error strategy makes the process fail even when its error branch stops the flow (`StopTask`, `ProcessState::stop()`): the failure was swallowed, the process ended as succeeded or stopped (`cleverage_process.end` event, exit code 0) and a parent process went on with a `null` output. Update documentation, add tests.
1112
* [#485](https://github.com/cleverage/process-bundle/issues/485) The process returns the last output of its `end_point` passed on to the next tasks: the output of an execution whose output is ignored (skipped, filtered, failed with the `skip` strategy) was returned, e.g. `""` for a `skip_empty` end point whose last item is empty, or `null` for a filtered last item. Update documentation, add tests.
@@ -397,7 +398,6 @@ v3.2.2
397398
* `\CleverAge\ProcessBundle\Exception\InvalidProcessConfigurationException` now displays the failing process code
398399
* `\CleverAge\ProcessBundle\Transformer\TransformerTrait` now displays a more explicit message on wrong options type
399400

400-
401401
v3.2.1
402402
------
403403

@@ -418,8 +418,6 @@ v3.2.0
418418
There is no BC break for this version, but note that `sidus/base-bundle` has been removed from dependencies.
419419
If you use it, it should already be inside your own composer.json.
420420

421-
422-
423421
Release v3.1
424422
============
425423

@@ -445,15 +443,13 @@ v3.1.5
445443

446444
* [GITHUB-120](https://github.com/cleverage/process-bundle/pull/120): FolderBrowserTask: Accept array type for `name_pattern` option
447445

448-
449446
v3.1.4
450447
------
451448

452449
### Features
453450

454451
* (_backport from v3.0.9_) Adding simple task to launch system commands
455452

456-
457453
v3.1.3
458454
------
459455

@@ -509,7 +505,6 @@ v3.1.0
509505
* [GITHUB-106](https://github.com/cleverage/process-bundle/issues/106): an entry-point cannot have an ancestor anymore.
510506
The behaviour was undefined, and now it will throw an exception.
511507

512-
513508
UPGRADE TO 2.0
514509
==============
515510

@@ -533,7 +528,6 @@ options:
533528
<key>: <options>
534529
```
535530
536-
537531
UPGRADE TO 1.1
538532
==============
539533

‎UPGRADE.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@ as succeeded or stopped. To stop the process on purpose on the first error (not
1111
on the failing task, as in the "Stop the process on the first invalid item" example of the
1212
[StopTask](docs/reference/tasks/stop_task.md).
1313

14+
### Dashes in the generic transformers
15+
16+
Since 5.9, the codes of the transformers and of the contextual options of a generic transformer keep their dashes, as
17+
written. Before 5.9, they were replaced by underscores (unless the code also had an underscore), and only their former
18+
name worked: `array-first` was found as the `array_first` transformer, and a `the-prefix` contextual option was given
19+
and used as `the_prefix` / `{{ the_prefix }}`. These former names work again but are deprecated since 5.10 (`Since
20+
cleverage/process-bundle 5.10: The transformer "array-first" of the generic transformer...`, `Using the contextual option
21+
"the-prefix" of the generic transformer...`), and will not work in 6.0: write the code of the transformer as registered
22+
(`array_first`), and the contextual option as defined in `contextual_options` (`the-prefix`, `{{ the-prefix }}`).
23+
1424
## v5.8
1525

1626
### Unknown task references

‎docs/reference/03-generic_transformers_definition.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,7 @@ clever_age_process:
8686

8787
As any transformer code, generic transformer codes must be unique: use a prefix to avoid conflicts. A dash is kept in
8888
the code (it was replaced by an underscore before 5.7, the former name is deprecated), as in the codes of the
89-
transformers and of the contextual options of a generic transformer.
89+
transformers and of the contextual options of a generic transformer (replaced by an underscore before 5.9, unless the
90+
code also had an underscore: `array-first` was found as the `array_first` transformer, and the `the-prefix` contextual
91+
option was given and used as `the_prefix` / `{{ the_prefix }}`; these former names still work but are deprecated since
92+
5.10, write the code of the transformer and of the contextual option as registered).

‎src/DependencyInjection/Configuration.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,16 @@ protected function appendTransformerConfigDefinition(NodeBuilder $definition): v
106106
->info('Options of the transformer (code: definition with required, default, default_is_null), injected in the "transformers" with "{{ code }}"')
107107
->example(['delimiter' => ['default' => ',']])
108108
// Keep the dashes of the option codes, as written in the options given to the transformer and in "{{ code }}"
109+
// (their former name, with underscores, is deprecated, see GenericTransformer)
109110
->normalizeKeys(false)
110111
->variablePrototype()
111112
->end()
112113
->end()
113114
->arrayNode('transformers')
114115
->info('Transformers applied in sequence, by code, with their options')
115116
->example(['explode' => ['delimiter' => '{{ separator }}']])
116-
// Keep the dashes of the transformer codes (the former name of a generic transformer is deprecated)
117+
// Keep the dashes of the transformer codes (the former name of a generic transformer is deprecated, as the
118+
// former name of a transformer code that is only registered with underscores, see GenericTransformer)
117119
->normalizeKeys(false)
118120
->variablePrototype()
119121
->end()

‎src/Transformer/GenericTransformer.php‎

Lines changed: 177 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313

1414
namespace CleverAge\ProcessBundle\Transformer;
1515

16+
use CleverAge\ProcessBundle\Configuration\DeprecatedCodeAliases;
1617
use CleverAge\ProcessBundle\Context\ContextualOptionResolver;
1718
use CleverAge\ProcessBundle\Exception\InvalidArgumentException;
1819
use CleverAge\ProcessBundle\Exception\LogicException;
1920
use CleverAge\ProcessBundle\Registry\TransformerRegistry;
21+
use Symfony\Component\OptionsResolver\Exception\MissingOptionsException;
2022
use Symfony\Component\OptionsResolver\Options;
2123
use Symfony\Component\OptionsResolver\OptionsResolver;
2224

@@ -41,6 +43,15 @@ class GenericTransformer implements ConfigurableTransformerInterface
4143
*/
4244
protected ?array $contextualOptions = null;
4345

46+
/**
47+
* Former name (with underscores instead of dashes, before 5.9) of the contextual options whose code has a dash.
48+
*
49+
* Deprecated since 5.10, the former names will not be accepted in v6.0.
50+
*
51+
* @var array<string, string> former name => code
52+
*/
53+
private array $deprecatedContextualOptions = [];
54+
4455
public function __construct(
4556
protected ContextualOptionResolver $contextualOptionResolver,
4657
TransformerRegistry $transformerRegistry,
@@ -61,7 +72,13 @@ public function initialize(string $code, array $options = []): void
6172

6273
$initialOptions = $resolver->resolve($options);
6374
$this->contextualOptions = $initialOptions['contextual_options'];
64-
$this->preconfiguredTransformerOptions = $initialOptions['transformers'];
75+
$this->deprecatedContextualOptions = DeprecatedCodeAliases::create(array_keys($this->contextualOptions));
76+
$transformers = $initialOptions['transformers'];
77+
if (\is_array($transformers)) {
78+
$transformers = $this->replaceFormerTransformerCodes($transformers);
79+
$this->checkFormerContextualOptionPlaceholders($transformers);
80+
}
81+
$this->preconfiguredTransformerOptions = $transformers;
6582
}
6683

6784
/**
@@ -91,6 +108,11 @@ public function configureInitialOptions(OptionsResolver $resolver): void
91108
public function configureOptions(OptionsResolver $resolver): void
92109
{
93110
foreach ($this->contextualOptions ?? [] as $option => $optionConfig) {
111+
$formerName = array_search($option, $this->deprecatedContextualOptions, true);
112+
if (false !== $formerName) {
113+
$this->configureFormerContextualOption($resolver, $option, $formerName, $optionConfig);
114+
continue;
115+
}
94116
$resolver->setDefined($option);
95117
if (null !== $optionConfig['default'] || $optionConfig['default_is_null']) {
96118
$resolver->setDefault($option, $optionConfig['default']);
@@ -148,6 +170,10 @@ public function normalizeTransformerOptions(Options $options, array $transformer
148170
? $options[$contextualOption]
149171
: null;
150172
}
173+
// The former name of a contextual option whose code has a dash, in "{{ former_name }}" (deprecated)
174+
foreach ($this->deprecatedContextualOptions as $formerName => $contextualOption) {
175+
$contextualizedOptionValues[$formerName] = $contextualizedOptionValues[$contextualOption] ?? null;
176+
}
151177

152178
return $this->contextualOptionResolver->contextualizeOptions($transformerOptions, $contextualizedOptionValues);
153179
}
@@ -165,4 +191,154 @@ public function configureContextualOptions(OptionsResolver $resolver): void
165191
$resolver->setDefault('default_is_null', false);
166192
$resolver->setAllowedTypes('default_is_null', 'bool');
167193
}
194+
195+
/**
196+
* Before 5.9, the dashes of the transformer codes of a generic transformer were replaced by underscores (unless the
197+
* code also had an underscore): "array-first" was found as "array_first". Such a code, when it is not registered
198+
* but its former name is, is replaced by its former name (deprecated).
199+
*
200+
* Deprecated since 5.10, will be removed in v6.0.
201+
*
202+
* @param array<int|string, mixed> $transformers
203+
*
204+
* @return array<int|string, mixed>
205+
*/
206+
private function replaceFormerTransformerCodes(array $transformers): array
207+
{
208+
if (array_is_list($transformers)) {
209+
return $transformers;
210+
}
211+
$replaced = [];
212+
foreach ($transformers as $key => $transformerOptions) {
213+
$key = $this->replaceFormerTransformerCode($key, $transformers);
214+
$replaced[$key] = $transformerOptions;
215+
}
216+
217+
return $replaced;
218+
}
219+
220+
/**
221+
* @param array<int|string, mixed> $transformers
222+
*/
223+
private function replaceFormerTransformerCode(int|string $key, array $transformers): int|string
224+
{
225+
if (!\is_string($key) || !str_contains($key, '-') || str_contains($key, '_')) {
226+
return $key;
227+
}
228+
$formerKey = str_replace('-', '_', $key);
229+
// Both were written: none was renamed
230+
if (\array_key_exists($formerKey, $transformers)) {
231+
return $key;
232+
}
233+
// The code, without the "#" suffix of the key
234+
$code = explode('#', $key, 2)[0];
235+
$formerCode = str_replace('-', '_', $code);
236+
$registry = $this->transformerRegistry;
237+
if (!$registry instanceof TransformerRegistry || $registry->hasTransformer($code) || !$registry->hasTransformer($formerCode)) {
238+
return $key;
239+
}
240+
trigger_deprecation(
241+
'cleverage/process-bundle',
242+
'5.10',
243+
'The transformer "%s" of the generic transformer "%s" is not registered, its former name "%s", with underscores instead of dashes, is used: write "%s". The dashes of the transformer codes of a generic transformer were replaced by underscores before 5.9, the former name will not be used in v6.0.',
244+
$code,
245+
$this->transformerCode,
246+
$formerCode,
247+
$formerCode,
248+
);
249+
250+
return $formerKey;
251+
}
252+
253+
/**
254+
* Before 5.9, a contextual option whose code has a dash was named with underscores, also in "{{ former_name }}".
255+
*
256+
* Deprecated since 5.10, will be removed in v6.0.
257+
*
258+
* @param array<int|string, mixed> $transformers
259+
*/
260+
private function checkFormerContextualOptionPlaceholders(array $transformers): void
261+
{
262+
if ([] === $this->deprecatedContextualOptions) {
263+
return;
264+
}
265+
$formerNames = array_map(static fn (string $formerName): string => preg_quote($formerName, '/'), array_keys($this->deprecatedContextualOptions));
266+
$pattern = \sprintf('/{{[ ]*(%s)[ ]*}}/', implode('|', $formerNames));
267+
$used = [];
268+
foreach (self::getStrings($transformers) as $string) {
269+
if (preg_match_all($pattern, $string, $matches)) {
270+
$used = [...$used, ...$matches[1]];
271+
}
272+
}
273+
foreach (array_unique($used) as $formerName) {
274+
trigger_deprecation(
275+
'cleverage/process-bundle',
276+
'5.10',
277+
'Using the contextual option "%s" of the generic transformer "%s" as "{{ %s }}", with underscores instead of dashes, is deprecated: write "{{ %s }}". The dashes of the contextual option codes were replaced by underscores before 5.9, the former name will not be replaced in v6.0.',
278+
$this->deprecatedContextualOptions[$formerName],
279+
$this->transformerCode,
280+
$formerName,
281+
$this->deprecatedContextualOptions[$formerName],
282+
);
283+
}
284+
}
285+
286+
/**
287+
* A contextual option whose code has a dash can also be given by its former name, with underscores (deprecated).
288+
*
289+
* Deprecated since 5.10, will be removed in v6.0.
290+
*
291+
* @param array{required: bool, default: mixed, default_is_null: bool} $optionConfig
292+
*/
293+
private function configureFormerContextualOption(OptionsResolver $resolver, string $option, string $formerName, array $optionConfig): void
294+
{
295+
$resolver->setDefined($formerName);
296+
$transformerCode = $this->transformerCode;
297+
// Given by its code, by its former name, or the default value
298+
$resolver->setDefault($option, static function (Options $options) use ($option, $formerName, $optionConfig, $transformerCode): mixed {
299+
if ($options->offsetExists($formerName)) {
300+
trigger_deprecation(
301+
'cleverage/process-bundle',
302+
'5.10',
303+
'Using the contextual option "%s" of the generic transformer "%s" by the name "%s", with underscores instead of dashes, is deprecated: use "%s". The dashes of the contextual option codes were replaced by underscores before 5.9, the former name will not be accepted in v6.0.',
304+
$option,
305+
$transformerCode,
306+
$formerName,
307+
$option,
308+
);
309+
310+
return $options[$formerName];
311+
}
312+
if (null !== $optionConfig['default'] || $optionConfig['default_is_null']) {
313+
return $optionConfig['default'];
314+
}
315+
if ($optionConfig['required']) {
316+
throw new MissingOptionsException(\sprintf('The required option "%s" is missing.', $option));
317+
}
318+
319+
return null;
320+
});
321+
}
322+
323+
/**
324+
* The strings of the transformer options, keys and values, where "{{ code }}" is replaced.
325+
*
326+
* @param array<int|string, mixed> $values
327+
*
328+
* @return list<string>
329+
*/
330+
private static function getStrings(array $values): array
331+
{
332+
$strings = [];
333+
foreach ($values as $key => $value) {
334+
$strings[] = (string) $key;
335+
if (\is_array($value)) {
336+
$strings = [...$strings, ...self::getStrings($value)];
337+
} elseif (\is_string($value)) {
338+
$strings[] = $value;
339+
}
340+
}
341+
342+
return $strings;
343+
}
168344
}

0 commit comments

Comments
 (0)