laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.69k stars 11.05k forks source link

Validating array where keys contain forward slashes #25146

Closed sorge-it closed 6 years ago

sorge-it commented 6 years ago

Description:

When running validator on an array key which contains a forward slash, I run into an error exception in Validator.php line 2716 (Validator::getExplicitKeys($attribute), where preg_match is run on $pattern generated from the attribute.

I have not found the proper way (if there is any) to escape this forward slash, and note that in my use case the data comes in a pre-specified format which I cannot change. I realise I could change the key before validation, but I am wondering if there is a way to make the validator work as-is, or if the validator is even intended to work in this case.

The bug was already discuessed and fixed in 5.3: https://github.com/laravel/framework/issues/16287 but was re-introduced in 5.4.

Steps To Reproduce:

The following code does not work, since calling $validator->fails() generates the ErrorException.

    $arr = [
            'fine' => 'foo',
                'not/fine' => 'bar'
        ];
        $validator = Validator::make($arr, [
            'fine' => 'required',
                'not/fine' => 'required',
        ]);

        if($validator->fails()) {
            dd('uh-oh');
        }
staudenmeir commented 6 years ago

You must be running an older Laravel version.

It works for me and getExplicitKeys() is now in a different line.

sorge-it commented 6 years ago

Yes and no.

This is the 5.3 patch: https://github.com/laravel/framework/pull/16309/commits/0b04c1da5adc04c05855b17b1b361e2fc304f09b

The exception is thrown in line 61: https://github.com/laravel/framework/blob/5.6/src/Illuminate/Validation/ValidationData.php

54 protected static function extractValuesForWildcards($masterData, $data, $attribute)
55    {
56        $keys = [];
57
57        $pattern = str_replace('\*', '[^\.]+', preg_quote($attribute));
58        foreach ($data as $key => $value) {
59
60            if ((bool) preg_match('/^'.$pattern.'/', $key, $matches)) {
61                $keys[] = $matches[0];

because preg_quote is missing the '/' parameter in line 57. This works fine:

57 $pattern = str_replace('\*', '[^\.]+', preg_quote($attribute, '/'));

Miguel-Serejo commented 6 years ago

Cannot reproduce on 5.6.29:

>>> $arr = [ 'fine' => 'foo', 'not/fine' => 'bar'];
=> [
     "fine" => "foo",
     "not/fine" => "bar",
   ]
>>> $validator = Validator::make($arr, ['fine' => 'required', 'not/fine' => 'required', ]);
=> Illuminate\Validation\Validator {#2942
     +customMessages: [],
     +fallbackMessages: [],
     +customAttributes: [],
     +customValues: [],
     +extensions: [],
     +replacers: [],
   }
>>> $validator->fails()
=> false
>>> $validator->passes()
=> true
staudenmeir commented 6 years ago

Are you using wildcard rules with .*.?

sorge-it commented 6 years ago

Sorry guys, I did not give you enough information.

So here is the failing code (tinker):

>>> \Validator::validate(
            [
                'data' => [
                    'catalog' => [
                        'foo/' => [
                            'attribute1' => null,
                        ],
                    ],
                ],
            ],
            [
                'data.catalog.*.attribute1' => 'sometimes',
            ]
        );
...             [
...                 'data' => [
...                     'catalog' => [
...                         'foo/' => [
...                             'attribute1' => null,
...                         ],
...                     ],
...                 ],
...             ],
...             [
...                 'data.catalog.*.attribute1' => 'sometimes',
...             ]
...         );
PHP Warning:  preg_match(): Unknown modifier '/' in 
xxx/vendor/laravel/framework/src/Illuminate/Validation/ValidationData.php on line 61
>>>

The problem only exists, when you include the sometimes validator. If sometimes is not in the list of validators, the code works fine. In my production code I use sometimes|array|nullable.

Thanks!