Reputation: 8698
Just as a learning exercise I'm trying to build my own mini MVC in PHP.
What I want to achieve is a method that can be called before cetain other methods (similar to the before_filter method in ruby on rails)
For example; given the below controller class a user must have permission to do certain activities, so say I wanted to call checkPermissions()
from BaseController
before the create()
, update()
and delete()
.
class HomeController extends BaseController {
beforeFilter(checkPermissions,['create','update','delete']);
function index(){}
function create(){}
function update(){}
function delete(){}
}
Can anyone give me any guidance on how to achieve this? or enlighten me on the PHP way of doing this sort of task. I'm relatively new to PHP so please be gentle.
Upvotes: 3
Views: 1201
Reputation: 992
You should make use of the magic method __call
That magic method is called every time you call any function within your class.
That way you should be able to do the following:
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() {}
}
Please note that this only works for private or protected methods, not public ones.
If you want to use this on public methods, you should apply a decorator pattern like the following example:
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
I hope it helps :)
Upvotes: 5
Reputation: 11328
There are several ways to accomplish this, here's one way to do it:
Create a beforeFilter
method in your controller:
// In HomeController:
public function beforeFilter($action = 'index') {
if (in_array($action, ['create', 'update', 'delete'])) {
return checkPermissions(); // where checkPermissions() returns true/false
}
return true;
}
I assume you have some sort of dispatcher in your framework that knows that for a certain URI, it needs to call the create
action of your HomeController
. Before it calls that, you can call beforeFilter
if it exists:
// 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");
}
}
Upvotes: -1