diff --git a/framework/YiiBase.php b/framework/YiiBase.php index ce196bd..16ae1ff 100644 --- a/framework/YiiBase.php +++ b/framework/YiiBase.php @@ -531,11 +531,13 @@ class YiiBase */ public static function t($category, $message, $params = array(), $source = null, $language = null) { - if (self::$app !== null) + // todo; + return $params !== array() ? strtr($message, $params) : $message; + if (self::$application !== null) { if ($source === null) $source = $category === 'yii' ? 'coreMessages' : 'messages'; - if (($source = self::$app->getComponent($source)) !== null) + if (($source = self::$application->getComponent($source)) !== null) $message = $source->translate($category, $message, $language); } if ($params === array()) @@ -549,7 +551,7 @@ class YiiBase if (strpos($message, '#') === false) { $chunks = explode('|', $message); - $expressions = self::$app->getLocale($language)->getPluralRules(); + $expressions = self::$application->getLocale($language)->getPluralRules(); if ($n = min(count($chunks), count($expressions))) { for ($i = 0;$i < $n;$i++) diff --git a/framework/console/Controller.php b/framework/console/Controller.php index cb72764..210820b 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -29,12 +29,23 @@ 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 + */ + public function invalidActionParams($action, $exception) + { + echo "Error: " . $exception->getMessage() . "\n"; + \Yii::$application->end(1); + } + + /** * 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) { @@ -42,9 +53,10 @@ class Controller extends \yii\base\Controller $keys = array_diff(array_keys($actual), array_keys($expected)); if (!empty($keys)) { - throw new Exception(\Yii::t('yii', 'Unknown parameters: {params}', array( + echo "Error: " . \Yii::t('yii', 'Unknown parameters: {params}', array( '{params}' => implode(', ', $keys), - ))); + )) . "\n"; + \Yii::$application->end(1); } } } \ No newline at end of file diff --git a/framework/console/commands/HelpController.php b/framework/console/commands/HelpController.php index a7836bd..3fd99d0 100644 --- a/framework/console/commands/HelpController.php +++ b/framework/console/commands/HelpController.php @@ -9,31 +9,139 @@ namespace yii\console\commands; +use yii\base\Application; +use yii\base\InlineAction; +use yii\console\Controller; + /** - * HelpCommand represents a console help command. + * This command provides help information about console commands. * - * HelpCommand displays the available command list or the help instructions - * about a specific command. + * This command displays the available command list in + * the application or the detailed instructions about using + * a specific command. * - * To use this command, enter the following on the command line: + * This command can be used as follows on command line: * * ~~~ * yiic help [command name] * ~~~ * - * In the above, if the command name is not provided, it will display all - * available commands. - * - * @property string $help The command description. + * In the above, if the command name is not provided, all + * available commands will be displayed. * * @author Qiang Xue * @since 2.0 */ -class HelpController extends \yii\console\Controller +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()) { - echo "Yii console command helper (based on Yii v" . \Yii::getVersion() . ").\n"; + if (empty($args)) { + $status = $this->helpIndex(); + } else { + $result = \Yii::$application->createController($args[0]); + if ($result === false) { + echo "Unknown command: " . $args[0] . "\n"; + return 1; + } + + list($controller, $action) = $result; + + if ($action === '') { + $status = $this->helpController($controller); + } else { + $status = $this->helpAction($controller, $action); + } + } + 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) { + $name = $method->getName(); + if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0) { + $actions[] = lcfirst(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) + { + if ($module instanceof Application) { + $prefix = ''; + } else { + $prefix = $module->getUniqueId() . '/'; + } + + $commands = array(); + foreach (array_keys($module->controllers) 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 helpIndex() + { $commands = $this->getCommands(); if ($commands !== array()) { echo "\n Usage: yiic [...options...]\n\n"; @@ -46,40 +154,202 @@ class HelpController extends \yii\console\Controller } else { echo "\nNo commands are found.\n"; } + return 0; } - protected function getCommands() + /** + * Displays the overall information of the command. + * @param Controller $controller the controller instance + * @return integer the exit status + */ + protected function helpController($controller) { - $commands = $this->getModuleCommands(\Yii::$application); - sort($commands); - return array_unique($commands); + $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 "\nDESCRIPTION"; + echo "\n-----------\n\n"; + echo $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/$action (default)\n"; + } else { + echo " * $prefix/$action\n"; + } + } + echo "\n"; + } + + return 0; } /** - * @param \yii\base\Module $module - * @return array + * 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 getModuleCommands($module) + protected function helpAction($controller, $actionID) { - if ($module === null) { - return array(); + $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 = ''; } - $commands = array_keys($module->controllers); + if ($comment !== '') { + echo "\nDESCRIPTION"; + echo "\n-----------\n\n"; + echo $comment . "\n"; + } - foreach ($module->getModules() as $id => $module) { - foreach ($this->getModuleCommands($module->getModule($id)) as $command) { - $commands[] = $command; + $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"; } - $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 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()] = ''; } } - return $commands; + 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 .= "\n" . preg_replace("/^/m", " ", $doc); + } + $options[$name] = $comment; + break; + } + } + if (!isset($options[$name])) { + $options[$name] = ''; + } + } + ksort($options); + return $options; } } \ No newline at end of file diff --git a/framework/util/ReflectionHelper.php b/framework/util/ReflectionHelper.php index 8920af6..e858619 100644 --- a/framework/util/ReflectionHelper.php +++ b/framework/util/ReflectionHelper.php @@ -54,7 +54,7 @@ class ReflectionHelper } elseif ($param->isDefaultValueAvailable()) { $ps[$name] = $param->getDefaultValue(); } else { - throw new Exception(\Yii::t('yii', 'Missing required parameter "{name}".', array('{name' => $name))); + throw new Exception(\Yii::t('yii', 'Missing required parameter "{name}".', array('{name}' => $name))); } } return $ps;