mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-02 21:41:19 +08:00
Fix #19031: Fix displaying console help for parameters with declared types
This commit is contained in:
@ -31,6 +31,7 @@ Yii Framework 2 Change Log
|
||||
- Bug #18988: Fix default value of `yii\console\controllers\MessageController::$translator` (WinterSilence)
|
||||
- Bug #18993: Load defaults by `attributes()` in `yii\db\ActiveRecord::loadDefaultValues()` (WinterSilence)
|
||||
- Bug #19021: Fix return type in PhpDoc `yii\db\Migration` functions `up()`, `down()`, `safeUp()` and `safeDown()` (WinterSilence, rhertogh)
|
||||
- Bug #19031: Fix displaying console help for parameters with declared types (WinterSilence)
|
||||
- Bug #19030: Add DI container usage to `yii\base\Widget::end()` (papppeter)
|
||||
|
||||
|
||||
|
||||
@ -542,60 +542,69 @@ class Controller extends \yii\base\Controller
|
||||
* The returned value should be an array. The keys are the argument names, and the values are
|
||||
* the corresponding help information. Each value must be an array of the following structure:
|
||||
*
|
||||
* - required: boolean, whether this argument is required.
|
||||
* - type: string, the PHP type of this argument.
|
||||
* - default: string, the default value of this argument
|
||||
* - comment: string, the comment of this argument
|
||||
* - required: bool, whether this argument is required
|
||||
* - type: string|null, the PHP type(s) of this argument
|
||||
* - default: mixed, the default value of this argument
|
||||
* - comment: string, the description of this argument
|
||||
*
|
||||
* The default implementation will return the help information extracted from the doc-comment of
|
||||
* the parameters corresponding to the action method.
|
||||
* The default implementation will return the help information extracted from the Reflection or
|
||||
* DocBlock of the parameters corresponding to the action method.
|
||||
*
|
||||
* @param Action $action
|
||||
* @param Action $action the action instance
|
||||
* @return array the help information of the action arguments
|
||||
*/
|
||||
public function getActionArgsHelp($action)
|
||||
{
|
||||
$method = $this->getActionMethodReflection($action);
|
||||
|
||||
$tags = $this->parseDocCommentTags($method);
|
||||
$params = isset($tags['param']) ? (array) $tags['param'] : [];
|
||||
$tags['param'] = isset($tags['param']) ? (array) $tags['param'] : [];
|
||||
$phpDocParams = [];
|
||||
foreach ($tags['param'] as $i => $tag) {
|
||||
if (preg_match('/^(?<type>\S+)(\s+\$(?<name>\w+))?(?<comment>.*)/us', $tag, $matches) === 1) {
|
||||
$key = empty($matches['name']) ? $i : $matches['name'];
|
||||
$phpDocParams[$key] = ['type' => $matches['type'], 'comment' => $matches['comment']];
|
||||
}
|
||||
}
|
||||
unset($tags);
|
||||
|
||||
$args = [];
|
||||
|
||||
/** @var \ReflectionParameter $reflection */
|
||||
foreach ($method->getParameters() as $i => $reflection) {
|
||||
if (PHP_VERSION_ID >= 80000) {
|
||||
$class = $reflection->getType();
|
||||
} else {
|
||||
$class = $reflection->getClass();
|
||||
/** @var \ReflectionParameter $parameter */
|
||||
foreach ($method->getParameters() as $i => $parameter) {
|
||||
$type = null;
|
||||
$comment = '';
|
||||
if (PHP_MAJOR_VERSION > 5 && $parameter->hasType()) {
|
||||
$reflectionType = $parameter->getType();
|
||||
if (PHP_VERSION_ID >= 70100) {
|
||||
$types = method_exists($reflectionType, 'getTypes') ? $reflectionType->getTypes() : [$reflectionType];
|
||||
foreach ($types as $key => $reflectionType) {
|
||||
$types[$key] = $reflectionType->getName();
|
||||
}
|
||||
$type = implode('|', $types);
|
||||
} else {
|
||||
$type = (string) $reflectionType;
|
||||
}
|
||||
}
|
||||
// find PhpDoc tag by property name or position
|
||||
$key = isset($phpDocParams[$parameter->name]) ? $parameter->name : (isset($phpDocParams[$i]) ? $i : null);
|
||||
if ($key !== null) {
|
||||
$comment = $phpDocParams[$key]['comment'];
|
||||
if ($type === null && !empty($phpDocParams[$key]['type'])) {
|
||||
$type = $phpDocParams[$key]['type'];
|
||||
}
|
||||
}
|
||||
// if type still not detected, then using type of default value
|
||||
if ($type === null && $parameter->isDefaultValueAvailable() && $parameter->getDefaultValue() !== null) {
|
||||
$type = gettype($parameter->getDefaultValue());
|
||||
}
|
||||
|
||||
if ($class !== null) {
|
||||
continue;
|
||||
}
|
||||
$name = $reflection->getName();
|
||||
$tag = isset($params[$i]) ? $params[$i] : '';
|
||||
if (preg_match('/^(\S+)\s+(\$\w+\s+)?(.*)/s', $tag, $matches)) {
|
||||
$type = $matches[1];
|
||||
$comment = $matches[3];
|
||||
} else {
|
||||
$type = null;
|
||||
$comment = $tag;
|
||||
}
|
||||
if ($reflection->isDefaultValueAvailable()) {
|
||||
$args[$name] = [
|
||||
'required' => false,
|
||||
'type' => $type,
|
||||
'default' => $reflection->getDefaultValue(),
|
||||
'comment' => $comment,
|
||||
];
|
||||
} else {
|
||||
$args[$name] = [
|
||||
'required' => true,
|
||||
'type' => $type,
|
||||
'default' => null,
|
||||
'comment' => $comment,
|
||||
];
|
||||
}
|
||||
$args[$parameter->name] = [
|
||||
'required' => !$parameter->isOptional(),
|
||||
'type' => $type,
|
||||
'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,
|
||||
'comment' => $comment,
|
||||
];
|
||||
}
|
||||
|
||||
return $args;
|
||||
|
||||
@ -286,18 +286,24 @@ class ControllerTest extends TestCase
|
||||
$this->assertEquals(FakeHelpController::getActionIndexLastCallParams(), ['news/posts/index']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if action help does not include (class) type hinted arguments.
|
||||
* @see #10372
|
||||
* @see https://github.com/yiisoft/yii2/issues/19028
|
||||
*/
|
||||
public function testHelpSkipsTypeHintedArguments()
|
||||
public function testGetActionArgsHelp()
|
||||
{
|
||||
$controller = new FakeController('fake', Yii::$app);
|
||||
$help = $controller->getActionArgsHelp($controller->createAction('with-complex-type-hint'));
|
||||
$help = $controller->getActionArgsHelp($controller->createAction('aksi2'));
|
||||
|
||||
$this->assertArrayNotHasKey('typedArgument', $help);
|
||||
$this->assertArrayHasKey('simpleArgument', $help);
|
||||
$this->assertArrayHasKey('values', $help);
|
||||
if (PHP_MAJOR_VERSION > 5) {
|
||||
// declared type
|
||||
$this->assertEquals('array', $help['values']['type']);
|
||||
} else {
|
||||
$this->markTestSkipped('Can not test declared type of parameter $values on PHP < 7.0');
|
||||
}
|
||||
$this->assertArrayHasKey('value', $help);
|
||||
// PHPDoc type
|
||||
$this->assertEquals('string', $help['value']['type']);
|
||||
}
|
||||
|
||||
public function testGetActionHelpSummaryOnNull()
|
||||
|
||||
@ -57,9 +57,13 @@ class FakeController extends Controller
|
||||
|
||||
public function actionAksi1($fromParam, $other = 'default')
|
||||
{
|
||||
return[$fromParam, $other];
|
||||
return [$fromParam, $other];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value the string value
|
||||
* @return array
|
||||
*/
|
||||
public function actionAksi2(array $values, $value)
|
||||
{
|
||||
return [$values, $value];
|
||||
@ -89,11 +93,6 @@ class FakeController extends Controller
|
||||
return func_get_args();
|
||||
}
|
||||
|
||||
public function actionWithComplexTypeHint(self $typedArgument, $simpleArgument)
|
||||
{
|
||||
return $simpleArgument;
|
||||
}
|
||||
|
||||
public function actionStatus($status = 0)
|
||||
{
|
||||
return $status;
|
||||
|
||||
@ -123,7 +123,7 @@ STRING
|
||||
]);
|
||||
$result = Console::stripAnsiFormat($this->runControllerAction('list-action-options', ['action' => 'help/list-action-options']));
|
||||
$this->assertEqualsWithoutLE(<<<'STRING'
|
||||
action:route to action
|
||||
action: route to action
|
||||
|
||||
--interactive: whether to run the command interactively.
|
||||
--color: whether to enable ANSI color in the output.If not set, ANSI color will only be enabled for terminals that support it.
|
||||
|
||||
Reference in New Issue
Block a user