Yii2: StringValidator fails on integer

Created on 4 Jan 2017  路  15Comments  路  Source: yiisoft/yii2

I don't really know if it is a bug or a "feature". But String Validator fails when the attribute contains an integer. But if doesn't fail if you set the value from a form submit.

What steps will reproduce the problem?

Define a string rule in any ActiveRecord model.
[['value'], 'string', 'max' => 255],

Set the value this way
$model->value=123;
$model->save();

What is the expected result?

I expected a conversion or a validation error.

What do you get instead?

$model not save and no error message
screen shot 2017-01-04 at 17 05 03

But, when submitting a form it works.

Additional info

| Q | A
| ---------------- | ---
| Yii version | 2.0.10
| PHP version | 7.0.10
| Operating system |OSX

bug

Most helpful comment

Source-code docs say:

Note, this validator should only be used with string-typed attributes.

I'd propose to add a property strict which does the check on the type of the value.

All 15 comments

Thank you for the detailed description.
This behavior is expected.

it is an inconsistent behavior compared to the NumberValidator
NumberValidator validate the value to be a valid integer or double, do not care if the value is a string|integer|double
StringValidator does a strict validation so the value must be string type

in StringValidator

if (!is_string($value))

is_string check the type.
in NumberValidator there is a loose type validation

if (is_array($value) || is_object($value))

which basically (beside the format) accept any scalar value, even a boolean is fine
By the way the type checking in NumberValidator is incomplete I'll fill another bug for this.

Source-code docs say:

Note, this validator should only be used with string-typed attributes.

I'd propose to add a property strict which does the check on the type of the value.

Yes a string attribute for which "123" and 123 should be valid value, why you want oblige me to typecast an integer value when assigning it to a string attribute?

Can you imagine if you do the same for number fields (which would make more sense)? You must typecast any number value coming from an html form.

In my use case the problem is when importing data from an excell sheet.
There is a column which can contain either numbers or alphanumeric (is a code column). When it is a number the excell class typecast it to integer and when I save I get an error.
Since db column and excell column have same name I use the row from excell to instantiate my model.
I do not assign value singularly so I should code more to do an unnecessary typecast.

So again if for NumberValidator either "123" and 123 are both valid number, why for StringValidator is not the same?

By the way StringValidator will fail also for null value which should be fine, since most db see null and '' as same value unless you do a specific search for null

Yes a string attribute for which "123" and 123 should be valid value, why you want oblige me to typecast an integer value when assigning it to a string attribute?

I can just assume a basic string validator would not make much sense otherwise.

But you are right, the behaviour of the validators should be consistent.

@juanmacias Was this different on PHP 5, btw?

I can say that php 5 (even if I didn't test it) has same behavior, the problem is in this part of the code:

    protected function validateValue($value)
    {
        if (!is_string($value)) {
            return [$this->message, []];
        }

is_string($value) perform a type checking on the value so anything that is not a string would fail, even null. Its behavior didn't change between php5 and php7

I can just assume a basic string validator would not make much sense otherwise.

Well string validator is mainly to check length, not for datatype

@silverfire don't you think this is to be considered a bug or at least an unwanted behavior and should be reopened?

By the way StringValidator will fail also for null value which should be fine, since most db see null and '' as same value unless you do a specific search for null

There are a lot more subtle differences; like aggregate functions or joins...

So again if for NumberValidator either "123" and 123 are both valid number, why for StringValidator is not the same?

There is also another difference: data coming from the browser is always typed as a string, so it is quite unexpected if you are getting integers all of a sudden.
Of course there are plenty of valid explanations like submitting JSON or XML. Note that a string validator on an integer is often a bad sign while the reverse is not true..

In my opinion neither int nor NULL should not be a valid string.
The only argument for the other direction NumberValidator is the fact that data coming from the browser is mostly string typed and seldomly would you save a field as text to the database when using such a validator...

I agree with @SamMousa there are valid cases where the current behavior is expected. In your case, adding strict property as suggested by @schmunk42 would help. It should be default to true in this case though.

In my opinion neither int nor NULL should not be a valid string.

actually it is impossible to assign null value to a varchar field. Allowing null data in column is a choice by design and null value can have a specific meaning in the implementation:

            "filed_1"=> $this->string(50)->notNull(),
            "filed_2"=> $this->string(50)->null()->defaultValue(null),

So if in application I want to assign null to field_2, how should I do if string validator keep failing on a valid value by design?

There is also another difference: data coming from the browser is always typed as a string,

I know that, and thats why only few till now had problem with string validator and that's the reason that number validator do not check the type. (no casting to specific numerical type leads to a different problem #11741)

Nowadays in which we implement architecture where data can come from different sources, not only web form, more and more you risk to get invalid validation for string just because of wrong datatype.

I do not say that there should not be validation on type at all, resource and object for instance should not be allowed (like in integer validator)

adding strict property

Indeed this would solve the problem giving the developer the choice, and probably this should be added to some other validator too (ie integer and double)

should be default to true

for sure this would keep the current behaviors of the of string validator but this would give a different default compared to other validators which default strict=>false
Validator with strict flag which default to false: boolean, in, required

So the choice are:
having different strict default on string validators
having same default but string validators change behavior

Did this ever get implemented. Would love to pass strict => false and have it cast the value to string before running the validator?

Did this ever get implemented

No, the issue is open.

As I've also been encountering this I've put a work around using is_scalar in my project by overriding the StringValidator.

If a $strict property is added, adding something like the following to the StringValidator before lines 108 and 132 should do the trick.

if (!$this->strict && is_scalar($value) && !is_string($value)) {
    $value = (string)$value;
}
Was this page helpful?
0 / 5 - 0 ratings