laravel / framework

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

Using single character attributes in validation rules #52656

Closed Douglasdc3 closed 2 months ago

Douglasdc3 commented 2 months ago

Laravel Version

11.22.0

PHP Version

8.3.7

Database Driver & Version

No response

Description

When using the date validators (before, after, after_or_equal, before_or_equal) both with and without date_formats the validator will fail when the length of the key is equal to 1.

Cause

Without date_format rule it is caused by getDateTimestamp inside Illuminate\Validation\Concerns\ValidatesAttributes

    protected function compareDates($attribute, $value, $parameters, $operator)
    {
        // ...
+        if (is_null($date = $this->getDateTimestamp($parameters[0]))) {
            $date = $this->getDateTimestamp($this->getValue($parameters[0]));
        }

        return $this->compare($this->getDateTimestamp($value), $date, $operator);
    }

When looking closer at this methods implementation it contains a reference to getDateTime

    protected function getDateTime($value)
    {
        try {
            return @Date::parse($value) ?: null;
        } catch (Exception) {
            //
        }
    }

When given bar this method outputs null (as Date::parse exception is silenced). When given b the method outputs a Date Carbon object containing the current date + a timezone offset based on the letter given where a = 1 and b = 2 etc..

Similarly when the date_format validation rule is provided another method checkDateTimeOrder inside the same class is called which also performs a similar check

Since current date time object is returned instead of null both will check the rules against now rather then the referenced attributed.

Possible fix

As possible fix we could check if the referenced attribute exists if it does use it if it doesn't assume the value provided is a a strtotime compatible value.

However when testing this out one test breaks (see referenced PR) where if the field being referenced is a valid also a date input

$v = new Validator($trans, ['x' => '2038-01-18', '2018-05-12' => '2038-01-19'], ['x' => 'date_format:Y-m-d|before:2018-05-12']);
$this->assertTrue($v->fails());

Steps To Reproduce

$validator = validator()->make([
    'foo' => '2024-01-02',
    'bar' => '2024-01-01'
], [
    'foo' => 'after:bar',
]);

dump($validator->fails()); // False

$validator = validator()->make([
    'foo' => '2024-01-02',
    'b' => '2024-01-01'
], [
    'foo' => 'after:b',
]);

dump($validator->fails()); // True

The same happens with the following

$validator = validator()->make([
    'foo' => '2024-01-02',
    'bar' => '2024-01-01'
], [
    'foo' => 'date_format:Y-m-d|after:bar',
]);

dump($validator->fails()); // False

$validator = validator()->make([
    'foo' => '2024-01-02',
    'b' => '2024-01-01'
], [
    'foo' => 'date_format:Y-m-d|after:b',
]);

dump($validator->fails()); // True
driesvints commented 2 months ago

Thanks. You sent in a PR so let's see how it goes.