Browse Source

Fix #17810: Fix EachValidator crashing with uninitialized typed properties

tags/2.0.35
ricardomm85 5 years ago committed by GitHub
parent
commit
355ca1562d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      framework/CHANGELOG.md
  2. 17
      framework/base/DynamicModel.php
  3. 50
      framework/validators/EachValidator.php
  4. 5
      tests/data/validators/models/FakedValidationModel.php
  5. 15
      tests/data/validators/models/ValidatorTestTypedPropModel.php
  6. 25
      tests/framework/validators/EachValidatorTest.php

1
framework/CHANGELOG.md

@ -4,6 +4,7 @@ Yii Framework 2 Change Log
2.0.35 under development
------------------------
- Bug #17810: Fix EachValidator crashing with uninitialized typed properties (ricardomm85)
- Bug #17942: Fix for `DbCache` loop in MySQL `QueryBuilder` (alex-code)

17
framework/base/DynamicModel.php

@ -174,15 +174,26 @@ class DynamicModel extends Model
* You can also directly manipulate [[validators]] to add or remove validation rules.
* This method provides a shortcut.
* @param string|array $attributes the attribute(s) to be validated by the rule
* @param mixed $validator the validator for the rule.This can be a built-in validator name,
* a method name of the model class, an anonymous function, or a validator class name.
* @param string|Validator|\Closure $validator the validator. This can be either:
* * a built-in validator name listed in [[builtInValidators]];
* * a method name of the model class;
* * an anonymous function;
* * a validator class name.
* * a Validator.
* @param array $options the options (name-value pairs) to be applied to the validator
* @return $this the model itself
*/
public function addRule($attributes, $validator, $options = [])
{
$validators = $this->getValidators();
$validators->append(Validator::createValidator($validator, $this, (array)$attributes, $options));
if ($validator instanceof Validator) {
$validator->attributes = (array)$attributes;
} else {
$validator = Validator::createValidator($validator, $this, (array)$attributes, $options);
}
$validators->append($validator);
return $this;
}

50
framework/validators/EachValidator.php

@ -8,6 +8,7 @@
namespace yii\validators;
use Yii;
use yii\base\DynamicModel;
use yii\base\InvalidConfigException;
use yii\base\Model;
@ -128,43 +129,38 @@ class EachValidator extends Validator
*/
public function validateAttribute($model, $attribute)
{
$value = $model->$attribute;
if (!is_array($value) && !$value instanceof \ArrayAccess) {
$arrayOfValues = $model->$attribute;
if (!is_array($arrayOfValues) && !$arrayOfValues instanceof \ArrayAccess) {
$this->addError($model, $attribute, $this->message, []);
return;
}
$validator = $this->getValidator($model); // ensure model context while validator creation
$dynamicModel = new DynamicModel($model->getAttributes());
$dynamicModel->addRule($attribute, $this->getValidator($model));
foreach ($arrayOfValues as $k => $v) {
$dynamicModel->defineAttribute($attribute, $v);
$dynamicModel->validate();
$arrayOfValues[$k] = $dynamicModel->$attribute; // filtered values like 'trim'
$detectedErrors = $model->getErrors($attribute);
$filteredValue = $model->$attribute;
foreach ($value as $k => $v) {
$model->clearErrors($attribute);
$model->$attribute = $v;
if (!$validator->skipOnEmpty || !$validator->isEmpty($v)) {
$validator->validateAttribute($model, $attribute);
if (!$dynamicModel->hasErrors($attribute)) {
continue;
}
$filteredValue[$k] = $model->$attribute;
if ($model->hasErrors($attribute)) {
if ($this->allowMessageFromRule) {
$validationErrors = $model->getErrors($attribute);
$detectedErrors = array_merge($detectedErrors, $validationErrors);
} else {
$model->clearErrors($attribute);
$this->addError($model, $attribute, $this->message, ['value' => $v]);
$detectedErrors[] = $model->getFirstError($attribute);
}
$model->$attribute = $value;
if ($this->stopOnFirstError) {
break;
}
if ($this->allowMessageFromRule) {
$validationErrors = $dynamicModel->getErrors($attribute);
$model->addErrors([$attribute => $validationErrors]);
} else {
$this->addError($model, $attribute, $this->message, ['value' => $v]);
}
if ($this->stopOnFirstError) {
break;
}
}
$model->$attribute = $filteredValue;
$model->clearErrors($attribute);
$model->addErrors([$attribute => $detectedErrors]);
$model->$attribute = $arrayOfValues;
}
/**

5
tests/data/validators/models/FakedValidationModel.php

@ -88,4 +88,9 @@ class FakedValidationModel extends Model
{
return $this->inlineValArgs;
}
public function attributes()
{
return array_keys($this->attr);
}
}

15
tests/data/validators/models/ValidatorTestTypedPropModel.php

@ -0,0 +1,15 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\data\validators\models;
use yii\base\Model;
class ValidatorTestTypedPropModel extends Model
{
public array $arrayTypedProperty = [true, false];
}

25
tests/framework/validators/EachValidatorTest.php

@ -7,11 +7,10 @@
namespace yiiunit\framework\validators;
use yii\db\ArrayExpression;
use yii\validators\EachValidator;
use yiiunit\data\base\ArrayAccessObject;
use yiiunit\data\base\TraversableObject;
use yiiunit\data\validators\models\FakedValidationModel;
use yiiunit\data\validators\models\ValidatorTestTypedPropModel;
use yiiunit\TestCase;
/**
@ -200,4 +199,26 @@ class EachValidatorTest extends TestCase
$this->assertTrue($validator->validate($model->attr_array));
}
/**
* @see https://github.com/yiisoft/yii2/issues/17810
*
* Do not reuse model property for storing value
* of different type during validation.
* (ie: public array $dummy; where $dummy is array of booleans,
* validator will try to assign these booleans one by one to $dummy)
*/
public function testTypedProperties()
{
if (PHP_VERSION_ID < 70400) {
$this->markTestSkipped('Can not be tested on PHP < 7.4');
return;
}
$model = new ValidatorTestTypedPropModel();
$validator = new EachValidator(['rule' => ['boolean']]);
$validator->validateAttribute($model, 'arrayTypedProperty');
$this->assertFalse($model->hasErrors('arrayTypedProperty'));
}
}

Loading…
Cancel
Save