mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-13 20:59:12 +08:00
...
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace yii\base;
|
||||
|
||||
use yii\util\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* Action is the base class for all controller action classes.
|
||||
*
|
||||
@@ -52,13 +54,12 @@ class Action extends Component
|
||||
*/
|
||||
public function runWithParams($params)
|
||||
{
|
||||
$method = new \ReflectionMethod($this, 'run');
|
||||
$params = \yii\util\ReflectionHelper::bindParams($method, $params);
|
||||
if ($params === false) {
|
||||
$this->controller->invalidActionParams($this);
|
||||
try {
|
||||
$params = ReflectionHelper::extractMethodParams($this, 'run', $params);
|
||||
return (int)call_user_func_array(array($this, 'run'), $params);
|
||||
} catch (Exception $e) {
|
||||
$this->controller->invalidActionParams($this, $e);
|
||||
return 1;
|
||||
} else {
|
||||
return (int)$method->invokeArgs($this, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ use yii\base\Exception;
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class Application extends Module
|
||||
class Application extends Module
|
||||
{
|
||||
/**
|
||||
* @var string the application name. Defaults to 'My Application'.
|
||||
@@ -131,13 +131,6 @@ abstract class Application extends Module
|
||||
$this->preloadComponents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the application.
|
||||
* This is the main entrance of an application. Derived classes must implement this method.
|
||||
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
|
||||
*/
|
||||
abstract public function run();
|
||||
|
||||
/**
|
||||
* Terminates the application.
|
||||
* This method replaces PHP's exit() function by calling [[afterRequest()]] before exiting.
|
||||
@@ -156,6 +149,19 @@ abstract class Application extends Module
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the application.
|
||||
* This is the main entrance of an application.
|
||||
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$this->beforeRequest();
|
||||
$status = $this->processRequest();
|
||||
$this->afterRequest();
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises the [[beforeRequest]] event right BEFORE the application processes the request.
|
||||
*/
|
||||
@@ -174,16 +180,25 @@ abstract class Application extends Module
|
||||
|
||||
/**
|
||||
* Processes the request.
|
||||
* The request is represented in terms of a controller route and action parameters.
|
||||
* @param string $route the route of the request. It may contain module ID, controller ID, and/or action ID.
|
||||
* @param array $params parameters to be bound to the controller action.
|
||||
* Child classes should override this method with actual request processing logic.
|
||||
* @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
|
||||
*/
|
||||
public function processRequest()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a controller with the given route and parameters.
|
||||
* @param string $route the route (e.g. `post/create`)
|
||||
* @param array $params the parameters to be passed to the controller action
|
||||
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
|
||||
* @throws Exception if the route cannot be resolved into a controller
|
||||
*/
|
||||
public function processRequest($route, $params = array())
|
||||
public function runController($route, $params = array())
|
||||
{
|
||||
$result = $this->parseRoute($route);
|
||||
if ($result === null) {
|
||||
$result = $this->createController($route);
|
||||
if ($result === false) {
|
||||
throw new Exception(\Yii::t('yii', 'Unable to resolve the request.'));
|
||||
}
|
||||
list($controller, $action) = $result;
|
||||
|
||||
@@ -114,7 +114,10 @@ class Controller extends Component implements Initable
|
||||
$this->action = $action;
|
||||
|
||||
if ($this->authorize($action) && $this->beforeAction($action)) {
|
||||
$status = $action->runWithParams($params !== null ?: $this->getActionParams());
|
||||
if ($params === null) {
|
||||
$params = $this->getActionParams();
|
||||
}
|
||||
$status = $action->runWithParams($params);
|
||||
$this->afterAction($action);
|
||||
} else {
|
||||
$status = 1;
|
||||
@@ -163,11 +166,12 @@ class Controller extends Component implements Initable
|
||||
* 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)
|
||||
public function invalidActionParams($action, $exception)
|
||||
{
|
||||
throw new Exception(\Yii::t('yii', 'Your request is invalid.'));
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,7 +223,7 @@ class Controller extends Component implements Initable
|
||||
if ($route[0] !== '/' && !$this->module instanceof Application) {
|
||||
$route = '/' . $this->module->getUniqueId() . '/' . $route;
|
||||
}
|
||||
$status = \Yii::$application->processRequest($route, $params);
|
||||
$status = \Yii::$application->runController($route, $params);
|
||||
}
|
||||
if ($exit) {
|
||||
\Yii::$application->end($status);
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace yii\base;
|
||||
|
||||
use yii\util\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* InlineAction represents an action that is defined as a controller method.
|
||||
*
|
||||
@@ -28,13 +30,13 @@ class InlineAction extends Action
|
||||
*/
|
||||
public function runWithParams($params)
|
||||
{
|
||||
$method = new \ReflectionMethod($this->controller, 'action' . $this->id);
|
||||
$params = \yii\util\ReflectionHelper::bindParams($method, $params);
|
||||
if ($params === false) {
|
||||
$this->controller->invalidActionParams($this);
|
||||
try {
|
||||
$method = 'action' . $this->id;
|
||||
$params = ReflectionHelper::extractMethodParams($this->controller, $method, $params);
|
||||
return (int)call_user_func_array(array($this->controller, $method), $params);
|
||||
} catch (Exception $e) {
|
||||
$this->controller->invalidActionParams($this, $e);
|
||||
return 1;
|
||||
} else {
|
||||
return (int)$method->invokeArgs($this, $params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,8 +481,8 @@ abstract class Module extends Component implements Initable
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a given route into controller and action ID.
|
||||
* The parsing process follows the following algorithm:
|
||||
* Creates a controller instance based on the given route.
|
||||
* This method tries to parse the given route (e.g. `post/create`) using the following algorithm:
|
||||
*
|
||||
* 1. Get the first segment in route
|
||||
* 2. If the segment matches
|
||||
@@ -490,12 +490,12 @@ abstract class Module extends Component implements Initable
|
||||
* and return the controller with the rest part of the route;
|
||||
* - a controller class under [[controllerPath]], create the controller instance, and return it
|
||||
* with the rest part of the route;
|
||||
* - an ID in [[modules]], let the corresponding module to parse 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|null the controller instance and action ID. Null if the route cannot be resolved.
|
||||
* @return array|boolean the array of controller instance and action ID. False if the route cannot be resolved.
|
||||
*/
|
||||
public function parseRoute($route)
|
||||
public function createController($route)
|
||||
{
|
||||
if (($route = trim($route, '/')) === '') {
|
||||
$route = $this->defaultRoute;
|
||||
@@ -509,40 +509,16 @@ abstract class Module extends Component implements Initable
|
||||
$route = '';
|
||||
}
|
||||
|
||||
$controller = $this->createController($id);
|
||||
if ($controller !== null) {
|
||||
return array($controller, $route);
|
||||
}
|
||||
|
||||
if (($module = $this->getModule($id)) !== null) {
|
||||
return $module->parseRoute($route);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a controller instance according to the specified controller ID.
|
||||
* For security reasons, the controller ID must start with a lower-case letter
|
||||
* and consist of word characters only.
|
||||
*
|
||||
* This method will first match the controller ID in [[controllers]]. If found,
|
||||
* it will create an instance of controller using the corresponding configuration.
|
||||
* If not found, it will look for a controller class named `XyzController` under
|
||||
* the [[controllerPath]] directory, where `xyz` is the controller ID with the first
|
||||
* letter in upper-case.
|
||||
* @param string $id the controller ID
|
||||
* @return Controller|null the created controller instance. Null if the controller ID is invalid.
|
||||
*/
|
||||
public function createController($id)
|
||||
{
|
||||
// Controller IDs must start with a lower-case letter and consist of word characters only
|
||||
if (!preg_match('/^[a-z][a-zA-Z0-9_]*$/', $id)) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($this->controllers[$id])) {
|
||||
return \Yii::createObject($this->controllers[$id], $id, $this);
|
||||
return array(
|
||||
\Yii::createObject($this->controllers[$id], $id, $this),
|
||||
$route,
|
||||
);
|
||||
}
|
||||
|
||||
$className = ucfirst($id) . 'Controller';
|
||||
@@ -552,10 +528,17 @@ abstract class Module extends Component implements Initable
|
||||
require($classFile);
|
||||
}
|
||||
if (class_exists($className, false) && is_subclass_of($className, '\yii\base\Controller')) {
|
||||
return $className::newInstance(array(), $id, $this);
|
||||
return array(
|
||||
$className::newInstance(array(), $id, $this),
|
||||
$route,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
if (($module = $this->getModule($id)) !== null) {
|
||||
return $module->createController($route);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace yii\console;
|
||||
|
||||
use yii\base\Exception;
|
||||
use yii\util\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* Application represents a console application.
|
||||
@@ -71,19 +72,39 @@ class Application extends \yii\base\Application
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the application.
|
||||
* This is the main entrance of an application.
|
||||
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
|
||||
* Processes the request.
|
||||
* The request is represented in terms of a controller route and action parameters.
|
||||
* @return integer the exit status of the controller action (0 means normal, non-zero values mean abnormal)
|
||||
* @throws Exception if the route cannot be resolved into a controller
|
||||
*/
|
||||
public function run()
|
||||
public function processRequest()
|
||||
{
|
||||
if (!isset($_SERVER['argv'])) {
|
||||
die('This script must be run from the command line.');
|
||||
}
|
||||
list($route, $params) = $this->resolveRequest($_SERVER['argv']);
|
||||
$this->beforeRequest();
|
||||
$status = $this->processRequest($route, $params);
|
||||
$this->afterRequest();
|
||||
return $this->runController($route, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a controller with the given route and parameters.
|
||||
* @param string $route the route (e.g. `post/create`)
|
||||
* @param array $params the parameters to be passed to the controller action
|
||||
* @return integer the exit status (0 means normal, non-zero values mean abnormal)
|
||||
* @throws Exception if the route cannot be resolved into a controller
|
||||
*/
|
||||
public function runController($route, $params = array())
|
||||
{
|
||||
$result = $this->createController($route);
|
||||
if ($result === false) {
|
||||
throw new Exception(\Yii::t('yii', 'Unable to resolve the request.'));
|
||||
}
|
||||
list($controller, $action) = $result;
|
||||
$priorController = $this->controller;
|
||||
$this->controller = $controller;
|
||||
$params = ReflectionHelper::initObjectWithParams($controller, $params);
|
||||
$status = $controller->run($action, $params);
|
||||
$this->controller = $priorController;
|
||||
return $status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Command class file.
|
||||
* Controller class file.
|
||||
*
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright Copyright © 2008-2012 Yii Software LLC
|
||||
@@ -9,6 +9,10 @@
|
||||
|
||||
namespace yii\console;
|
||||
|
||||
use yii\base\InlineAction;
|
||||
use yii\base\Exception;
|
||||
use yii\util\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* Command represents an executable console command.
|
||||
*
|
||||
@@ -46,105 +50,6 @@ namespace yii\console;
|
||||
*/
|
||||
class Controller extends \yii\base\Controller
|
||||
{
|
||||
/**
|
||||
* Executes the command.
|
||||
* The default implementation will parse the input parameters and
|
||||
* dispatch the command request to an appropriate action with the corresponding
|
||||
* option values
|
||||
* @param array $args command line parameters for this command.
|
||||
*/
|
||||
public function run($args)
|
||||
{
|
||||
list($action, $options, $args) = $this->resolveRequest($args);
|
||||
$methodName = 'action' . $action;
|
||||
if (!preg_match('/^\w+$/', $action) || !method_exists($this, $methodName))
|
||||
{
|
||||
$this->usageError("Unknown action: " . $action);
|
||||
}
|
||||
|
||||
$method = new \ReflectionMethod($this, $methodName);
|
||||
$params = array();
|
||||
// named and unnamed options
|
||||
foreach ($method->getParameters() as $param) {
|
||||
$name = $param->getName();
|
||||
if (isset($options[$name])) {
|
||||
if ($param->isArray())
|
||||
$params[] = is_array($options[$name]) ? $options[$name] : array($options[$name]);
|
||||
else if (!is_array($options[$name]))
|
||||
$params[] = $options[$name];
|
||||
else
|
||||
$this->usageError("Option --$name requires a scalar. Array is given.");
|
||||
} else if ($name === 'args')
|
||||
$params[] = $args;
|
||||
else if ($param->isDefaultValueAvailable())
|
||||
$params[] = $param->getDefaultValue();
|
||||
else
|
||||
$this->usageError("Missing required option --$name.");
|
||||
unset($options[$name]);
|
||||
}
|
||||
|
||||
// try global options
|
||||
if (!empty($options)) {
|
||||
$class = new \ReflectionClass(get_class($this));
|
||||
foreach ($options as $name => $value) {
|
||||
if ($class->hasProperty($name)) {
|
||||
$property = $class->getProperty($name);
|
||||
if ($property->isPublic() && !$property->isStatic()) {
|
||||
$this->$name = $value;
|
||||
unset($options[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($options))
|
||||
$this->usageError("Unknown options: " . implode(', ', array_keys($options)));
|
||||
|
||||
if ($this->beforeAction($action, $params)) {
|
||||
$method->invokeArgs($this, $params);
|
||||
$this->afterAction($action, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the command line arguments and determines which action to perform.
|
||||
* @param array $args command line arguments
|
||||
* @return array the action name, named options (name=>value), and unnamed options
|
||||
*/
|
||||
protected function resolveRequest($args)
|
||||
{
|
||||
$options = array(); // named parameters
|
||||
$params = array(); // unnamed parameters
|
||||
foreach ($args as $arg) {
|
||||
if (preg_match('/^--(\w+)(=(.*))?$/', $arg, $matches)) // an option
|
||||
{
|
||||
$name = $matches[1];
|
||||
$value = isset($matches[3]) ? $matches[3] : true;
|
||||
if (isset($options[$name])) {
|
||||
if (!is_array($options[$name]))
|
||||
$options[$name] = array($options[$name]);
|
||||
$options[$name][] = $value;
|
||||
} else
|
||||
$options[$name] = $value;
|
||||
} else if (isset($action))
|
||||
$params[] = $arg;
|
||||
else
|
||||
$action = $arg;
|
||||
}
|
||||
if (!isset($action))
|
||||
$action = $this->defaultAction;
|
||||
|
||||
return array($action, $options, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string the command name.
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the command description.
|
||||
* This method may be overridden to return the actual command description.
|
||||
@@ -208,28 +113,6 @@ class Controller extends \yii\base\Controller
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a view file.
|
||||
* @param string $_viewFile_ view file path
|
||||
* @param array $_data_ optional data to be extracted as local view variables
|
||||
* @param boolean $_return_ whether to return the rendering result instead of displaying it
|
||||
* @return mixed the rendering result if required. Null otherwise.
|
||||
*/
|
||||
public function renderFile($_viewFile_, $_data_ = null, $_return_ = false)
|
||||
{
|
||||
if (is_array($_data_))
|
||||
extract($_data_, EXTR_PREFIX_SAME, 'data');
|
||||
else
|
||||
$data = $_data_;
|
||||
if ($_return_) {
|
||||
ob_start();
|
||||
ob_implicit_flush(false);
|
||||
require($_viewFile_);
|
||||
return ob_get_clean();
|
||||
} else
|
||||
require($_viewFile_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads input via the readline PHP extension if that's available, or fgets() if readline is not installed.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
namespace yii\util;
|
||||
|
||||
use yii\base\Exception;
|
||||
|
||||
/**
|
||||
* ReflectionHelper
|
||||
*
|
||||
@@ -19,46 +21,83 @@ class ReflectionHelper
|
||||
{
|
||||
/**
|
||||
* Prepares parameters so that they can be bound to the specified method.
|
||||
* This method mainly helps method parameter binding. It converts `$params`
|
||||
* into an array which can be passed to `call_user_func_array()` when calling
|
||||
* the specified method. The conversion is based on the matching of method parameter names
|
||||
* This method converts the input parameters into an array that can later be
|
||||
* passed to `call_user_func_array()` when calling the specified method.
|
||||
* The conversion is based on the matching of method parameter names
|
||||
* and the input array keys. For example,
|
||||
*
|
||||
* ~~~
|
||||
* class Foo {
|
||||
* function bar($a, $b) { ... }
|
||||
* }
|
||||
*
|
||||
* $method = new \ReflectionMethod('Foo', 'bar');
|
||||
* $object = new Foo;
|
||||
* $params = array('b' => 2, 'c' => 3, 'a' => 1);
|
||||
* var_export(ReflectionHelper::bindMethodParams($method, $params));
|
||||
* // would output: array('a' => 1, 'b' => 2)
|
||||
* var_export(ReflectionHelper::extractMethodParams($object, 'bar', $params));
|
||||
* // output: array('a' => 1, 'b' => 2);
|
||||
* ~~~
|
||||
*
|
||||
* @param \ReflectionMethod $method the method reflection
|
||||
* @param object|string $object the object or class name that owns the specified method
|
||||
* @param string $method the method name
|
||||
* @param array $params the parameters in terms of name-value pairs
|
||||
* @return array|boolean the parameters that can be passed to the method via `call_user_func_array()`.
|
||||
* False is returned if the input parameters do not follow the method declaration.
|
||||
* @return array parameters that are needed by the method only and
|
||||
* can be passed to the method via `call_user_func_array()`.
|
||||
* @throws Exception if any required method parameter is not found in the given parameters
|
||||
*/
|
||||
public static function bindParams($method, $params)
|
||||
public static function extractMethodParams($object, $method, $params)
|
||||
{
|
||||
$m = new \ReflectionMethod($object, $method);
|
||||
$ps = array();
|
||||
foreach ($method->getParameters() as $param) {
|
||||
foreach ($m->getParameters() as $param) {
|
||||
$name = $param->getName();
|
||||
if (array_key_exists($name, $params)) {
|
||||
if ($param->isArray()) {
|
||||
$ps[$name] = is_array($params[$name]) ? $params[$name] : array($params[$name]);
|
||||
} elseif (!is_array($params[$name])) {
|
||||
$ps[$name] = $params[$name];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$ps[$name] = $params[$name];
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$ps[$name] = $param->getDefaultValue();
|
||||
} else {
|
||||
return false;
|
||||
throw new Exception(\Yii::t('yii', 'Missing required parameter "{name}".', array('{name' => $name)));
|
||||
}
|
||||
}
|
||||
return $ps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes an object with the given parameters.
|
||||
* Only the public non-static properties of the object will be initialized, and their names must
|
||||
* match the given parameter names. For example,
|
||||
*
|
||||
* ~~~
|
||||
* class Foo {
|
||||
* public $a;
|
||||
* protected $b;
|
||||
* }
|
||||
* $object = new Foo;
|
||||
* $params = array('b' => 2, 'c' => 3, 'a' => 1);
|
||||
* $remaining = ReflectionHelper::bindObjectParams($object, $params);
|
||||
* var_export($object); // output: $object->a = 1; $object->b = null;
|
||||
* var_export($remaining); // output: array('b' => 2, 'c' => 3);
|
||||
* ~~~
|
||||
*
|
||||
* @param object $object the object whose properties are to be initialized
|
||||
* @param array $params the input parameters to be used to initialize the object
|
||||
* @return array the remaining unused input parameters
|
||||
*/
|
||||
public static function initObjectWithParams($object, $params)
|
||||
{
|
||||
if (empty($params)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$class = new \ReflectionClass(get_class($object));
|
||||
foreach ($params as $name => $value) {
|
||||
if ($class->hasProperty($name)) {
|
||||
$property = $class->getProperty($name);
|
||||
if ($property->isPublic() && !$property->isStatic()) {
|
||||
$object->$name = $value;
|
||||
unset($params[$name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user