Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Validator] catch any UnexpectedValueException on validation #27917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 24, 2018

Conversation

xabbuh
Copy link
Member

@xabbuh xabbuh commented Jul 10, 2018

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #12312
License MIT
Doc PR

@xabbuh
Copy link
Member Author

xabbuh commented Jul 10, 2018

This change would fix issues like #14943 (and many other related issues). I didn't add any tests yet, but would like to get some feedback first as setting them up seems to be quite complex. I am not sure if anyone could have actually relied on the previous behaviour. If we think that this was a supported use case, we will probably have to make this feature opt-in.

@ro0NL
Copy link
Contributor

ro0NL commented Jul 10, 2018

Shouldnt we, for consistency, raise a violation per case:

if (!is_numeric($value) && !$value instanceof \DateTimeInterface) {
$this->context->buildViolation($constraint->invalidMessage)
->setParameter('{{ value }}', $this->formatValue($value, self::PRETTY_DATE))
->setCode(Range::INVALID_CHARACTERS_ERROR)
->addViolation();
return;
}

and basically get rid of UnexpectedTypeException for this case.

@stoccc
Copy link
Contributor

stoccc commented Jul 10, 2018

Imho a validator should not throw exceptions if you ask to validate unexpected data types, that could come from user or whatever untrusted, it should just add violations.

@ogizanagi
Copy link
Contributor

I agree with @ro0NL , we should handle it per case in each validator. Relates to #26477 (comment) too where I also suggested this. Handling this properly in each validator would not interrupt the validation and keep adding other violations (and answer the AllValidator issue too).
Also we cannot just catch UnexpectedTypeException as this can also be logic exceptions due to a developer mistake that should be fixed (i.e the constraint class check).

@ro0NL
Copy link
Contributor

ro0NL commented Jul 11, 2018

Also i was wondering if we can somehow leverage the Type constraint/validator, in terms of forwarding this validation. To do all the work basically, more concise and to avoid a new error code/message for each constraint i guess.

Maybe with some helper, it could also ease the migration of custom constraint validators.

@xabbuh
Copy link
Member Author

xabbuh commented Sep 24, 2018

@ro0NL Your example in #27917 (comment) is IMO not good in terms of DX. The goal of this PR is precisely to lower the burden when writing constraint validators. But @ogizanagi has a valid point in the exception could as well have been thrown because of the wrong constraint being passed.

What do you think about a different approach like the one @webmozart suggested in #12312?

@ro0NL
Copy link
Contributor

ro0NL commented Sep 24, 2018

Oh wow :) @webmozart created all the tickets already :D

His suggestion indeed looks very good! Except im not sure about proposed strict/loose varying in Type.. we dont really need that IMHO (now we have normalizers as well).

We still agree a constraint violation should be favored over an exception right?

Your example in #27917 (comment) is IMO not good in terms of DX

Still.., we need to trigger type validation somewhere right?

@xabbuh
Copy link
Member Author

xabbuh commented Sep 24, 2018

@ro0NL I still think it's okay to throw an exception in the validator which will then be transformed into a proper constraint violation.

@ogizanagi made another good suggestion in #26477 (comment):

Or perhaps we should introduce a new UnexpectedValueTypeException extending UnexpectedTypeException that'll be caught and transformed into a violation by the component.
We should also inspect more thoroughly other validators.

@ro0NL
Copy link
Contributor

ro0NL commented Sep 24, 2018

👍 for UnexpectedValueTypeException

@xabbuh xabbuh force-pushed the issue-14943 branch 2 times, most recently from 9d30a98 to fac281d Compare September 25, 2018 08:48
@xabbuh xabbuh changed the title [Validator] catch any UnexpectedTypeException on validation [Validator] catch any UnexpectedValueException on validation Sep 25, 2018
@xabbuh
Copy link
Member Author

xabbuh commented Sep 25, 2018

PR updated with a new UnexpectedValueException which will be caught by the validator and transformed into a constraint violation

{
private $expectedType;

public function __construct($value, string $expectedType)
Copy link
Contributor

@ro0NL ro0NL Sep 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about array $expectedTypes. Right now array or Traversable is not really a friendly translation parameter. We should leverage the new message formatter for this :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That assumes it's all unions. It will prevent consumers to use more complicated rules in future, e.g. intersection types. Also, I'm interested how would you improve message array or Traversable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe

throw UnexpectedValueException::notUnionOf($value, $type, ...$type);
throw UnexpectedValueException::notIntersectionOf($value, $type, ...$type);

@ro0NL
Copy link
Contributor

ro0NL commented Oct 4, 2018

From #28645 (comment) not sure the current approach is blocking for @nicolas-grekas

Alternatively, what about exposing the variables and pre-validate as such in i.e. abstract SimpleConstraintValidator:

getConstraintClasses(): array { return [Some::class]; }
getValidTypes(): array { return ['string']; }
allowsNull(): bool { return true; }

E.g. getConstraintClasses() could be guessed, similar like bundle extension class is guessed.

Also #27917 (comment) is still considerable IMHO, that solves translation messages, complex type validation and overall consistency.

Your example in #27917 (comment) is IMO not good in terms of DX. The goal of this PR is precisely to lower the burden when writing constraint validators

I think we can simplify it by adding some util to the base class, e.g. $this->validType().

@xabbuh
Copy link
Member Author

xabbuh commented Oct 4, 2018

Well, the main idea for this PR is not to centralise logic. I mean I would be fine with adding more code to our core constraint validators to achieve the same. The code added here is more important for individual constraint validators written for applications where users do not want to deal with more code.

@ro0NL
Copy link
Contributor

ro0NL commented Oct 4, 2018

Well, the main idea for this PR is not to centralise logic.

But it does exactly that, inferring the constraint violation thru exception in RecursiveContextualValidator. Meaning the constraint might behave differently between validator implementations? It feels a bit like a trick, compared to raising a pure violation from the constraint validator (one way or another).

@nicolas-grekas nicolas-grekas removed this from the next milestone Oct 20, 2018
@nicolas-grekas nicolas-grekas added this to the 4.2 milestone Oct 20, 2018
@nicolas-grekas
Copy link
Member

Do you think we can resolve this before the end of the month to have it in 4.2?

@xabbuh
Copy link
Member Author

xabbuh commented Oct 21, 2018

From my point of view the PR is finished. Do you think we should take another direction?

@@ -34,7 +35,7 @@ public function validate($value, Constraint $constraint)
}

if (!\is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess)) {
throw new UnexpectedTypeException($value, 'array or Traversable and ArrayAccess');
throw new UnexpectedValueException($value, 'array or Traversable and ArrayAccess');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i still think we need to tackle this being translator friendly, it'll only work for english

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. different messages like

  • This value should be of type {{ type }} (generic)
  • This value should be array accessible
  • This value should be iterable
  • This value should be a countable

Copy link
Member

@nicolas-grekas nicolas-grekas Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about using array|(Traversable&ArrayAccess)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds like a plan yes :) the messages dont have to be pretty (tailored), but should be correct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't translate exception messages

Copy link
Contributor

@ro0NL ro0NL Oct 23, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -34,7 +35,7 @@ public function validate($value, Constraint $constraint)
}

if (!\is_array($value) && !$value instanceof \Traversable) {
throw new UnexpectedTypeException($value, 'array or Traversable');
throw new UnexpectedValueException($value, 'array or Traversable');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets use iterable here

@nicolas-grekas
Copy link
Member

Thank you @xabbuh.

@nicolas-grekas nicolas-grekas merged commit fa35860 into symfony:master Oct 24, 2018
nicolas-grekas added a commit that referenced this pull request Oct 24, 2018
…dation (xabbuh)

This PR was merged into the 4.2-dev branch.

Discussion
----------

[Validator] catch any UnexpectedValueException on validation

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #12312
| License       | MIT
| Doc PR        |

Commits
-------

fa35860 catch any UnexpectedValueException on validation
@xabbuh xabbuh deleted the issue-14943 branch October 24, 2018 07:55
This was referenced Nov 3, 2018
chalasr added a commit that referenced this pull request Jan 2, 2024
This PR was merged into the 5.4 branch.

Discussion
----------

[Validator] fix the exception being thrown

| Q             | A
| ------------- | ---
| Branch?       | 5.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        |
| License       | MIT

An `UnexpectedValueException` is caught and transformed into a violation (`UnexpectedTypeException` indicates a misconfiguration of a constraint and is not caught).

see also #27917

Commits
-------

3a8f10b fix the exception being thrown
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants