Skip to content

Commit 671cffb

Browse files
authored
Merge branch 'master' into ReworkExceptions
2 parents a66f9e2 + a4c89a5 commit 671cffb

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

docs/source/nonStandardExtensions/filter.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Filters may change the type of the property. For example the builtin filter **da
8484

8585
As the required check is executed before the filter a filter may transform a required value into a null value. Be aware when writing custom filters which transform values to not break your validation rules by adding filters to a property.
8686

87-
Only one transforming filter per property is allowed. may be positioned anywhere in the filter chain of a single property. If multiple filters are applied and a transforming filter is among them you have to make sure the property types are compatible. If you use a custom filter after the dateTime filter for example the custom filter has to accept a DateTime value. Filters used before a transforming filter must accept the base type of the property the filter is applied to defined in the schema.
87+
Only one transforming filter per property is allowed. may be positioned anywhere in the filter chain of a single property. If multiple filters are applied and a transforming filter is among them you have to make sure the property types are compatible. If you use a custom filter after the dateTime filter for example the custom filter has to accept a DateTime value. Filters used before a transforming filter must accept the base type of the property the filter is applied to defined in the schema. If the transformation of a property fails (the transforming filter throws an exception), subsequent filters won't be executed as their execution would add another error due to incompatible types which is irrelevant for the currently provided value.
8888

8989
If you write a custom transforming filter you must define the return type of your filter function as the implementation uses Reflection methods to determine to which type a value is transformed by a filter.
9090

src/Model/Validator/FilterValidator.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
*/
2323
class FilterValidator extends PropertyTemplateValidator
2424
{
25+
/** @var FilterInterface $filter */
26+
protected $filter;
27+
2528
/**
2629
* FilterValidator constructor.
2730
*
@@ -41,14 +44,17 @@ public function __construct(
4144
array $filterOptions = [],
4245
?TransformingFilterInterface $transformingFilter = null
4346
) {
47+
$this->filter = $filter;
48+
4449
$transformingFilter === null
4550
? $this->validateFilterCompatibilityWithBaseType($filter, $property)
4651
: $this->validateFilterCompatibilityWithTransformedType($filter, $transformingFilter, $property);
4752

4853
parent::__construct(
4954
DIRECTORY_SEPARATOR . 'Validator' . DIRECTORY_SEPARATOR . 'Filter.phptpl',
5055
[
51-
'skipTransformedValuesCheck' => false,
56+
'skipTransformedValuesCheck' => $transformingFilter !== null ? '!$transformationFailed' : '',
57+
'isTransformingFilter' => $filter instanceof TransformingFilterInterface,
5258
// check if the given value has a type matched by the filter
5359
'typeCheck' => !empty($filter->getAcceptedTypes())
5460
? '($value !== null && (' .
@@ -72,6 +78,19 @@ public function __construct(
7278
);
7379
}
7480

81+
/**
82+
* Track if a transformation failed. If a transformation fails don't execute subsequent filter as they'd fail with
83+
* an invalid type
84+
*
85+
* @return string
86+
*/
87+
public function getValidatorSetUp(): string
88+
{
89+
return $this->filter instanceof TransformingFilterInterface
90+
? '$transformationFailed = false;'
91+
: '';
92+
}
93+
7594
/**
7695
* Make sure the filter is only executed if a non-transformed value is provided.
7796
* This is required as a setter (eg. for a string property which is modified by the DateTime filter into a DateTime

src/Templates/Validator/Filter.phptpl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
{% if skipTransformedValuesCheck %}{{ skipTransformedValuesCheck }} && {% endif %}
22
(
33
{% if typeCheck %}{{ typeCheck }} || {% endif %}
4-
(function (&$value) {
4+
(function (&$value) use (&$transformationFailed): bool {
55
// make sure exceptions from the filter are caught and added to the error handling
66
try {
77
$value = call_user_func_array([\{{ filterClass }}::class, "{{ filterMethod }}"], [$value, {{ filterOptions }}]);
88
} catch (\Exception $filterException) {
9+
{% if isTransformingFilter %}
10+
$transformationFailed = true;
11+
{% endif %}
12+
913
{{ viewHelper.validationError(filterValueValidator) }}
1014
}
1115

tests/Basic/FilterTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,29 @@ public function testFilterChainWithTransformingFilterOnMultiTypeProperty(bool $i
679679
$this->assertSame(['filteredProperty' => '2020-12-12T00:00:00+0000'], $object->toArray());
680680
}
681681

682+
public function testFilterAfterTransformingFilterIsSkippedIfTransformingFilterFails(): void
683+
{
684+
$this->expectException(ErrorRegistryException::class);
685+
$this->expectExceptionMessage(
686+
'Invalid value for property filteredProperty denied by filter dateTime: Invalid Date Time value "Hello"'
687+
);
688+
689+
$className = $this->generateClassFromFile(
690+
'FilterChainMultiType.json',
691+
(new GeneratorConfiguration())
692+
->addFilter(
693+
$this->getCustomFilter(
694+
[self::class, 'exceptionFilter'],
695+
'stripTime',
696+
[DateTime::class]
697+
)
698+
),
699+
false
700+
);
701+
702+
new $className(['filteredProperty' => 'Hello']);
703+
}
704+
682705
public function testFilterWhichAppliesToMultiTypePropertyPartiallyThrowsAnException(): void
683706
{
684707
$this->expectException(SchemaException::class);

0 commit comments

Comments
 (0)