mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-16 15:21:13 +08:00
array union in controllers
! fix yii\web\Controller::bindActionParams behaviour for union types o some refactoring in the process
This commit is contained in:
@ -133,41 +133,15 @@ class Controller extends \yii\base\Controller
|
||||
$name = $param->getName();
|
||||
if (array_key_exists($name, $params)) {
|
||||
$isValid = true;
|
||||
$isArray = ($type = $param->getType()) instanceof \ReflectionNamedType && $type->getName() === 'array';
|
||||
if ($isArray) {
|
||||
$params[$name] = (array)$params[$name];
|
||||
} elseif (is_array($params[$name])) {
|
||||
$isValid = false;
|
||||
} elseif (
|
||||
PHP_VERSION_ID >= 70000
|
||||
&& ($type = $param->getType()) !== null
|
||||
&& method_exists($type, 'isBuiltin')
|
||||
&& $type->isBuiltin()
|
||||
&& ($params[$name] !== null || !$type->allowsNull())
|
||||
) {
|
||||
$typeName = PHP_VERSION_ID >= 70100 ? $type->getName() : (string)$type;
|
||||
|
||||
if ($params[$name] === '' && $type->allowsNull()) {
|
||||
if ($typeName !== 'string') { // for old string behavior compatibility
|
||||
$params[$name] = null;
|
||||
}
|
||||
} else {
|
||||
switch ($typeName) {
|
||||
case 'int':
|
||||
$params[$name] = filter_var($params[$name], FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
|
||||
break;
|
||||
case 'float':
|
||||
$params[$name] = filter_var($params[$name], FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);
|
||||
break;
|
||||
case 'bool':
|
||||
$params[$name] = filter_var($params[$name], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
break;
|
||||
}
|
||||
if ($params[$name] === null) {
|
||||
$isValid = false;
|
||||
}
|
||||
}
|
||||
$type = $param->getType();
|
||||
if ($type instanceof \ReflectionNamedType) {
|
||||
[$result, $isValid] = $this->filterSingleTypeActionParam($params[$name], $type);
|
||||
$params[$name] = $result;
|
||||
} elseif (class_exists('\ReflectionUnionType') && $type instanceof \ReflectionUnionType) {
|
||||
[$result, $isValid] = $this->filterUnionTypeActionParam($params[$name], $type);
|
||||
$params[$name] = $result;
|
||||
}
|
||||
|
||||
if (!$isValid) {
|
||||
throw new BadRequestHttpException(
|
||||
Yii::t('yii', 'Invalid data received for parameter "{param}".', ['param' => $name])
|
||||
@ -211,6 +185,123 @@ class Controller extends \yii\base\Controller
|
||||
return $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* The logic for [[bindActionParam]] to validate whether a given parameter matches the action's typing
|
||||
* if the function parameter has a single named type.
|
||||
* @param mixed $param The parameter value.
|
||||
* @param \ReflectionNamedType $type
|
||||
* @return array{0: mixed, 1: bool} The resulting parameter value and a boolean indicating whether the value is valid.
|
||||
*/
|
||||
private function filterSingleTypeActionParam($param, $type)
|
||||
{
|
||||
$isArray = $type->getName() === 'array';
|
||||
if ($isArray) {
|
||||
return [(array)$param, true];
|
||||
}
|
||||
|
||||
if (is_array($param)) {
|
||||
return [$param, false];
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 70000
|
||||
&& method_exists($type, 'isBuiltin')
|
||||
&& $type->isBuiltin()
|
||||
&& ($param !== null || !$type->allowsNull())) {
|
||||
$typeName = PHP_VERSION_ID >= 70100 ? $type->getName() : (string)$type;
|
||||
if ($param === '' && $type->allowsNull()) {
|
||||
if ($typeName !== 'string') { // for old string behavior compatibility
|
||||
return [null, true];
|
||||
}
|
||||
return ['', true];
|
||||
}
|
||||
|
||||
$filterResult = $this->filterParamByType($param, $typeName);
|
||||
return [$filterResult, $filterResult !== null];
|
||||
}
|
||||
return [$param, true];
|
||||
}
|
||||
|
||||
/**
|
||||
* The logic for [[bindActionParam]] to validate whether a given parameter matches the action's typing
|
||||
* if the function parameter has a union type.
|
||||
* @param mixed $param The parameter value.
|
||||
* @param \ReflectionUnionType $type
|
||||
* @return array{0: mixed, 1: bool} The resulting parameter value and a boolean indicating whether the value is valid.
|
||||
*/
|
||||
private function filterUnionTypeActionParam($param, $type)
|
||||
{
|
||||
$types = $type->getTypes();
|
||||
if ($param === '' && $type->allowsNull()) {
|
||||
// check if type can be string for old string behavior compatibility
|
||||
foreach ($types as $partialType) {
|
||||
if ($partialType === null
|
||||
|| !method_exists($partialType, 'isBuiltin')
|
||||
|| !$partialType->isBuiltin()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$typeName = PHP_VERSION_ID >= 70100 ? $partialType->getName() : (string)$partialType;
|
||||
if ($typeName === 'string') {
|
||||
return ['', true];
|
||||
}
|
||||
}
|
||||
return [null, true];
|
||||
}
|
||||
// if we found a built-in type but didn't return out, its validation failed
|
||||
$foundBuiltinType = false;
|
||||
// we save returning out an array or string for later because other types should take precedence
|
||||
$canBeArray = false;
|
||||
$canBeString = false;
|
||||
foreach ($types as $partialType) {
|
||||
if ($partialType === null
|
||||
|| !method_exists($partialType, 'isBuiltin')
|
||||
|| !$partialType->isBuiltin()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$foundBuiltinType = true;
|
||||
$typeName = PHP_VERSION_ID >= 70100 ? $partialType->getName() : (string)$partialType;
|
||||
$canBeArray |= $typeName === 'array';
|
||||
$canBeString |= $typeName === 'string';
|
||||
if (is_array($param)) {
|
||||
if ($canBeArray) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$filterResult = $this->filterParamByType($param, $typeName);
|
||||
if ($filterResult !== null) {
|
||||
return [$filterResult, true];
|
||||
}
|
||||
}
|
||||
if (!is_array($param) && $canBeString) {
|
||||
return [$param, true];
|
||||
}
|
||||
if ($canBeArray) {
|
||||
return [(array)$param, true];
|
||||
}
|
||||
return [$param, $canBeString || !$foundBuiltinType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the according filter_var logic for teh given type.
|
||||
* @param string $param The value to filter.
|
||||
* @param string $typeName The type name.
|
||||
* @return mixed|null The resulting value, or null if validation failed or the type can't be validated.
|
||||
*/
|
||||
private function filterParamByType(string $param, string $typeName) {
|
||||
switch ($typeName) {
|
||||
case 'int':
|
||||
return filter_var($param, FILTER_VALIDATE_INT, FILTER_NULL_ON_FAILURE);
|
||||
case 'float':
|
||||
return filter_var($param, FILTER_VALIDATE_FLOAT, FILTER_NULL_ON_FAILURE);
|
||||
case 'bool':
|
||||
return filter_var($param, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
Reference in New Issue
Block a user