mirror of
				https://github.com/yiisoft/yii2.git
				synced 2025-11-04 06:37:55 +08:00 
			
		
		
		
	
				
					committed by
					
						
						Carsten Brandt
					
				
			
			
				
	
			
			
			
						parent
						
							cf51d46a75
						
					
				
				
					commit
					3f24954659
				
			@ -46,6 +46,7 @@ Yii Framework 2 Change Log
 | 
			
		||||
- Enh #7409: Allow `yii\filters\auth\CompositeAuth::authMethods` to take authentication objects (fernandezekiel, qiangxue)
 | 
			
		||||
- Enh #7443: Allow specification of the `$key` as an array at `yii\helpers\ArrayHelper::getValue()` (Alex-Code)
 | 
			
		||||
- Enh #7488: Added `StringHelper::explode` to perform explode with trimming and skipping of empty elements (SilverFire, nineinchnick, creocoder, samdark)
 | 
			
		||||
- Enh #7514: Added min/max validation to DateValidator (nkovacs)
 | 
			
		||||
- Enh #7515: Added support to use `indexBy()` together with `column()` in query builder (qiangxue)
 | 
			
		||||
- Enh #7530: Improved default values for `yii\data\Sort` link labels in a `ListView` when used with an `ActiveDataProvider` (cebe)
 | 
			
		||||
- Enh #7539: `yii\console\controllers\AssetController` provides dependency trace in case bundle circular dependency detected (klimov-paul)
 | 
			
		||||
 | 
			
		||||
@ -7,11 +7,11 @@
 | 
			
		||||
 | 
			
		||||
namespace yii\validators;
 | 
			
		||||
 | 
			
		||||
use DateTimeInterface;
 | 
			
		||||
use DateTime;
 | 
			
		||||
use IntlDateFormatter;
 | 
			
		||||
use Yii;
 | 
			
		||||
use DateTime;
 | 
			
		||||
use yii\base\Exception;
 | 
			
		||||
use yii\base\InvalidConfigException;
 | 
			
		||||
use yii\helpers\FormatConverter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -90,6 +90,44 @@ class DateValidator extends Validator
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $timestampAttributeTimeZone = 'UTC';
 | 
			
		||||
    /**
 | 
			
		||||
     * @var integer|string upper limit of the date. Defaults to null, meaning no upper limit.
 | 
			
		||||
     * This can be a unix timestamp or a string representing a date time value.
 | 
			
		||||
     * If this property is a string, [[format]] will be used to parse it.
 | 
			
		||||
     * @see tooBig for the customized message used when the date is too big.
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $max;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var integer|string lower limit of the date. Defaults to null, meaning no lower limit.
 | 
			
		||||
     * This can be a unix timestamp or a string representing a date time value.
 | 
			
		||||
     * If this property is a string, [[format]] will be used to parse it.
 | 
			
		||||
     * @see tooSmall for the customized message used when the date is too small.
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $min;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string user-defined error message used when the value is bigger than [[max]].
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $tooBig;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string user-defined error message used when the value is smaller than [[min]].
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $tooSmall;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string user friendly value of upper limit to display in the error message.
 | 
			
		||||
     * If this property is null, the value of [[max]] will be used (before parsing).
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $maxString;
 | 
			
		||||
    /**
 | 
			
		||||
     * @var string user friendly value of lower limit to display in the error message.
 | 
			
		||||
     * If this property is null, the value of [[min]] will be used (before parsing).
 | 
			
		||||
     * @since 2.0.4
 | 
			
		||||
     */
 | 
			
		||||
    public $minString;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @var array map of short format names to IntlDateFormatter constant values.
 | 
			
		||||
@ -120,6 +158,32 @@ class DateValidator extends Validator
 | 
			
		||||
        if ($this->timeZone === null) {
 | 
			
		||||
            $this->timeZone = Yii::$app->timeZone;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->min !== null && $this->tooSmall === null) {
 | 
			
		||||
            $this->tooSmall = Yii::t('yii', '{attribute} must be no less than {min}.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->max !== null && $this->tooBig === null) {
 | 
			
		||||
            $this->tooBig = Yii::t('yii', '{attribute} must be no greater than {max}.');
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->maxString === null) {
 | 
			
		||||
            $this->maxString = (string)$this->max;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->minString === null) {
 | 
			
		||||
            $this->minString = (string)$this->min;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->max !== null && is_string($this->max)) {
 | 
			
		||||
            $timestamp = $this->parseDateValue($this->max);
 | 
			
		||||
            if ($timestamp === false) {
 | 
			
		||||
                throw new InvalidConfigException("Invalid max date value: {$this->max}");
 | 
			
		||||
            }
 | 
			
		||||
            $this->max = $timestamp;
 | 
			
		||||
        }
 | 
			
		||||
        if ($this->min !== null && is_string($this->min)) {
 | 
			
		||||
            $timestamp = $this->parseDateValue($this->min);
 | 
			
		||||
            if ($timestamp === false) {
 | 
			
		||||
                throw new InvalidConfigException("Invalid min date value: {$this->min}");
 | 
			
		||||
            }
 | 
			
		||||
            $this->min = $timestamp;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -131,6 +195,10 @@ class DateValidator extends Validator
 | 
			
		||||
        $timestamp = $this->parseDateValue($value);
 | 
			
		||||
        if ($timestamp === false) {
 | 
			
		||||
            $this->addError($model, $attribute, $this->message, []);
 | 
			
		||||
        } elseif ($this->min !== null && $timestamp < $this->min) {
 | 
			
		||||
            $this->addError($model, $attribute, $this->tooSmall, ['min' => $this->minString]);
 | 
			
		||||
        } elseif ($this->max !== null && $timestamp > $this->max) {
 | 
			
		||||
            $this->addError($model, $attribute, $this->tooBig, ['max' => $this->maxString]);
 | 
			
		||||
        } elseif ($this->timestampAttribute !== null) {
 | 
			
		||||
            if ($this->timestampAttributeFormat === null) {
 | 
			
		||||
                $model->{$this->timestampAttribute} = $timestamp;
 | 
			
		||||
@ -145,14 +213,23 @@ class DateValidator extends Validator
 | 
			
		||||
     */
 | 
			
		||||
    protected function validateValue($value)
 | 
			
		||||
    {
 | 
			
		||||
        return $this->parseDateValue($value) === false ? [$this->message, []] : null;
 | 
			
		||||
        $timestamp = $this->parseDateValue($value);
 | 
			
		||||
        if ($timestamp === false) {
 | 
			
		||||
            return [$this->message, []];
 | 
			
		||||
        } elseif ($this->min !== null && $timestamp < $this->min) {
 | 
			
		||||
            return [$this->tooSmall, ['min' => $this->minString]];
 | 
			
		||||
        } elseif ($this->max !== null && $timestamp > $this->max) {
 | 
			
		||||
            return [$this->tooBig, ['max' => $this->maxString]];
 | 
			
		||||
        } else {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Parses date string into UNIX timestamp
 | 
			
		||||
     *
 | 
			
		||||
     * @param string $value string representing date
 | 
			
		||||
     * @return integer|boolean a UNIX timestamp or `false` on failure.
 | 
			
		||||
     * @return integer|false a UNIX timestamp or `false` on failure.
 | 
			
		||||
     */
 | 
			
		||||
    protected function parseDateValue($value)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ use yii\validators\DateValidator;
 | 
			
		||||
use yiiunit\data\validators\models\FakedValidationModel;
 | 
			
		||||
use yiiunit\framework\i18n\IntlTestHelper;
 | 
			
		||||
use yiiunit\TestCase;
 | 
			
		||||
use IntlDateFormatter;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @group validators
 | 
			
		||||
@ -409,4 +410,113 @@ class DateValidatorTest extends TestCase
 | 
			
		||||
        $this->assertFalse($model->hasErrors('attr_timestamp'));
 | 
			
		||||
        $this->assertSame('2013-09-13 10:23:15', $model->attr_timestamp);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIntlValidateRange()
 | 
			
		||||
    {
 | 
			
		||||
        $this->testValidateValueRange();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testValidateValueRange()
 | 
			
		||||
    {
 | 
			
		||||
        $date = '14-09-13';
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd']);
 | 
			
		||||
        $this->assertTrue($val->validate($date), "$date is valid");
 | 
			
		||||
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'min' => '1900-01-01']);
 | 
			
		||||
        $date = "1958-01-12";
 | 
			
		||||
        $this->assertTrue($val->validate($date), "$date is valid");
 | 
			
		||||
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'max' => '2000-01-01']);
 | 
			
		||||
        $date = '2014-09-13';
 | 
			
		||||
        $this->assertFalse($val->validate($date), "$date is too big");
 | 
			
		||||
        $date = "1958-01-12";
 | 
			
		||||
        $this->assertTrue($val->validate($date), "$date is valid");
 | 
			
		||||
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'min' => '1900-01-01', 'max' => '2000-01-01']);
 | 
			
		||||
        $this->assertTrue($val->validate('1999-12-31'), "max -1 day is valid");
 | 
			
		||||
        $this->assertTrue($val->validate('2000-01-01'), "max is inside range");
 | 
			
		||||
        $this->assertTrue($val->validate('1900-01-01'), "min is inside range");
 | 
			
		||||
        $this->assertFalse($val->validate('1899-12-31'), "min -1 day is invalid");
 | 
			
		||||
        $this->assertFalse($val->validate('2000-01-02'), "max +1 day is invalid");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function validateModelAttribute($validator, $date, $expected, $message = '')
 | 
			
		||||
    {
 | 
			
		||||
        $model = new FakedValidationModel;
 | 
			
		||||
        $model->attr_date = $date;
 | 
			
		||||
        $validator->validateAttribute($model, 'attr_date');
 | 
			
		||||
        if (!$expected) {
 | 
			
		||||
            $this->assertTrue($model->hasErrors('attr_date'), $message);
 | 
			
		||||
        } else {
 | 
			
		||||
            $this->assertFalse($model->hasErrors('attr_date'), $message);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIntlValidateAttributeRange() {
 | 
			
		||||
        $this->testValidateAttributeRange();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testValidateAttributeRange()
 | 
			
		||||
    {
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd']);
 | 
			
		||||
        $date = '14-09-13';
 | 
			
		||||
        $this->validateModelAttribute($val, $date, true, "$date is valid");
 | 
			
		||||
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'min' => '1900-01-01']);
 | 
			
		||||
        $date = '1958-01-12';
 | 
			
		||||
        $this->validateModelAttribute($val, $date, true, "$date is valid");
 | 
			
		||||
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'max' => '2000-01-01']);
 | 
			
		||||
        $date = '2014-09-13';
 | 
			
		||||
        $this->validateModelAttribute($val, $date, false, "$date is too big");
 | 
			
		||||
        $date = '1958-01-12';
 | 
			
		||||
        $this->validateModelAttribute($val, $date, true, "$date is valid");
 | 
			
		||||
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'min' => '1900-01-01', 'max' => '2000-01-01']);
 | 
			
		||||
        $this->validateModelAttribute($val, '1999-12-31', true, "max -1 day is valid");
 | 
			
		||||
        $this->validateModelAttribute($val, '2000-01-01', true, "max is inside range");
 | 
			
		||||
        $this->validateModelAttribute($val, '1900-01-01', true, "min is inside range");
 | 
			
		||||
        $this->validateModelAttribute($val, '1899-12-31', false, "min -1 day is invalid");
 | 
			
		||||
        $this->validateModelAttribute($val, '2000-01-02', false, "max +1 day is invalid");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIntlValidateValueRangeOld()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->checkOldIcuBug()) {
 | 
			
		||||
            $this->markTestSkipped("ICU is too old.");
 | 
			
		||||
        }
 | 
			
		||||
        $date = '14-09-13';
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'min' => '1900-01-01']);
 | 
			
		||||
        $this->assertFalse($val->validate($date), "$date is too small");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testIntlValidateAttributeRangeOld()
 | 
			
		||||
    {
 | 
			
		||||
        if ($this->checkOldIcuBug()) {
 | 
			
		||||
            $this->markTestSkipped("ICU is too old.");
 | 
			
		||||
        }
 | 
			
		||||
        $date = '14-09-13';
 | 
			
		||||
        $val = new DateValidator(['format' => 'yyyy-MM-dd', 'min' => '1900-01-01']);
 | 
			
		||||
        $this->validateModelAttribute($val, $date, false, "$date is too small");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * returns true if the version of ICU is old and has a bug that makes it
 | 
			
		||||
     * impossible to parse two digit years properly.
 | 
			
		||||
     * see http://bugs.icu-project.org/trac/ticket/9836
 | 
			
		||||
     * @return boolean
 | 
			
		||||
     */
 | 
			
		||||
    private function checkOldIcuBug()
 | 
			
		||||
    {
 | 
			
		||||
        $date = '14';
 | 
			
		||||
        $formatter = new IntlDateFormatter('en-US', IntlDateFormatter::NONE, IntlDateFormatter::NONE, null, null, 'yyyy');
 | 
			
		||||
        $parsePos = 0;
 | 
			
		||||
        $parsedDate = @$formatter->parse($date, $parsePos);
 | 
			
		||||
 | 
			
		||||
        if (is_int($parsedDate) && $parsedDate > 0) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user