From 6a595de4be7347d2937f027a17881eec8525cccf Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 3 Feb 2013 13:54:27 -0500 Subject: [PATCH] refactored MVC. --- framework/base/Action.php | 33 +--- framework/base/Controller.php | 42 +++++ framework/base/InlineAction.php | 5 +- framework/console/Controller.php | 63 ++++++-- framework/console/controllers/HelpController.php | 191 ++++++++++++----------- 5 files changed, 194 insertions(+), 140 deletions(-) diff --git a/framework/base/Action.php b/framework/base/Action.php index a852e0b..f72aa1b 100644 --- a/framework/base/Action.php +++ b/framework/base/Action.php @@ -76,36 +76,7 @@ class Action extends Component if (!method_exists($this, 'run')) { throw new InvalidConfigException(get_class($this) . ' must define a "run()" method.'); } - $method = new \ReflectionMethod($this, 'run'); - $args = $this->bindActionParams($method, $params); - return (int)$method->invokeArgs($this, $args); - } - - /** - * Binds the given parameters to the action method. - * The returned array contains the parameters that need to be passed to the action method. - * This method calls [[Controller::validateActionParams()]] to check if any exception - * should be raised if there are missing or unknown parameters. - * @param \ReflectionMethod $method the action method reflection object - * @param array $params the supplied parameters - * @return array the parameters that can be passed to the action method - */ - protected function bindActionParams($method, $params) - { - $args = array(); - $missing = array(); - foreach ($method->getParameters() as $param) { - $name = $param->getName(); - if (array_key_exists($name, $params)) { - $args[] = $params[$name]; - unset($params[$name]); - } elseif ($param->isDefaultValueAvailable()) { - $args[] = $param->getDefaultValue(); - } else { - $missing[] = $name; - } - } - $this->controller->validateActionParams($this, $missing, $params); - return $args; + $args = $this->controller->bindActionParams($this, $params); + return (int)call_user_func_array(array($this, 'run'), $args); } } diff --git a/framework/base/Controller.php b/framework/base/Controller.php index 3cbee77..b9d2916 100644 --- a/framework/base/Controller.php +++ b/framework/base/Controller.php @@ -144,6 +144,48 @@ class Controller extends Component } /** + * Binds the parameters to the action. + * This method is invoked by [[Action]] when it begins to run with the given parameters. + * This method will check the parameter names that the action requires and return + * the provided parameters according to the requirement. If there is any missing parameter, + * an exception will be thrown. + * @param Action $action the action to be bound with parameters + * @param array $params the parameters to be bound to the action + * @return array the valid parameters that the action can run with. + * @throws InvalidRequestException if there are missing parameters. + */ + public function bindActionParams($action, $params) + { + if ($action instanceof InlineAction) { + $method = new \ReflectionMethod($this, $action->actionMethod); + } else { + $method = new \ReflectionMethod($action, 'run'); + } + + $args = array(); + $missing = array(); + foreach ($method->getParameters() as $param) { + $name = $param->getName(); + if (array_key_exists($name, $params)) { + $args[] = $params[$name]; + unset($params[$name]); + } elseif ($param->isDefaultValueAvailable()) { + $args[] = $param->getDefaultValue(); + } else { + $missing[] = $name; + } + } + + if ($missing !== array()) { + throw new InvalidRequestException(Yii::t('yii', 'Missing required parameters: {params}', array( + '{params}' => implode(', ', $missing), + ))); + } + + return $args; + } + + /** * Forwards the current execution flow to handle a new request specified by a route. * The only difference between this method and [[run()]] is that after calling this method, * the application will exit. diff --git a/framework/base/InlineAction.php b/framework/base/InlineAction.php index 4cd5413..c315675 100644 --- a/framework/base/InlineAction.php +++ b/framework/base/InlineAction.php @@ -45,8 +45,7 @@ class InlineAction extends Action */ public function runWithParams($params) { - $method = new \ReflectionMethod($this->controller, $this->actionMethod); - $args = $this->bindActionParams($method, $params); - return (int)$method->invokeArgs($this->controller, $args); + $args = $this->controller->bindActionParams($this, $params); + return (int)call_user_func_array(array($this->controller, $this->actionMethod), $args); } } diff --git a/framework/console/Controller.php b/framework/console/Controller.php index faab435..f51588f 100644 --- a/framework/console/Controller.php +++ b/framework/console/Controller.php @@ -11,6 +11,7 @@ namespace yii\console; use Yii; use yii\base\Action; +use yii\base\InlineAction; use yii\base\InvalidRouteException; /** @@ -60,28 +61,60 @@ class Controller extends \yii\base\Controller } /** - * Validates the parameter being bound to actions. - * This method is invoked when parameters are being bound to the currently requested action. - * Child classes may override this method to throw exceptions when there are missing and/or unknown parameters. - * @param Action $action the currently requested action - * @param array $missingParams the names of the missing parameters - * @param array $unknownParams the unknown parameters (name=>value) - * @throws Exception if there are missing or unknown parameters + * Binds the parameters to the action. + * This method is invoked by [[Action]] when it begins to run with the given parameters. + * This method will first bind the parameters with the [[globalOptions()|global options]] + * available to the action. It then validates the given arguments. + * @param Action $action the action to be bound with parameters + * @param array $params the parameters to be bound to the action + * @return array the valid parameters that the action can run with. + * @throws Exception if there are unknown options or missing arguments */ - public function validateActionParams($action, $missingParams, $unknownParams) + public function bindActionParams($action, $params) { - unset($missingParams[Request::ANONYMOUS_PARAMS], $unknownParams[Request::ANONYMOUS_PARAMS]); + if ($params !== array()) { + $options = $this->globalOptions(); + foreach ($params as $name => $value) { + if (in_array($name, $options, true)) { + $this->$name = $value; + unset($params[$name]); + } + } + } - if (!empty($missingParams)) { - throw new Exception(Yii::t('yii', 'Missing required options: {params}', array( - '{params}' => implode(', ', $missingParams), + $args = isset($params[Request::ANONYMOUS_PARAMS]) ? $params[Request::ANONYMOUS_PARAMS] : array(); + unset($params[Request::ANONYMOUS_PARAMS]); + if ($params !== array()) { + throw new Exception(Yii::t('yii', 'Unknown options: {params}', array( + '{params}' => implode(', ', array_keys($params)), ))); } - if (!empty($unknownParams)) { - throw new Exception(Yii::t('yii', 'Unknown options: {params}', array( - '{params}' => implode(', ', $unknownParams), + + if ($action instanceof InlineAction) { + $method = new \ReflectionMethod($this, $action->actionMethod); + } else { + $method = new \ReflectionMethod($action, 'run'); + } + + $missing = array(); + foreach ($method->getParameters() as $i => $param) { + $name = $param->getName(); + if (!isset($args[$i])) { + if ($param->isDefaultValueAvailable()) { + $args[$i] = $param->getDefaultValue(); + } else { + $missing[] = $name; + } + } + } + + if ($missing !== array()) { + throw new Exception(Yii::t('yii', 'Missing required arguments: {params}', array( + '{params}' => implode(', ', $missing), ))); } + + return $args; } /** diff --git a/framework/console/controllers/HelpController.php b/framework/console/controllers/HelpController.php index d2e50b8..3cd6a07 100644 --- a/framework/console/controllers/HelpController.php +++ b/framework/console/controllers/HelpController.php @@ -47,27 +47,28 @@ class HelpController extends Controller * yiic help message # display help info about "message" * ~~~ * - * @param array $args The name of the command to show help about. + * @param string $command The name of the command to show help about. * If not provided, all available commands will be displayed. * @return integer the exit status * @throws Exception if the command for help is unknown */ - public function actionIndex($args = array()) + public function actionIndex($command) { - if (isset($args[0])) { - $result = Yii::$application->createController($args[0]); + if ($command !== null) { + $result = Yii::$application->createController($command); if ($result === false) { throw new Exception(Yii::t('yii', 'No help for unknown command "{command}".', array( - '{command}' => $args[0], + '{command}' => $command, ))); } list($controller, $actionID) = $result; - if ($actionID === '') { - $this->getControllerHelp($controller); - } else { + $actions = $this->getActions($controller); + if ($actionID !== '' || count($actions) === 1 && $actions[0] === $controller->defaultAction) { $this->getActionHelp($controller, $actionID); + } else { + $this->getControllerHelp($controller); } } else { $this->getHelp(); @@ -144,13 +145,12 @@ class HelpController extends Controller { $commands = $this->getCommands(); if ($commands !== array()) { - echo "Usage: yiic [...options...] [...arguments...]\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"; + echo "\n yiic help \n\n"; } else { echo "\nNo commands are found.\n"; } @@ -178,7 +178,11 @@ class HelpController extends Controller echo "\nSUB-COMMANDS\n\n"; $prefix = $controller->getUniqueId(); foreach ($actions as $action) { - echo "* $prefix/$action\n"; + if ($action === $controller->defaultAction) { + echo "* $prefix/$action (default)\n"; + } else { + echo "* $prefix/$action\n"; + } } echo "\n\nTo see the help of each sub-command, enter:\n"; echo "\n yiic help \n\n"; @@ -206,9 +210,11 @@ class HelpController extends Controller } $tags = $this->parseComment($method->getDocComment()); - $options = $this->getOptions($method, isset($tags['param']) ? $tags['param'] : array()); - $globalOptions = $this->getGlobalOptions($controller); - $options = array_merge($options, $globalOptions); + + if ($tags['description'] !== '') { + echo "\nDESCRIPTION"; + echo "\n\n" . $tags['description'] . "\n\n"; + } echo "\nUSAGE\n\n"; if ($action->id === $controller->defaultAction) { @@ -216,59 +222,34 @@ class HelpController extends Controller } else { echo "yiic " . $action->getUniqueId(); } - if (isset($options[Request::ANONYMOUS_PARAMS])) { - if (count($options) > 1) { - echo ' [...options...]'; - } - echo " [...arguments...]"; - } elseif (count($options)) { - echo " [...options...]"; + list ($required, $optional) = $this->getArgHelps($method, isset($tags['param']) ? $tags['param'] : array()); + if (!empty($required)) { + echo ' <' . implode('> <', array_keys($required)) . '>'; } - echo "\n\n"; - - if ($tags['description'] !== '') { - echo "\nDESCRIPTION"; - echo "\n\n" . $tags['description'] . "\n\n"; + if (!empty($optional)) { + echo ' [' . implode('] [', array_keys($optional)) . ']'; } + echo "\n\n"; - if (isset($options[Request::ANONYMOUS_PARAMS])) { + if (!empty($required) || !empty($optional)) { echo "\nARGUMENTS\n\n"; - echo $options[Request::ANONYMOUS_PARAMS] . "\n\n"; - unset($options[Request::ANONYMOUS_PARAMS]); + echo implode("\n\n", array_merge($required, $optional)) . "\n\n"; } + $options = $this->getOptionHelps($controller); if ($options !== array()) { echo "\nOPTIONS\n\n"; echo implode("\n\n", $options) . "\n\n"; } } - function parseComment($comment) - { - $tags = array(); - $comment = "@description \n" . strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($comment, '/'))), "\r", ''); - $parts = preg_split('/^\s*@/m', $comment, -1, PREG_SPLIT_NO_EMPTY); - foreach ($parts as $part) { - if (preg_match('/^(\w+)(.*)/ms', trim($part), $matches)) { - $name = $matches[1]; - if (!isset($tags[$name])) { - $tags[$name] = trim($matches[2]); - } elseif (is_array($tags[$name])) { - $tags[$name][] = trim($matches[2]); - } else { - $tags[$name] = array($tags[$name], trim($matches[2])); - } - } - } - return $tags; - } - /** + * Returns the help information about arguments. * @param \ReflectionMethod $method - * @param string $meta - * @return array + * @param string $tags the parsed comment block related with arguments + * @return array the required and optional argument help information */ - protected function getOptions($method, $tags) + protected function getArgHelps($method, $tags) { if (is_string($tags)) { $tags = array($tags); @@ -286,51 +267,21 @@ class HelpController extends Controller $comment = $tag; } if ($param->isDefaultValueAvailable()) { - $optional[$name] = $this->formatOptionHelp($name, false, $type, $param->getDefaultValue(), $comment); + $optional[$name] = $this->formatOptionHelp('* ' . $name, false, $type, $param->getDefaultValue(), $comment); } else { - $required[$name] = $this->formatOptionHelp($name, true, $type, null, $comment); + $required[$name] = $this->formatOptionHelp('* ' . $name, true, $type, null, $comment); } } - ksort($required); - ksort($optional); - - return array_merge($required, $optional); - } - - protected function formatOptionHelp($name, $required, $type, $defaultValue, $comment) - { - $doc = ''; - $comment = trim($comment); - - if ($name === Request::ANONYMOUS_PARAMS) { - return $comment; - } - - if ($defaultValue !== null && !is_array($defaultValue)) { - if ($type === null) { - $type = gettype($defaultValue); - } - $doc = "$type (defaults to " . var_export($defaultValue, true) . ")"; - } elseif (trim($type) !== '') { - $doc = $type; - } - - if ($doc === '') { - $doc = $comment; - } elseif ($comment !== '') { - $doc .= "\n" . preg_replace("/^/m", " ", $comment); - } - - $name = $required ? "--$name (required)" : "--$name"; - return $doc === '' ? $name : "$name: $doc"; + return array($required, $optional); } /** - * @param Controller $controller - * @return array + * Returns the help information about the options available for a console controller. + * @param Controller $controller the console controller + * @return array the help information about the options */ - protected function getGlobalOptions($controller) + protected function getOptionHelps($controller) { $optionNames = $controller->globalOptions(); if (empty($optionNames)) { @@ -358,12 +309,70 @@ class HelpController extends Controller $type = null; $comment = $doc; } - $options[$name] = $this->formatOptionHelp($name, false, $type, $defaultValue, $comment); + $options[$name] = $this->formatOptionHelp('--' . $name, false, $type, $defaultValue, $comment); } else { - $options[$name] = $this->formatOptionHelp($name, false, null, $defaultValue, ''); + $options[$name] = $this->formatOptionHelp('--' . $name, false, null, $defaultValue, ''); } } ksort($options); return $options; } + + /** + * Parses the comment block into tags. + * @param string $comment the comment block + * @return array the parsed tags + */ + protected function parseComment($comment) + { + $tags = array(); + $comment = "@description \n" . strtr(trim(preg_replace('/^\s*\**( |\t)?/m', '', trim($comment, '/'))), "\r", ''); + $parts = preg_split('/^\s*@/m', $comment, -1, PREG_SPLIT_NO_EMPTY); + foreach ($parts as $part) { + if (preg_match('/^(\w+)(.*)/ms', trim($part), $matches)) { + $name = $matches[1]; + if (!isset($tags[$name])) { + $tags[$name] = trim($matches[2]); + } elseif (is_array($tags[$name])) { + $tags[$name][] = trim($matches[2]); + } else { + $tags[$name] = array($tags[$name], trim($matches[2])); + } + } + } + return $tags; + } + + /** + * Generates a well-formed string for an argument or option. + * @param string $name the name of the argument or option + * @param boolean $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) + { + $doc = ''; + $comment = trim($comment); + + if ($defaultValue !== null && !is_array($defaultValue)) { + if ($type === null) { + $type = gettype($defaultValue); + } + $doc = "$type (defaults to " . var_export($defaultValue, true) . ")"; + } elseif (trim($type) !== '') { + $doc = $type; + } + + if ($doc === '') { + $doc = $comment; + } elseif ($comment !== '') { + $doc .= "\n" . preg_replace("/^/m", " ", $comment); + } + + $name = $required ? "$name (required)" : $name; + return $doc === '' ? $name : "$name: $doc"; + } } \ No newline at end of file