I'm in the process of building a soon to be opensource CMS. 100% MVC. The basic idea is that a plugin should be able to add pages (read: methods) to a given controller, without needing to instantiate a new Route object, which the Router would then have to iterate over to test for a match. class_alias won't work. You can only extend 1 controller using extend won't work. You can only extend 1 controller.After a number of hours scanning the manual and using google to try to find an answer to this problem, I was at a loss, so I started hacking. This is what I came up with, and it works, and due to the design of the application, it adds only a slight performance hit, which is due to the need to instantiate the controller twice and pass the called method to the proxied instance of the class. Bear in mind that the end user writing modules and plugins for this system needs to use minimal effort to accomplish these things, which provides the need for all of this context switching.Base Controller class which controllers that wish to be virtually extensible must extendnamespace t;class Controller{    public $callables = [];    public $app;    final public function __construct()    {        $this->app = PHP::getInstance();        $this->app->invokePlugins('\\'.get_class($this));        $c = $this->app->getControllerExtensions($this);//plugins register virtual methods to controllers.         if(!empty($c))        {            foreach($c as $k=>$v)            {                //handle the next step in the extend method to prevent vague exceptions being thrown                $this->extend($k,$v);            }           }    }    final public function __call($fn_name,$fn_args=[])    {        if(isset($this->callables[$fn_name])){            return call_user_func_array($this->callables[$fn_name],$fn_args); //first try callables. allows overwriting built in methods.        }        $c2 = '\\'.get_class($this);        $instance = new $c2; //instantiate the class again since we cant get at its private methods from this context        if(method_exists($instance,$fn_name)){            return $instance->proxy($fn_name,$fn_args); //use the proxy trait to successfully invoke the controller method that is private        }        //if we made it here, we know that the method does not exist. throw \t\Exceptions\Pass        $this->app->pass();    }    final protected function extend($fn_name,$fn_callback)    {        if(!is_callable($fn_callback)){            throw new \InvalidArgumentException('`extend` expects the second parameter to be a valid callable.');        }        $this->callables[$fn_name] = $fn_callback;    }}The Proxy trait required to allow base controller to call child controllers private methodsnamespace t;trait Proxy{    public function proxy($fn,$args=[])    {        // here we are in the private context of the class using this trait,         // so we can call its private methods.        return call_user_func_array([$this,$fn],$args);    }}An example class. Notice how this is achieved with minimal effort to the developer.namespace t\BuiltIns\Routes;class Admin extends \t\Controller{    use \t\Proxy;    // methods must be private to ensure that     // `\t\Controller::__call()` gets called in the parent class context    private function dashboard()    {        $this->app->view->render('admin/dashboard.twig');    }    private function plugins()    {        $this->app->view->render('admin/plugins.twig');    }    // now plugins and modules may extend this class to add new routes,     // without requiring a costly new instance of `RouteObject` for the    // `Router` to iterate over when matching routes.}Comments:I do realize that it is technically an abuse of the language to support a feature that isn't built in. But it seems to be a solid enough implementation that it won't cause any unforeseen problems. For what it's worth, RouteObject will not invoke the __call method in any controller, so that prevents people from causing the error log to be filled up with \t\Controller::__call() missing argument exceptions. The system is based heavily on Slim 2.6.x, with fairly extensive modifications to suit this particular use case.
Monkey patch allowing virtual controller methods in PHP MVC application
php;mvc;controller