123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573 |
- <?php
- /**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
- namespace yii\console\controllers;
- use Yii;
- use yii\base\Application;
- use yii\console\Controller;
- use yii\console\Exception;
- use yii\helpers\Console;
- use yii\helpers\Inflector;
- /**
- * Provides help information about console commands.
- *
- * This command displays the available command list in
- * the application or the detailed instructions about using
- * a specific command.
- *
- * This command can be used as follows on command line:
- *
- * ```
- * yii help [command name]
- * ```
- *
- * In the above, if the command name is not provided, all
- * available commands will be displayed.
- *
- * @property array $commands All available command names. This property is read-only.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @since 2.0
- */
- class HelpController extends Controller
- {
- /**
- * Displays available commands or the detailed information
- * about a particular command.
- *
- * @param string $command The name of the command to show help about.
- * If not provided, all available commands will be displayed.
- * @return int the exit status
- * @throws Exception if the command for help is unknown
- */
- public function actionIndex($command = null)
- {
- if ($command !== null) {
- $result = Yii::$app->createController($command);
- if ($result === false) {
- $name = $this->ansiFormat($command, Console::FG_YELLOW);
- throw new Exception("No help for unknown command \"$name\".");
- }
- list($controller, $actionID) = $result;
- $actions = $this->getActions($controller);
- if ($actionID !== '' || count($actions) === 1 && $actions[0] === $controller->defaultAction) {
- $this->getSubCommandHelp($controller, $actionID);
- } else {
- $this->getCommandHelp($controller);
- }
- } else {
- $this->getDefaultHelp();
- }
- }
- /**
- * List all available controllers and actions in machine readable format.
- * This is used for shell completion.
- * @since 2.0.11
- */
- public function actionList()
- {
- foreach ($this->getCommandDescriptions() as $command => $description) {
- $result = Yii::$app->createController($command);
- if ($result === false || !($result[0] instanceof Controller)) {
- continue;
- }
- /** @var $controller Controller */
- list($controller, $actionID) = $result;
- $actions = $this->getActions($controller);
- if (!empty($actions)) {
- $prefix = $controller->getUniqueId();
- $this->stdout("$prefix\n");
- foreach ($actions as $action) {
- $this->stdout("$prefix/$action\n");
- }
- }
- }
- }
- /**
- * List all available options for the $action in machine readable format.
- * This is used for shell completion.
- *
- * @param string $action route to action
- * @since 2.0.11
- */
- public function actionListActionOptions($action)
- {
- $result = Yii::$app->createController($action);
- if ($result === false || !($result[0] instanceof Controller)) {
- return;
- }
- /** @var Controller $controller */
- list($controller, $actionID) = $result;
- $action = $controller->createAction($actionID);
- if ($action === null) {
- return;
- }
- foreach ($controller->getActionArgsHelp($action) as $argument => $help) {
- $description = preg_replace("~\R~", '', addcslashes($help['comment'], ':')) ?: $argument;
- $this->stdout($argument . ':' . $description . "\n");
- }
- $this->stdout("\n");
- foreach ($controller->getActionOptionsHelp($action) as $argument => $help) {
- $description = preg_replace("~\R~", '', addcslashes($help['comment'], ':'));
- $this->stdout('--' . $argument . ($description ? ':' . $description : '') . "\n");
- }
- }
- /**
- * Displays usage information for $action.
- *
- * @param string $action route to action
- * @since 2.0.11
- */
- public function actionUsage($action)
- {
- $result = Yii::$app->createController($action);
- if ($result === false || !($result[0] instanceof Controller)) {
- return;
- }
- /** @var Controller $controller */
- list($controller, $actionID) = $result;
- $action = $controller->createAction($actionID);
- if ($action === null) {
- return;
- }
- $scriptName = $this->getScriptName();
- if ($action->id === $controller->defaultAction) {
- $this->stdout($scriptName . ' ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW));
- } else {
- $this->stdout($scriptName . ' ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW));
- }
- foreach ($controller->getActionArgsHelp($action) as $name => $arg) {
- if ($arg['required']) {
- $this->stdout(' <' . $name . '>', Console::FG_CYAN);
- } else {
- $this->stdout(' [' . $name . ']', Console::FG_CYAN);
- }
- }
- $this->stdout("\n");
- }
- /**
- * Returns all available command names.
- * @return array all available command names
- */
- public function getCommands()
- {
- $commands = $this->getModuleCommands(Yii::$app);
- sort($commands);
- return array_unique($commands);
- }
- /**
- * Returns an array of commands an their descriptions.
- * @return array all available commands as keys and their description as values.
- */
- protected function getCommandDescriptions()
- {
- $descriptions = [];
- foreach ($this->getCommands() as $command) {
- $description = '';
- $result = Yii::$app->createController($command);
- if ($result !== false && $result[0] instanceof Controller) {
- list($controller, $actionID) = $result;
- /** @var Controller $controller */
- $description = $controller->getHelpSummary();
- }
- $descriptions[$command] = $description;
- }
- return $descriptions;
- }
- /**
- * Returns all available actions of the specified controller.
- * @param Controller $controller the controller instance
- * @return array all available action IDs.
- */
- public function getActions($controller)
- {
- $actions = array_keys($controller->actions());
- $class = new \ReflectionClass($controller);
- foreach ($class->getMethods() as $method) {
- $name = $method->getName();
- if ($name !== 'actions' && $method->isPublic() && !$method->isStatic() && strncmp($name, 'action', 6) === 0) {
- $actions[] = $this->camel2id(substr($name, 6));
- }
- }
- sort($actions);
- return array_unique($actions);
- }
- /**
- * Returns available commands of a specified module.
- * @param \yii\base\Module $module the module instance
- * @return array the available command names
- */
- protected function getModuleCommands($module)
- {
- $prefix = $module instanceof Application ? '' : $module->getUniqueId() . '/';
- $commands = [];
- foreach (array_keys($module->controllerMap) as $id) {
- $commands[] = $prefix . $id;
- }
- foreach ($module->getModules() as $id => $child) {
- if (($child = $module->getModule($id)) === null) {
- continue;
- }
- foreach ($this->getModuleCommands($child) as $command) {
- $commands[] = $command;
- }
- }
- $controllerPath = $module->getControllerPath();
- if (is_dir($controllerPath)) {
- $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($controllerPath, \RecursiveDirectoryIterator::KEY_AS_PATHNAME));
- $iterator = new \RegexIterator($iterator, '/.*Controller\.php$/', \RecursiveRegexIterator::GET_MATCH);
- foreach ($iterator as $matches) {
- $file = $matches[0];
- $relativePath = str_replace($controllerPath, '', $file);
- $class = strtr($relativePath, [
- '/' => '\\',
- '.php' => '',
- ]);
- $controllerClass = $module->controllerNamespace . $class;
- if ($this->validateControllerClass($controllerClass)) {
- $dir = ltrim(pathinfo($relativePath, PATHINFO_DIRNAME), '\\/');
- $command = Inflector::camel2id(substr(basename($file), 0, -14), '-', true);
- if (!empty($dir)) {
- $command = $dir . '/' . $command;
- }
- $commands[] = $prefix . $command;
- }
- }
- }
- return $commands;
- }
- /**
- * Validates if the given class is a valid console controller class.
- * @param string $controllerClass
- * @return bool
- */
- protected function validateControllerClass($controllerClass)
- {
- if (class_exists($controllerClass)) {
- $class = new \ReflectionClass($controllerClass);
- return !$class->isAbstract() && $class->isSubclassOf('yii\console\Controller');
- }
- return false;
- }
- /**
- * Displays all available commands.
- */
- protected function getDefaultHelp()
- {
- $commands = $this->getCommandDescriptions();
- $this->stdout($this->getDefaultHelpHeader());
- if (!empty($commands)) {
- $this->stdout("\nThe following commands are available:\n\n", Console::BOLD);
- $len = 0;
- foreach ($commands as $command => $description) {
- $result = Yii::$app->createController($command);
- if ($result !== false && $result[0] instanceof Controller) {
- /** @var $controller Controller */
- list($controller, $actionID) = $result;
- $actions = $this->getActions($controller);
- if (!empty($actions)) {
- $prefix = $controller->getUniqueId();
- foreach ($actions as $action) {
- $string = $prefix . '/' . $action;
- if ($action === $controller->defaultAction) {
- $string .= ' (default)';
- }
- if (($l = strlen($string)) > $len) {
- $len = $l;
- }
- }
- }
- } elseif (($l = strlen($command)) > $len) {
- $len = $l;
- }
- }
- foreach ($commands as $command => $description) {
- $this->stdout('- ' . $this->ansiFormat($command, Console::FG_YELLOW));
- $this->stdout(str_repeat(' ', $len + 4 - strlen($command)));
- $this->stdout(Console::wrapText($description, $len + 4 + 2), Console::BOLD);
- $this->stdout("\n");
- $result = Yii::$app->createController($command);
- if ($result !== false && $result[0] instanceof Controller) {
- list($controller, $actionID) = $result;
- $actions = $this->getActions($controller);
- if (!empty($actions)) {
- $prefix = $controller->getUniqueId();
- foreach ($actions as $action) {
- $string = ' ' . $prefix . '/' . $action;
- $this->stdout(' ' . $this->ansiFormat($string, Console::FG_GREEN));
- if ($action === $controller->defaultAction) {
- $string .= ' (default)';
- $this->stdout(' (default)', Console::FG_YELLOW);
- }
- $summary = $controller->getActionHelpSummary($controller->createAction($action));
- if ($summary !== '') {
- $this->stdout(str_repeat(' ', $len + 4 - strlen($string)));
- $this->stdout(Console::wrapText($summary, $len + 4 + 2));
- }
- $this->stdout("\n");
- }
- }
- $this->stdout("\n");
- }
- }
- $scriptName = $this->getScriptName();
- $this->stdout("\nTo see the help of each command, enter:\n", Console::BOLD);
- $this->stdout("\n $scriptName " . $this->ansiFormat('help', Console::FG_YELLOW) . ' '
- . $this->ansiFormat('<command-name>', Console::FG_CYAN) . "\n\n");
- } else {
- $this->stdout("\nNo commands are found.\n\n", Console::BOLD);
- }
- }
- /**
- * Displays the overall information of the command.
- * @param Controller $controller the controller instance
- */
- protected function getCommandHelp($controller)
- {
- $controller->color = $this->color;
- $this->stdout("\nDESCRIPTION\n", Console::BOLD);
- $comment = $controller->getHelp();
- if ($comment !== '') {
- $this->stdout("\n$comment\n\n");
- }
- $actions = $this->getActions($controller);
- if (!empty($actions)) {
- $this->stdout("\nSUB-COMMANDS\n\n", Console::BOLD);
- $prefix = $controller->getUniqueId();
- $maxlen = 5;
- foreach ($actions as $action) {
- $len = strlen($prefix . '/' . $action) + 2 + ($action === $controller->defaultAction ? 10 : 0);
- if ($maxlen < $len) {
- $maxlen = $len;
- }
- }
- foreach ($actions as $action) {
- $this->stdout('- ' . $this->ansiFormat($prefix . '/' . $action, Console::FG_YELLOW));
- $len = strlen($prefix . '/' . $action) + 2;
- if ($action === $controller->defaultAction) {
- $this->stdout(' (default)', Console::FG_GREEN);
- $len += 10;
- }
- $summary = $controller->getActionHelpSummary($controller->createAction($action));
- if ($summary !== '') {
- $this->stdout(str_repeat(' ', $maxlen - $len + 2) . Console::wrapText($summary, $maxlen + 2));
- }
- $this->stdout("\n");
- }
- $scriptName = $this->getScriptName();
- $this->stdout("\nTo see the detailed information about individual sub-commands, enter:\n");
- $this->stdout("\n $scriptName " . $this->ansiFormat('help', Console::FG_YELLOW) . ' '
- . $this->ansiFormat('<sub-command>', Console::FG_CYAN) . "\n\n");
- }
- }
- /**
- * Displays the detailed information of a command action.
- * @param Controller $controller the controller instance
- * @param string $actionID action ID
- * @throws Exception if the action does not exist
- */
- protected function getSubCommandHelp($controller, $actionID)
- {
- $action = $controller->createAction($actionID);
- if ($action === null) {
- $name = $this->ansiFormat(rtrim($controller->getUniqueId() . '/' . $actionID, '/'), Console::FG_YELLOW);
- throw new Exception("No help for unknown sub-command \"$name\".");
- }
- $description = $controller->getActionHelp($action);
- if ($description !== '') {
- $this->stdout("\nDESCRIPTION\n", Console::BOLD);
- $this->stdout("\n$description\n\n");
- }
- $this->stdout("\nUSAGE\n\n", Console::BOLD);
- $scriptName = $this->getScriptName();
- if ($action->id === $controller->defaultAction) {
- $this->stdout($scriptName . ' ' . $this->ansiFormat($controller->getUniqueId(), Console::FG_YELLOW));
- } else {
- $this->stdout($scriptName . ' ' . $this->ansiFormat($action->getUniqueId(), Console::FG_YELLOW));
- }
- $args = $controller->getActionArgsHelp($action);
- foreach ($args as $name => $arg) {
- if ($arg['required']) {
- $this->stdout(' <' . $name . '>', Console::FG_CYAN);
- } else {
- $this->stdout(' [' . $name . ']', Console::FG_CYAN);
- }
- }
- $options = $controller->getActionOptionsHelp($action);
- $options[\yii\console\Application::OPTION_APPCONFIG] = [
- 'type' => 'string',
- 'default' => null,
- 'comment' => "custom application configuration file path.\nIf not set, default application configuration is used.",
- ];
- ksort($options);
- if (!empty($options)) {
- $this->stdout(' [...options...]', Console::FG_RED);
- }
- $this->stdout("\n\n");
- if (!empty($args)) {
- foreach ($args as $name => $arg) {
- $this->stdout($this->formatOptionHelp(
- '- ' . $this->ansiFormat($name, Console::FG_CYAN),
- $arg['required'],
- $arg['type'],
- $arg['default'],
- $arg['comment']) . "\n\n");
- }
- }
- if (!empty($options)) {
- $this->stdout("\nOPTIONS\n\n", Console::BOLD);
- foreach ($options as $name => $option) {
- $this->stdout($this->formatOptionHelp(
- $this->ansiFormat('--' . $name . $this->formatOptionAliases($controller, $name),
- Console::FG_RED, empty($option['required']) ? Console::FG_RED : Console::BOLD),
- !empty($option['required']),
- $option['type'],
- $option['default'],
- $option['comment']) . "\n\n");
- }
- }
- }
- /**
- * Generates a well-formed string for an argument or option.
- * @param string $name the name of the argument or option
- * @param bool $required whether the argument is required
- * @param string $type the type of the option or argument
- * @param mixed $defaultValue the default value of the option or argument
- * @param string $comment comment about the option or argument
- * @return string the formatted string for the argument or option
- */
- protected function formatOptionHelp($name, $required, $type, $defaultValue, $comment)
- {
- $comment = trim($comment);
- $type = trim($type);
- if (strncmp($type, 'bool', 4) === 0) {
- $type = 'boolean, 0 or 1';
- }
- if ($defaultValue !== null && !is_array($defaultValue)) {
- if ($type === null) {
- $type = gettype($defaultValue);
- }
- if (is_bool($defaultValue)) {
- // show as integer to avoid confusion
- $defaultValue = (int)$defaultValue;
- }
- if (is_string($defaultValue)) {
- $defaultValue = "'" . $defaultValue . "'";
- } else {
- $defaultValue = var_export($defaultValue, true);
- }
- $doc = "$type (defaults to $defaultValue)";
- } else {
- $doc = $type;
- }
- if ($doc === '') {
- $doc = $comment;
- } elseif ($comment !== '') {
- $doc .= "\n" . preg_replace('/^/m', ' ', $comment);
- }
- $name = $required ? "$name (required)" : $name;
- return $doc === '' ? $name : "$name: $doc";
- }
- /**
- * @param Controller $controller the controller instance
- * @param string $option the option name
- * @return string the formatted string for the alias argument or option
- * @since 2.0.8
- */
- protected function formatOptionAliases($controller, $option)
- {
- foreach ($controller->optionAliases() as $name => $value) {
- if (Inflector::camel2id($value, '-', true) === $option) {
- return ', -' . $name;
- }
- }
- return '';
- }
- /**
- * @return string the name of the cli script currently running.
- */
- protected function getScriptName()
- {
- return basename(Yii::$app->request->scriptFile);
- }
- /**
- * Return a default help header.
- * @return string default help header.
- * @since 2.0.11
- */
- protected function getDefaultHelpHeader()
- {
- return "\nThis is Yii version " . \Yii::getVersion() . ".\n";
- }
- /**
- * Converts a CamelCase action name into an ID in lowercase.
- * Words in the ID are concatenated using the specified character '-'.
- * For example, 'CreateUser' will be converted to 'create-user'.
- * @param string $name the string to be converted
- * @return string the resulting ID
- */
- private function camel2id($name)
- {
- return mb_strtolower(trim(preg_replace('/\p{Lu}/u', '-\0', $name), '-'), 'UTF-8');
- }
- }
|