* @since 2.0 */ class HelpController extends Controller { /** * Displays available commands or the detailed information * about a particular command. For example, * * ~~~ * yiic help # list available commands * yiic help message # display help info about "message" * ~~~ * * @param array $args additional anonymous command line arguments. * You may provide a command name to display its detailed information. * @return integer the exit status */ public function actionIndex($args = array()) { if (empty($args)) { $status = $this->getHelp(); } else { $result = \Yii::$application->createController($args[0]); if ($result === false) { echo "\nError: no help for unknown command \"{$args[0]}\".\n"; return 1; } list($controller, $actionID) = $result; if ($actionID === '') { $status = $this->getControllerHelp($controller); } else { $status = $this->getActionHelp($controller, $actionID); } } return $status; } /** * Returns all available command names. * @return array all available command names */ public function getCommands() { $commands = $this->getModuleCommands(\Yii::$application); sort($commands); return array_unique($commands); } /** * 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) { /** @var $method \ReflectionMethod */ $name = $method->getName(); if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') { $actions[] = StringHelper::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 = array(); 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[] = $prefix . $id . '/' . $command; } } $files = scandir($module->getControllerPath()); foreach ($files as $file) { if(strcmp(substr($file,-14),'Controller.php') === 0 && is_file($file)) { $commands[] = $prefix . lcfirst(substr(basename($file), 0, -14)); } } return $commands; } /** * Displays all available commands. * @return integer the exit status */ protected function getHelp() { $commands = $this->getCommands(); if ($commands !== array()) { echo "\nUsage: yiic [...options...]\n\n"; echo "The following commands are available:\n\n"; foreach ($commands as $command) { echo " * $command\n"; } echo "\nTo see the help of each command, enter:\n"; echo "\n yiic help \n"; } else { echo "\nNo commands are found.\n"; } return 0; } /** * Displays the overall information of the command. * @param Controller $controller the controller instance * @return integer the exit status */ protected function getControllerHelp($controller) { $class = new \ReflectionClass($controller); $comment = strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($class->getDocComment(), '/'))), "\r", ''); if (preg_match('/^\s*@\w+/m', $comment, $matches, PREG_OFFSET_CAPTURE)) { $comment = trim(substr($comment, 0, $matches[0][1])); } if ($comment !== '') { echo "\n" . $comment . "\n"; } $options = $this->getGlobalOptions($class, $controller); if ($options !== array()) { echo "\nGLOBAL OPTIONS"; echo "\n--------------\n\n"; foreach ($options as $name => $description) { echo " --$name"; if ($description != '') { echo ": $description\n"; } echo "\n"; } } $actions = $this->getActions($controller); if ($actions !== array()) { echo "\nSUB-COMMANDS"; echo "\n------------\n\n"; $prefix = $controller->getUniqueId(); foreach ($actions as $action) { if ($controller->defaultAction === $action) { echo " * $prefix (default)\n"; } else { echo " * $prefix/$action\n"; } } echo "\n"; } return 0; } /** * Displays the detailed information of a command action. * @param Controller $controller the controller instance * @param string $actionID action ID * @return integer the exit status */ protected function getActionHelp($controller, $actionID) { $action = $controller->createAction($actionID); if ($action === null) { echo "Unknown sub-command: " . $controller->getUniqueId() . "/$actionID\n"; return 1; } if ($action instanceof InlineAction) { $method = new \ReflectionMethod($controller, 'action' . $action->id); } else { $method = new \ReflectionMethod($action, 'run'); } $comment = strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($method->getDocComment(), '/'))), "\r", ''); if (preg_match('/^\s*@\w+/m', $comment, $matches, PREG_OFFSET_CAPTURE)) { $meta = substr($comment, $matches[0][1]); $comment = trim(substr($comment, 0, $matches[0][1])); } else { $meta = ''; } if ($comment !== '') { echo "\n" . $comment . "\n"; } $options = $this->getOptions($method, $meta); if ($options !== array()) { echo "\nOPTIONS"; echo "\n-------\n\n"; foreach ($options as $name => $description) { echo " --$name"; if ($description != '') { echo ": $description\n"; } } echo "\n"; } return 0; } /** * @param \ReflectionMethod $method * @param string $meta * @return array */ protected function getOptions($method, $meta) { $params = $method->getParameters(); $tags = preg_split('/^\s*@/m', $meta, -1, PREG_SPLIT_NO_EMPTY); $options = array(); $count = 0; foreach ($tags as $tag) { $parts = preg_split('/\s+/', trim($tag), 2); if ($parts[0] === 'param' && isset($params[$count])) { $param = $params[$count]; $comment = isset($parts[1]) ? $parts[1] : ''; if (preg_match('/^([^\s]+)\s+(\$\w+\s+)?(.*)/s', $comment, $matches)) { $type = $matches[1]; $doc = $matches[3]; } else { $type = $comment; $doc = ''; } $comment = $type === '' ? '' : ($type . ', '); if ($param->isDefaultValueAvailable()) { $value = $param->getDefaultValue(); if (!is_array($value)) { $comment .= 'optional (defaults to ' . var_export($value, true) . ').'; } else { $comment .= 'optional.'; } } else { $comment .= 'required.'; } if (trim($doc) !== '') { $comment .= "\n" . preg_replace("/^/m", " ", $doc); } $options[$param->getName()] = $comment; $count++; } } if ($count < count($params)) { for ($i = $count; $i < count($params); ++$i) { $options[$params[$i]->getName()] = ''; } } ksort($options); return $options; } /** * @param \ReflectionClass $class * @param Controller $controller * @return array */ protected function getGlobalOptions($class, $controller) { $options = array(); foreach ($class->getProperties() as $property) { if (!$property->isPublic() || $property->isStatic() || $property->getDeclaringClass()->getName() !== get_class($controller)) { continue; } $name = $property->getName(); $comment = strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($property->getDocComment(), '/'))), "\r", ''); if (preg_match('/^\s*@\w+/m', $comment, $matches, PREG_OFFSET_CAPTURE)) { $meta = substr($comment, $matches[0][1]); } else { $meta = ''; } $tags = preg_split('/^\s*@/m', $meta, -1, PREG_SPLIT_NO_EMPTY); foreach ($tags as $tag) { $parts = preg_split('/\s+/', trim($tag), 2); $comment = isset($parts[1]) ? $parts[1] : ''; if ($parts[0] === 'var' || $parts[0] === 'property') { if (preg_match('/^([^\s]+)(\s+.*)?/s', $comment, $matches)) { $type = $matches[1]; $doc = trim($matches[2]); } else { $type = $comment; $doc = ''; } $comment = $type === '' ? '' : ($type); if (trim($doc) !== '') { $comment .= ', ' . preg_replace("/^/m", "", $doc); } $options[$name] = $comment; break; } } if (!isset($options[$name])) { $options[$name] = ''; } } ksort($options); return $options; } }