From 12932f0d774cff5263ef97f4a8a9ea54a5913db9 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sat, 12 May 2012 16:58:17 -0400 Subject: [PATCH] help command. --- framework/base/ErrorHandler.php | 2 +- framework/base/Module.php | 13 ++- framework/console/Application.php | 83 +++---------- framework/console/Controller.php | 161 +++----------------------- framework/console/commands/HelpController.php | 85 +++++++------- 5 files changed, 82 insertions(+), 262 deletions(-) diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php index 0c3689c..9400083 100644 --- a/framework/base/ErrorHandler.php +++ b/framework/base/ErrorHandler.php @@ -287,7 +287,7 @@ class ErrorHandler extends ApplicationComponent if (YII_DEBUG) { echo $exception; } else { - echo get_class($exception) . ':' . $exception->getMessage(); + echo get_class($exception) . ': ' . $exception->getMessage(); } } diff --git a/framework/base/Module.php b/framework/base/Module.php index bc13c62..e76fd4a 100644 --- a/framework/base/Module.php +++ b/framework/base/Module.php @@ -488,9 +488,9 @@ abstract class Module extends Component implements Initable * 2. If the segment matches * - an ID in [[controllers]], create a controller instance using the corresponding configuration, * and return the controller with the rest part of the route; + * - an ID in [[modules]], call the [[createController()]] method of the corresponding module. * - a controller class under [[controllerPath]], create the controller instance, and return it * with the rest part of the route; - * - an ID in [[modules]], call the [[createController()]] method of the corresponding module. * * @param string $route the route which may consist module ID, controller ID and/or action ID (e.g. `post/create`) * @return array|boolean the array of controller instance and action ID. False if the route cannot be resolved. @@ -521,6 +521,13 @@ abstract class Module extends Component implements Initable ); } + if (($module = $this->getModule($id)) !== null) { + $result = $module->createController($route); + if ($result !== false) { + return $result; + } + } + $className = ucfirst($id) . 'Controller'; $classFile = $this->getControllerPath() . DIRECTORY_SEPARATOR . $className . '.php'; if (is_file($classFile)) { @@ -535,10 +542,6 @@ abstract class Module extends Component implements Initable } } - if (($module = $this->getModule($id)) !== null) { - return $module->createController($route); - } - return false; } } diff --git a/framework/console/Application.php b/framework/console/Application.php index 448acf4..584d887 100644 --- a/framework/console/Application.php +++ b/framework/console/Application.php @@ -20,23 +20,30 @@ use yii\util\ReflectionHelper; * through a command-based approach: * * - A console application consists of one or several possible user commands; - * - Each user command is implemented as a class extending [[Command]]; + * - Each user command is implemented as a class extending [[\yii\console\Controller]]; * - User specifies which command to run on the command line; * - The command processes the user request with the specified parameters. * - * The command classes reside in the directory specified by [[commandPath]]. - * The name of the class should be of the form `Command` (e.g. `HelpCommand`). + * The command classes reside in the directory specified by [[controllerPath]]. + * Their naming should follow the same naming as controllers. For example, the `help` command + * is implemented using the `HelpController` class. * * To run the console application, enter the following on the command line: * * ~~~ - * yiic [param 1] [param 2] ... + * yiic [...options...] * ~~~ * - * You may use the following line to see help instructions about a command: + * where `` refers to a controller route in the form of `ModuleID/ControllerID/ActionID` + * (e.g. `sitemap/create`), and `options` refers to a set of named parameters that will be used + * to initialize the command controller instance and the corresponding action (e.g. `--since=0` + * specifies a `since` parameter whose value is 0). + * + * A `help` command is provided by default, which may list available commands and show their usage. + * To use this command, simply type: * * ~~~ - * yiic help + * yiic help * ~~~ * * @author Qiang Xue @@ -66,7 +73,7 @@ class Application extends \yii\base\Application } } // ensure we have the 'help' command so that we can list the available commands - if ($this->defaultRoute === 'help' && !isset($this->controllers['help'])) { + if (!isset($this->controllers['help'])) { $this->controllers['help'] = 'yii\console\commands\HelpController'; } } @@ -137,68 +144,6 @@ class Application extends \yii\base\Application return array($route, $params); } - /** - * Searches for commands under the specified directory. - * @param string $path the directory containing the command class files. - * @return array list of commands (command name=>command class file) - */ - public function findCommands($path) - { - if (($dir = @opendir($path)) === false) - return array(); - $commands = array(); - while (($name = readdir($dir)) !== false) { - $file = $path . DIRECTORY_SEPARATOR . $name; - if (!strcasecmp(substr($name, -11), 'Command.php') && is_file($file)) - $commands[strtolower(substr($name, 0, -11))] = $file; - } - closedir($dir); - return $commands; - } - - /** - * Adds commands from the specified command path. - * If a command already exists, the new one will be ignored. - * @param string $path the alias of the directory containing the command class files. - */ - public function addCommands($path) - { - if (($commands = $this->findCommands($path)) !== array()) { - foreach ($commands as $name => $file) { - if (!isset($this->commands[$name])) - $this->commands[$name] = $file; - } - } - } - - /** - * @param string $name command name (case-insensitive) - * @return CConsoleCommand the command object. Null if the name is invalid. - */ - public function createCommand($name) - { - $name = strtolower($name); - if (isset($this->commands[$name])) { - if (is_string($this->commands[$name])) // class file path or alias - { - if (strpos($this->commands[$name], '/') !== false || strpos($this->commands[$name], '\\') !== false) { - $className = substr(basename($this->commands[$name]), 0, -4); - if (!class_exists($className, false)) - require_once($this->commands[$name]); - } - else // an alias - $className = Yii::import($this->commands[$name]); - return new $className($name, $this); - } - else // an array configuration - return Yii::createComponent($this->commands[$name], $name, $this); - } - else if ($name === 'help') - return new CHelpCommand('help', $this); - else - return null; - } - public function coreCommands() { return array( diff --git a/framework/console/Controller.php b/framework/console/Controller.php index b1d949a..cb72764 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -13,36 +13,15 @@ use yii\base\Action; use yii\base\Exception; /** - * Command represents an executable console command. + * Controller is the base class of console command classes. * - * It works like {@link \yii\web\Controller} by parsing command line options and dispatching - * the request to a specific action with appropriate option values. + * A controller consists of one or several actions known as sub-commands. + * Users call a console command by specifying the corresponding route which identifies a controller action. + * The `yiic` program is used when calling a console command, like the following: * - * Users call a console command via the following command format: - *
- * yiic CommandName ActionName --Option1=Value1 --Option2=Value2 ...
- * 
- * - * Child classes mainly needs to implement various action methods whose name must be - * prefixed with "action". The parameters to an action method are considered as options - * for that specific action. The action specified as {@link defaultAction} will be invoked - * when a user does not specify the action name in his command. - * - * Options are bound to action parameters via parameter names. For example, the following - * action method will allow us to run a command with yiic sitemap --type=News: - *
- * class SitemapCommand {
- *     public function actionIndex($type) {
- *         ....
- *     }
- * }
- * 
- * - * @property string $name The command name. - * @property CommandRunner $commandRunner The command runner instance. - * @property string $help The command description. Defaults to 'Usage: php entry-script.php command-name'. - * @property array $optionHelp The command option help information. Each array element describes - * the help information for a single action. + * ~~~ + * yiic [...options...] + * ~~~ * * @author Qiang Xue * @since 2.0 @@ -50,134 +29,22 @@ use yii\base\Exception; class Controller extends \yii\base\Controller { /** - * This method is invoked when the request parameters do not satisfy the requirement of the specified action. - * The default implementation will throw an exception. - * @param Action $action the action being executed - * @param Exception $exception the exception about the invalid parameters - * @throws Exception whenever this method is invoked - */ - public function invalidActionParams($action, $exception) - { - } - - /** * This method is invoked when extra parameters are provided to an action when it is executed. * The default implementation does nothing. * @param Action $action the action being executed * @param array $expected the expected action parameters (name => value) * @param array $actual the actual action parameters (name => value) + * @throws Exception if any unrecognized parameters are provided */ public function extraActionParams($action, $expected, $actual) { - } - - /** - * Provides the command description. - * This method may be overridden to return the actual command description. - * @return string the command description. Defaults to 'Usage: php entry-script.php command-name'. - */ - public function getHelp() - { - $help = 'Usage: ' . $this->getCommandRunner()->getScriptName() . ' ' . $this->getName(); - $options = $this->getOptionHelp(); - if (empty($options)) - return $help; - if (count($options) === 1) - return $help . ' ' . $options[0]; - $help .= " \nActions:\n"; - foreach ($options as $option) - $help .= ' ' . $option . "\n"; - return $help; - } - - /** - * Provides the command option help information. - * The default implementation will return all available actions together with their - * corresponding option information. - * @return array the command option help information. Each array element describes - * the help information for a single action. - */ - public function getOptionHelp() - { - $options = array(); - $class = new \ReflectionClass(get_class($this)); - foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) { - $name = $method->getName(); - if (!strncasecmp($name, 'action', 6) && strlen($name) > 6) { - $name = substr($name, 6); - $name[0] = strtolower($name[0]); - $help = $name; - - foreach ($method->getParameters() as $param) { - $optional = $param->isDefaultValueAvailable(); - $defaultValue = $optional ? $param->getDefaultValue() : null; - $name = $param->getName(); - if ($optional) - $help .= " [--$name=$defaultValue]"; - else - $help .= " --$name=value"; - } - $options[] = $help; - } - } - return $options; - } - - /** - * Displays a usage error. - * This method will then terminate the execution of the current application. - * @param string $message the error message - */ - public function usageError($message) - { - echo "Error: $message\n\n" . $this->getHelp() . "\n"; - exit(1); - } - - /** - * Reads input via the readline PHP extension if that's available, or fgets() if readline is not installed. - * - * @param string $message to echo out before waiting for user input - * @param string $default the default string to be returned when user does not write anything. - * Defaults to null, means that default string is disabled. - * @return mixed line read as a string, or false if input has been closed - */ - public function prompt($message, $default = null) - { - if ($default !== null) { - $message .= " [$default] "; - } - else { - $message .= ' '; - } + unset($expected['args'], $actual['args']); - if (extension_loaded('readline')) { - $input = readline($message); - if ($input) { - readline_add_history($input); - } - } else { - echo $message; - $input = fgets(STDIN); - } - if ($input === false) { - return false; - } - else { - $input = trim($input); - return ($input === '' && $default !== null) ? $default : $input; + $keys = array_diff(array_keys($actual), array_keys($expected)); + if (!empty($keys)) { + throw new Exception(\Yii::t('yii', 'Unknown parameters: {params}', array( + '{params}' => implode(', ', $keys), + ))); } } - - /** - * Asks user to confirm by typing y or n. - * - * @param string $message to echo out before waiting for user input - * @return bool if user confirmed - */ - public function confirm($message) - { - echo $message . ' [yes|no] '; - return !strncasecmp(trim(fgets(STDIN)), 'y', 1); - } } \ No newline at end of file diff --git a/framework/console/commands/HelpController.php b/framework/console/commands/HelpController.php index ba2f38e..a7836bd 100644 --- a/framework/console/commands/HelpController.php +++ b/framework/console/commands/HelpController.php @@ -1,8 +1,7 @@ * @link http://www.yiiframework.com/ * @copyright Copyright © 2008-2012 Yii Software LLC * @license http://www.yiiframework.com/license/ @@ -17,9 +16,11 @@ namespace yii\console\commands; * about a specific command. * * To use this command, enter the following on the command line: - *
- * php path/to/entry_script.php help [command name]
- * 
+ * + * ~~~ + * yiic help [command name] + * ~~~ + * * In the above, if the command name is not provided, it will display all * available commands. * @@ -32,49 +33,53 @@ class HelpController extends \yii\console\Controller { public function actionIndex($args = array()) { + echo "Yii console command helper (based on Yii v" . \Yii::getVersion() . ").\n"; + $commands = $this->getCommands(); + if ($commands !== array()) { + echo "\n Usage: yiic [...options...]\n\n"; + echo "The following commands are available:\n"; + foreach ($commands as $command) { + echo " - $command\n"; + } + echo "\nTo see individual command help, enter:\n"; + echo "\n yiic help \n"; + } else { + echo "\nNo commands are found.\n"; + } + } + protected function getCommands() + { + $commands = $this->getModuleCommands(\Yii::$application); + sort($commands); + return array_unique($commands); } /** - * Execute the action. - * @param array $args command line parameters specific for this command + * @param \yii\base\Module $module + * @return array */ - public function run($args) + protected function getModuleCommands($module) { - $runner=$this->getCommandRunner(); - $commands=$runner->commands; - if(isset($args[0])) - { - $name=strtolower($args[0]); + if ($module === null) { + return array(); } - if(!isset($args[0]) || !isset($commands[$name])) - { - if(!empty($commands)) - { - echo "Yii command runner (based on Yii v".\Yii::getVersion().")\n"; - echo "Usage: ".$runner->getScriptName()." [parameters...]\n"; - echo "\nThe following commands are available:\n"; - $commandNames=array_keys($commands); - sort($commandNames); - echo ' - '.implode("\n - ",$commandNames); - echo "\n\nTo see individual command help, use the following:\n"; - echo " ".$runner->getScriptName()." help \n"; - } else - { - echo "No available commands.\n"; - echo "Please define them under the following directory:\n"; - echo "\t".\Yii::$app->getCommandPath()."\n"; + + $commands = array_keys($module->controllers); + + foreach ($module->getModules() as $id => $module) { + foreach ($this->getModuleCommands($module->getModule($id)) as $command) { + $commands[] = $command; } - } else - echo $runner->createCommand($name)->getHelp(); - } + } - /** - * Provides the command description. - * @return string the command description. - */ - public function getHelp() - { - return parent::getHelp().' [command-name]'; + $files = scandir($module->getControllerPath()); + foreach ($files as $file) { + if(strcmp(substr($file,-14),'Controller.php') === 0 && is_file($file)) { + $commands[] = lcfirst(substr(basename($file), 0, -14)); + } + } + + return $commands; } } \ No newline at end of file