diff --git a/framework/base/Action.php b/framework/base/Action.php index 809d3b1..aec3b8d 100644 --- a/framework/base/Action.php +++ b/framework/base/Action.php @@ -18,7 +18,7 @@ namespace yii\base; * Derived classes must implement a method named `run()`. This method * will be invoked by the controller when the action is requested. * The `run()` method can have parameters which will be filled up - * automatically according to their names. + * with user input values automatically according to their names. * * @author Qiang Xue * @since 2.0 diff --git a/framework/base/ActionEvent.php b/framework/base/ActionEvent.php new file mode 100644 index 0000000..34c440e --- /dev/null +++ b/framework/base/ActionEvent.php @@ -0,0 +1,40 @@ + + * @since 2.0 + */ +class ActionEvent extends Event +{ + /** + * @var Action the action currently being executed + */ + public $action; + /** + * @var boolean whether the action is in valid state and its life cycle should proceed. + */ + public $isValid = true; + + /** + * Constructor. + * @param Action $action the action associated with this action event. + */ + public function __construct(Action $action) + { + $this->action = $action; + } +} diff --git a/framework/base/ActionFilter.php b/framework/base/ActionFilter.php index b269492..0da475a 100644 --- a/framework/base/ActionFilter.php +++ b/framework/base/ActionFilter.php @@ -9,66 +9,84 @@ namespace yii\base; +use yii\util\ArrayHelper; + /** - * ActionFilter is the base class for all filters. + * ActionFilter is the base class for all action filters. + * + * A filter can be applied to a controller action at different stages of its life cycle. In particular, + * it responds to the following events that are raised when an action is being executed: * - * A filter can be applied before and after an action is executed. - * It can modify the context that the action is to run or decorate the result that the - * action generates. + * 1. authorize + * 2. beforeAction + * 3. beforeRender + * 4. afterRender + * 5. afterAction * - * Override {@link preFilter()} to specify the filtering logic that should be applied - * before the action, and {@link postFilter()} for filtering logic after the action. + * Derived classes may respond to these events by overriding the corresponding methods in this class. + * For example, to create an access control filter, one may override the [[authorize()]] method. * * @author Qiang Xue * @since 2.0 */ -class ActionFilter extends CComponent implements IFilter +class ActionFilter extends Behavior { /** - * Performs the filtering. - * The default implementation is to invoke {@link preFilter} - * and {@link postFilter} which are meant to be overridden - * child classes. If a child class needs to override this method, - * make sure it calls $filterChain->run() - * if the action should be executed. - * @param ActionFilterChain $filterChain the filter chain that the filter is on. + * @var Controller the owner of this behavior. For action filters, this should be a controller object. */ - public function filter($filterChain) - { - if($this->preFilter($filterChain)) - { - $filterChain->run(); - $this->postFilter($filterChain); - } - } - + public $owner; + /** + * @var array IDs (case-insensitive) of actions that this filter applies to. + * If this property is empty or not set, it means this filter applies to all actions. + * Note that if an action appears in [[except]], the filter will not apply to this action, even + * if the action also appears in [[only]]. + * @see exception + */ + public $only; /** - * Initializes the filter. - * This method is invoked after the filter properties are initialized - * and before {@link preFilter} is called. - * You may override this method to include some initialization logic. - * @since 1.1.4 + * @var array IDs (case-insensitive) of actions that this filter does NOT apply to. */ + public $except; + public function init() { + $this->owner->on('authorize', array($this, 'handleEvent')); + $this->owner->on('beforeAction', array($this, 'handleEvent')); + $this->owner->on('beforeRender', array($this, 'handleEvent')); + $this->owner->getEventHandlers('afterRender')->insertAt(0, array($this, 'handleEvent')); + $this->owner->getEventHandlers('afterAction')->insertAt(0, array($this, 'handleEvent')); } - /** - * Performs the pre-action filtering. - * @param ActionFilterChain $filterChain the filter chain that the filter is on. - * @return boolean whether the filtering process should continue and the action - * should be executed. - */ - protected function preFilter($filterChain) + public function authorize(ActionEvent $event) { - return true; } - /** - * Performs the post-action filtering. - * @param ActionFilterChain $filterChain the filter chain that the filter is on. - */ - protected function postFilter($filterChain) + public function beforeAction(ActionEvent $event) + { + } + + public function beforeRender(ActionEvent $event) + { + } + + public function afterRender(ActionEvent $event) + { + } + + public function afterAction(ActionEvent $event) + { + } + + public function handleEvent(ActionEvent $event) + { + if ($this->applyTo($event->action)) { + $this->{$event->name}($event); + } + } + + public function applyTo(Action $action) { + return (empty($this->only) || ArrayHelper::search($action->id, $this->only, false) !== false) + && (empty($this->except) || ArrayHelper::search($action->id, $this->except, false) === false); } } \ No newline at end of file diff --git a/framework/base/Behavior.php b/framework/base/Behavior.php index d65fe91..f0c87a4 100644 --- a/framework/base/Behavior.php +++ b/framework/base/Behavior.php @@ -16,17 +16,15 @@ namespace yii\base; * In particular, it can "inject" its own methods and properties into the component * and make them directly accessible via the component. * - * @property Component $owner The owner component that this behavior is attached to. - * * @author Qiang Xue * @since 2.0 */ class Behavior extends \yii\base\Object { /** - * @var Component the owner component + * @var Component the owner of this behavior */ - private $_owner; + public $owner; /** * Declares event handlers for the [[owner]]'s events. @@ -70,7 +68,7 @@ class Behavior extends \yii\base\Object */ public function attach($owner) { - $this->_owner = $owner; + $this->owner = $owner; foreach ($this->events() as $event => $handler) { $owner->on($event, is_string($handler) ? array($this, $handler) : $handler); } @@ -88,15 +86,6 @@ class Behavior extends \yii\base\Object foreach ($this->events() as $event => $handler) { $owner->off($event, is_string($handler) ? array($this, $handler) : $handler); } - $this->_owner = null; - } - - /** - * Returns the owner component that this behavior is attached to. - * @return Component the owner component that this behavior is attached to. - */ - public function getOwner() - { - return $this->_owner; + $this->owner = null; } } diff --git a/framework/base/Component.php b/framework/base/Component.php index ac08315..0cf90d8 100644 --- a/framework/base/Component.php +++ b/framework/base/Component.php @@ -450,6 +450,9 @@ class Component extends \yii\base\Object if (!($behavior instanceof Behavior)) { $behavior = \Yii::createObject($behavior); } + if (is_int($name)) { + $name = '_b ' . $name; // anonymous behavior + } if (isset($this->_b[$name])) { $this->_b[$name]->detach($this); } diff --git a/framework/base/Controller.php b/framework/base/Controller.php index 05b568e..7f73bfc 100644 --- a/framework/base/Controller.php +++ b/framework/base/Controller.php @@ -71,7 +71,10 @@ abstract class Controller extends Component implements Initable public $defaultAction = 'index'; private $_id; - private $_action; + /** + * @var Action the action that is currently being executed + */ + public $action; private $_module; @@ -95,38 +98,6 @@ abstract class Controller extends Component implements Initable } /** - * Returns the filter configurations. - * - * By overriding this method, child classes can specify filters to be applied to actions. - * - * This method returns an array of filter specifications. Each array element specify a single filter. - * - * For a method-based filter (called inline filter), it is specified as 'FilterName[ +|- Action1, Action2, ...]', - * where the '+' ('-') operators describe which actions should be (should not be) applied with the filter. - * - * For a class-based filter, it is specified as an array like the following: - *
-	 * array(
-	 *	 'FilterClass[ +|- Action1, Action2, ...]',
-	 *	 'name1'=>'value1',
-	 *	 'name2'=>'value2',
-	 *	 ...
-	 * )
-	 * 
- * where the name-value pairs will be used to initialize the properties of the filter. - * - * Note, in order to inherit filters defined in the parent class, a child class needs to - * merge the parent filters with child filters using functions like array_merge(). - * - * @return array a list of filter configurations. - * @see CFilter - */ - public function filters() - { - return array(); - } - - /** * Returns a list of external action classes. * Array keys are action IDs, and array values are the corresponding * action class in dot syntax (e.g. 'edit'=>'application.controllers.article.EditArticle') @@ -182,16 +153,6 @@ abstract class Controller extends Component implements Initable } /** - * Returns the access rules for this controller. - * Override this method if you use the {@link filterAccessControl accessControl} filter. - * @return array list of access rules. See {@link CAccessControlFilter} for details about rule specification. - */ - public function accessRules() - { - return array(); - } - - /** * Runs the named action. * Filters specified via {@link filters()} will be applied. * @param string $actionID action ID @@ -234,10 +195,10 @@ abstract class Controller extends Component implements Initable } else { - $priorAction = $this->_action; - $this->_action = $action; + $priorAction = $this->action; + $this->action = $action; CFilterChain::create($this, $action, $filters)->run(); - $this->_action = $priorAction; + $this->action = $priorAction; } } @@ -249,8 +210,8 @@ abstract class Controller extends Component implements Initable */ public function runAction($action) { - $priorAction = $this->_action; - $this->_action = $action; + $priorAction = $this->action; + $this->action = $action; if ($this->beforeAction($action)) { if ($action->runWithParams($this->getActionParams())) { $this->afterAction($action); @@ -258,7 +219,7 @@ abstract class Controller extends Component implements Initable $this->invalidActionParams($action); } } - $this->_action = $priorAction; + $this->action = $priorAction; } /** @@ -384,22 +345,6 @@ abstract class Controller extends Component implements Initable } /** - * @return Action the action currently being executed, null if no active action. - */ - public function getAction() - { - return $this->_action; - } - - /** - * @param Action $value the action currently being executed. - */ - public function setAction($value) - { - $this->_action = $value; - } - - /** * @return string ID of the controller */ public function getId() @@ -467,14 +412,28 @@ abstract class Controller extends Component implements Initable } /** + * This method is invoked when checking the access for the action to be executed. + * @param Action $action the action to be executed. + * @return boolean whether the action is allowed to be executed. + */ + public function authorize(Action $action) + { + $event = new ActionEvent($action); + $this->trigger(__METHOD__, $event); + return $event->isValid; + } + + /** * This method is invoked right before an action is to be executed (after all possible filters.) * You may override this method to do last-minute preparation for the action. * @param Action $action the action to be executed. - * @return boolean whether the action should be executed. + * @return boolean whether the action should continue to be executed. */ - protected function beforeAction($action) + public function beforeAction(Action $action) { - return true; + $event = new ActionEvent($action); + $this->trigger(__METHOD__, $event); + return $event->isValid; } /** @@ -482,7 +441,31 @@ abstract class Controller extends Component implements Initable * You may override this method to do some postprocessing for the action. * @param Action $action the action just executed. */ - protected function afterAction($action) + public function afterAction(Action $action) + { + $event = new ActionEvent($action); + $this->trigger(__METHOD__, $event); + } + + /** + * This method is invoked right before an action renders its result using [[render()]]. + * @param Action $action the action to be executed. + * @return boolean whether the action should continue to render. + */ + public function beforeRender(Action $action) + { + $event = new ActionEvent($action); + $this->trigger(__METHOD__, $event); + return $event->isValid; + } + + /** + * This method is invoked right after an action renders its result using [[render()]]. + * @param Action $action the action just executed. + */ + public function afterRender(Action $action) { + $event = new ActionEvent($action); + $this->trigger(__METHOD__, $event); } } diff --git a/framework/base/View.php b/framework/base/View.php index d6749e0..e60d9cc 100644 --- a/framework/base/View.php +++ b/framework/base/View.php @@ -39,6 +39,10 @@ class View extends Component * the value of [[Application::sourceLanguage]]. */ public $sourceLanguage; + /** + * @var mixed custom parameters that are available in the view template + */ + public $params; /** * Renders a view. diff --git a/framework/db/dao/Command.php b/framework/db/dao/Command.php index 619b723..41601ce 100644 --- a/framework/db/dao/Command.php +++ b/framework/db/dao/Command.php @@ -354,7 +354,7 @@ class Command extends \yii\base\Component } \Yii::trace("Querying SQL: {$sql}{$paramLog}", __CLASS__); -//echo $sql . "\n\n"; + if ($db->queryCachingCount > 0 && $db->queryCachingDuration >= 0 && $method !== '') { $cache = \Yii::$application->getComponent($db->queryCacheID); } diff --git a/framework/util/ArrayHelper.php b/framework/util/ArrayHelper.php index dc4fe97..07a7ac5 100644 --- a/framework/util/ArrayHelper.php +++ b/framework/util/ArrayHelper.php @@ -208,4 +208,31 @@ class ArrayHelper extends \yii\base\Component } return $result; } + + /** + * Searches the array for a given value and returns the corresponding key if found. + * This method is similar to array_search() with the enhancement that it can also + * search for strings in a case-insensitive manner. + * @param mixed $needle the value being searched for + * @param array $haystack the array to be searched through + * @param boolean $caseSensitive whether to perform a case-sensitive search + * @param boolean $strict whether to perform a type-strict search + * @return boolean|mixed the key of the value if it matches $needle. False if the value is not found. + */ + public static function search($needle, array $haystack, $caseSensitive = true, $strict = true) + { + if ($caseSensitive || !is_string($needle)) { + return array_search($needle, $haystack, $strict); + } + foreach ($haystack as $key => $value) { + if (is_string($value)) { + if (strcasecmp($value, $needle) === 0) { + return true; + } + } elseif ($strict && $key === $value || !$strict && $key == $value) { + return true; + } + } + return false; + } } \ No newline at end of file