在PHP中创建一个前置过滤器方法

3

我正在尝试用PHP构建自己的迷你MVC框架作为学习练习。

我想实现一个在某些其他方法之前调用的方法(类似于Ruby on Rails中的before_filter方法)。

例如,给定以下控制器类,用户必须有权限执行某些活动,因此假设我想在BaseController中的create()update()delete()之前调用checkPermissions()方法。

class HomeController extends BaseController {

    beforeFilter(checkPermissions,['create','update','delete']);

    function index(){}

    function create(){}

    function update(){}

    function delete(){}

}

有人可以给我指导如何实现这个吗?或者启发我用PHP完成这样的任务。我对PHP比较新,请温柔点。


1
https://dev59.com/7XA65IYBdhLWcg3wogEb - Sebastian Brosch
2个回答

5

你应该利用魔术方法 __call

每当你在类中调用任何函数时,都会调用该魔术方法。

这样你就可以做到以下操作:

class HomeController extends BaseController {

    public function __call($method, $arguments)
    {
        if (method_exists($this, $method))
        {
            if (in_array($method, ['create', 'update', 'delete']) && !$this->checkPermissions())
            {
                return null; // Permission is wrong, do something
            }

            return call_user_func_array([$this, $method], $arguments);
        }
    }

    protected function index() {}

    protected function create() {}

    protected function update() {}

    protected function delete() {}

}

请注意,这仅适用于私有或受保护的方法,而不是公共方法。
如果您想在公共方法上使用此功能,则应像以下示例一样应用装饰器模式:
class HomeController extends BaseController {

    public function index() {}

    public function create() {}

    public function update() {}

    public function delete() {}

}

class ControllerDecorator {

    private $controller;

    public function __construct($controller)
    {
        $this->controller = $controller;
    }

    public function __call($method, $arguments)
    {
        if (method_exists($this->controller, $method))
        {
            if (in_array($method, ['create', 'update', 'delete']) && !$this->checkPermissions())
            {
                return null; // Permission is wrong, do something
            }

            return call_user_func_array([$this->controller, $method], $arguments);
        }
    }

    private function checkPermissions() {}
}

$homeController = new HomeController();
$decoratedHomeController = new ControllerDecorator($homeController);

$decoratedHomeController->update(); // Will check for permissions

我希望这有所帮助 :)

这听起来正是我正在寻找的。我很快就会尝试一下,非常感谢。 - Brad
1
请注意,如果您的方法是public,则不会调用__call(),而仅针对privateprotected函数。 - rickdenhaan
@rickdenhaan __call() 也适用于公共方法。它适用于所有不可访问的方法,__call 仅在调用代码无法访问成员时被调用。 - ajimix
1
请参见 https://3v4l.org/sFN5C 以获取示例。您可以看到它不调用 public function a()__call 方法。公共函数已经可访问,因此不会调用 __call() - rickdenhaan
所以只有在方法不是公共的情况下才能起作用,而我的方法都是公共的。那么这个解决方案行不通? - Brad
显示剩余6条评论

-1

有几种方法可以实现这个,这里介绍一种方法:

在你的控制器中创建一个beforeFilter方法:

// In HomeController:
public function beforeFilter($action = 'index') {
    if (in_array($action, ['create', 'update', 'delete'])) {
        return checkPermissions(); // where checkPermissions() returns true/false
    }

    return true;
}

我假设你的框架中有某种调度程序,它知道对于特定的URI,它需要调用HomeControllercreate操作。在调用之前,如果存在,你可以调用beforeFilter

// In your dispatcher class:
public function dispatch($uri) {
    // determine which $controller and $action to call

    $mayProceed = true;
    if (method_exists($controller, 'beforeFilter')) {
        $mayProceed = $controller->beforeFilter($action);
    }

    // note: because we're checking if the method exists, your
    // controller doesn't *need* to have a beforeFilter method.
    // If it doesn't, $mayProceed will simply be true.

    if ($mayProceed) {
        $controller->$action();
    } else {
        throw new Exception("Dispatcher: beforeFilter told me you can't access this page");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接