I try to use one behavior(ActionFilter) to control other behaviors(ActionFilter) whether to work or not.
For exp.
In my TestController
public function behaviors()
{
$behaviors['A'] = [
//ActionFilter A determine B or C which to run .....
];
$behaviors['B'] = [
//ActionFilter B .....
];
$behaviors['C'] = [
//ActionFilter C .....
];
return $behaviors;
}
In behavior A class,
I attach more behaviors to TestController works fine, but when I detach B or C,comes the problem.
Call to a member function on() on a non-object
The code was ActionFilter::beforeFilter($event)
$event->isValid = $this->beforeAction($event->action);
if ($event->isValid) {
// call afterFilter only if beforeFilter succeeds
// beforeFilter and afterFilter should be properly nested
$this->owner->on(Controller::EVENT_AFTER_ACTION, [$this, 'afterFilter'], null, false);
}
Then I look into it.I found filters not removed were all fine, Their owner is TestController,that’s correct.
But the filter detached by A is not really detached, and which has an owner(yii\base\ActionFilter)
In my TestController.php:
public function behaviors()
{
$behaviors['temperatureAdaptor'] = [
'class' => TemperatureFilter::className(),
'hotFilters' => [
[
'class' => IcyDrink::className(),
],
],
'coldFilters' => [
[
'class' => HotDrink::className(),
],
],
];
$behaviors['hotFilter'] = [
'class' => IcyDrink::className(),
];
$behaviors['coldFilter'] = [
'class' => HotDrink::className(),
];
return $behaviors;
}
Here’s the filter:
class TemperatureFilter extends ActionFilter
{
protected $hotFilters = [];
protected $coldFilters = [];
public function setHotFilters($hotFilters)
{
$this->hotFilters = $hotFilters;
}
public function setColdFilters($coldFilters)
{
$this->coldFilters = $coldFilters;
}
public function beforeAction($action)
{
$temperatureType = Temperature::hotOrCold();
//1. add corresponding filters
$matchedProperty = strtolower($temperatureType) . 'Filters';
foreach ($this->{$matchedProperty} as $filter) {
$this->attachFilter($action->controller, $filter);
}
//2.remove correspoding filters
$mismatchedType = ($temperatureType == "HOT") ? "COLD" : "HOT";
$mismatchedProperty = strtolower($mismatchedType) . 'Filters';
foreach ($this->{$mismatchedProperty} as $filter) {
$this->detachFilter($action->controller, $filter);
}
return parent::beforeAction($action); // TODO: Change the autogenerated stub
}
protected function attachFilter($controller, $filter)
{
$behaviors = $controller->getBehaviors();
foreach ($behaviors as $behavior) {
if (is_a($behavior, $filter['class'])) {
Yii::info(StringHelper::basename($behavior->className()) . " ALREADY INSTALLED");
return;
}
}
$filterName = ($filter['name']) ? $filter['name'] : StringHelper::basename($filter['class']);
$controller->attachBehavior($filterName, $filter);
}
protected function detachFilter($controller, $filter)
{
$behaviors = $controller->getBehaviors();
foreach ($behaviors as $name => $behavior) {
if (is_a($behavior, $filter['class'])) {
$controller->detachBehavior($name);
return;
}
}
}
}
Code above went wrong:
PHP Fatal Error – yii\base\ErrorException
Call to a member function on() on a non-object
Then I trace and found the IcyDrink or HotDrink was detached, but they were still invoke by owner yii\base\ActionFilter.
So weird.