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

Skip to content

[validator] ctype validators do not respect integer values #38278

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

Closed
Chi-teck opened this issue Sep 23, 2020 · 17 comments
Closed

[validator] ctype validators do not respect integer values #38278

Chi-teck opened this issue Sep 23, 2020 · 17 comments

Comments

@Chi-teck
Copy link
Contributor

Symfony version(s) affected: master

Description
ctype_digit() excepts all numbers to be strings (like: "123") and does not validate real integers (like: 123).

How to reproduce

$violations = Validation::createValidator()->validate(5, new Type('digit'));
foreach ($violations as $violation) {
    echo $violation . "\n";
}

Triggers the following violation.

5:
    This value should be of type digit. (code ba785a8c-82cb-4283-967c-3cf342181b40)

Possible Solution
Cast the value to string before validation.

@Chi-teck
Copy link
Contributor Author

That would a minor BC.

I also wonder if it is a good idea to have a constraint for natural numbers.

@xabbuh
Copy link
Member

xabbuh commented Sep 23, 2020

That's how the ctype_digit() function works. I don't think we want to change that. If you want to validate that a value is an integer, you can use the Type constraint for that.

@Chi-teck
Copy link
Contributor Author

@xabbuh I want to validate that a value is digit. Type('digit') seems to be the right constraint for this. Isn't it? However it simply does not work.

As of ctype_digit() it is just a detail of implementation which a consumer does not have to be aware of.

@xabbuh
Copy link
Member

xabbuh commented Sep 23, 2020

If you want to validate that the value is an integer, Type('int') is the way to go.

As of ctype_digit() it is just a detail of implementation which a consumer does not have to be aware of.

I disagree. We mention this explicitly in the documentation.

@Chi-teck
Copy link
Contributor Author

Type('int') will fail in case of an integer value inside PHP string ($namber = '123'). Also it respects negative numbers.
I need a contstraint to validate a natural number. So far I am using new Regex('^/\d+$/');.

We mention this explicitly in the documentation.

I did not notice it. Anyway I think currently the type validator solves two completely different tasks.

  1. Validates PHP types (is_* functions)
  2. Validates domain types (ctype_* functions)

That brings some inconsistency and confusion.

@Chi-teck Chi-teck changed the title [validator] ctype validators does not respect integer values [validator] ctype validators do not respect integer values Sep 23, 2020
@ro0NL
Copy link
Contributor

ro0NL commented Sep 23, 2020

I think you're looking for @Type('numeric') @Positive :)

btw ctype_digit also matches 0000001 which isnt a natural number either ;)

edit: hm so does is_numeric, i guess it's a php limitation somewhat 😓

@ro0NL
Copy link
Contributor

ro0NL commented Sep 23, 2020

is there any reason to not use the int type (+ Positive[OrZero] constraint) actually?

@Chi-teck
Copy link
Contributor Author

Chi-teck commented Sep 24, 2020

is there any reason to not use the int type (+ Positive[OrZero] constraint) actually?

Such a validator would not be accurate on wrong string values from user input.

var_dump((int) 'Wrong value'); // int(0)

By the way unlike Type('digit') the Positive() constraint works equally on integers and strings. Same applies to all other comparisson constraints.

For the reference here is the implementation of Natural assert. Сourilsly, it works in opposite way to ctype_digit() because it does accept integers but not strings.
https://github.com/webmozart/assert/blob/1.9.1/src/Assert.php#L157

@ro0NL
Copy link
Contributor

ro0NL commented Sep 24, 2020

I think it's related to #35316 also (depending if integer is your actual domain type), but ultimately there's no validation currently for is_int(0) || is_string('0') || is_positive_int || /^[1-9]\d*$/ i agree.

So @NaturalNumber(allowInt=true|false, allowZero=true|false) seems elegant 👍

@Chi-teck
Copy link
Contributor Author

Chi-teck commented Sep 26, 2020

Another example of inaccurate validation.

$violations = Validation::createValidator()->validate('wrong value', new EqualTo(0));
echo count($violations); // 0

Such behaviour may potentially cause security implications in some cases.

@Chi-teck
Copy link
Contributor Author

I think the correct approach to deal with this would be using safe casters wherever it possible. The corresponding RFC in PHP was declained but there is a polifill.

@ro0NL
Copy link
Contributor

ro0NL commented Sep 26, 2020

This is PHP :) there's both loose and strict comparison at the language level: == (EqualTo) and === (IdenticalTo)

Safe casting is interesting but still needs to be implemented per case, as a normalization step; back to # #35316 :)

What i think is the best approach currently, is introducing our own natural_number type for usage with @Type(natural_number), in which case we can handle the int|string case, and everything just works as usual (e.g. can be combined @Positive[OrZero]).

This is also the most obvious decision path for users, either @Type(int) or @Type(natural_number), rather than introducing a new constraint.

edit: IIUC @PositiveOrZero is implied for natural numbers, so you'd combine it with @Positive (not zero) at most.

@Chi-teck
Copy link
Contributor Author

Chi-teck commented Sep 26, 2020

I think currently Type() does to much which breaks the Single Responsibility principle.

Basically all types can be classified in two categories:

  1. PHP built-in types (int, string, bool, etc)
  2. Application types (email, url, digit, natural, etc)

Mixing them is a source of bugs and confusion. I would check with Type() only PHP types. Application types should have individual constarints. Actually most of them are already implemented that way. If we don't have Type('email') constraint why should we prefer Type('natural') over Natural()?

As of ctype functions we could have either a set of separate constraints (Digit(), Alpha(), etc) or a single Ctype() constraint.
And again using safe type casting is critical in many application constraints.

To my mind safe casting worths having a separate component because it can be helpful outside of validator. For instance the behaviour of $request->query->getInt($key) is also confusing. But that's out of scope of this ticket.

@ro0NL
Copy link
Contributor

ro0NL commented Sep 26, 2020

i've no strong opinion on naming/code locations :) but i'd say values fit into 2 different categories

  • typed values (built-in or not)
    • primitive: string, alnum, int, natural_number
    • complex: array, ClassName, ... heck it could support generics even array<int>, unions, intersections, etc.

Technically and semantically all these fit @Type IMHO. Then there are domain specific values: email, etc.

@carsonbot
Copy link

Hey, thanks for your report!
There has not been a lot of activity here for a while. Is this bug still relevant? Have you managed to find a workaround?

@carsonbot
Copy link

Just a quick reminder to make a comment on this. If I don't hear anything I'll close this.

@carsonbot
Copy link

Hey,

I didn't hear anything so I'm going to close it. Feel free to comment if this is still relevant, I can always reopen!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants