Search code examples
phpcakephpurl-routingautoloader

Cake PHP - creating some kind of a mod for application


I am working on outgoing dedicated project written in CakePHP, that grown quite big. The architecture uses a bunch of plugins as well. From the beginning of the project there was only one instance of this application. However Client found another area that the application can work. It will use the same codebase but separate database. The thing is that codebase is in fact applicable in about 90%, what means that in most of the time it will do its work, but there are some slight changes that are needed. After some little enhancements it is clear that those modifications will continue for this new instance. But at the same time there will be changes applicable for both instances. It wouldn't be very clean solution for making a bunch of conditions in whole application and checking which instance is actually running.

So my idea is:

  • keep the project in one repository and make changes and fixes applicable for all instances in one codebase
  • make some kind of "mod" that will contain classes that will overload the default classes of the project only when needed and only for the specific instance
  • to make it more clear it would the best to have parallel file structure to the app directory that will contain those new classes (of course only those that are needed)
  • very important thing is to not touch the cake core at all, I want be able to upgrade it anytime without problems

Example:

app/controllers/invoices_controller.php:
InvoicesController extends AppController {
  public function generateInvoice() {}
}

newbusinness/controllers/invoices_controller.php:
NewbusinessInvoicesController extends InvoicesController {
  public function generateInvoice() {}
}

What is important it when we will go to:

www.mynewbusinnes.com/Invoices/generateInvoice

the system will automatically call NewbusinessInvoicesController->generateInvoice(), because such child controller and method exists. Otherwise it would call default controllers and methods.

So is possible to create such mode for CakePHP, maybe on low level of loading classes? I would not like to use runkit_method_redefine() and replace for example the App::import() method, as long as runkit_method_redefine() is marked as "experimental". Any suggestions or ideas?


Solution

  • So is possible to create such mode for CakePHP, maybe on low level of loading classes? I would not like to use runkit_method_redefine() and replace for example the App::import() method, as long as runkit_method_redefine() is marked as "experimental". Any suggestions or ideas?

    All requests initially go through app/webroot/index.php. This file is not part of the core and can be modified by yourself. What you can do is change the include paths to list a folder of your own creation before CAKE_CORE_INCLUDE_PATH. Here I set /absolute/path/to/custom as an include path:

    if (function_exists('ini_set') && ini_set('include_path', ROOT . DS . 'custom' . PATH_SEPARATOR . CAKE_CORE_INCLUDE_PATH . PATH_SEPARATOR . ROOT . DS . APP_DIR . DS . PATH_SEPARATOR . ini_get('include_path'))) {
    

    Following this section, CakePHP's core bootstrap file is loaded (/cake/bootstrap.php) and many core classes start getting loaded:

    require CORE_PATH . 'cake' . DS . 'basics.php';
    $TIME_START = getMicrotime();
    require CORE_PATH . 'cake' . DS . 'config' . DS . 'paths.php';
    require LIBS . 'object.php';
    require LIBS . 'inflector.php';
    require LIBS . 'configure.php';
    require LIBS . 'set.php';
    require LIBS . 'cache.php';
    Configure::getInstance();
    require CAKE . 'dispatcher.php';
    

    Having inserted your own include path, you can replace them by copying them to that path:

    ROOT
    |- app/
    |- cake/
      |- libs/
        |- configure.php // this would usually be loaded
    |- custom/
      |- cake/
        |- libs/
          |- configure.php // now this is loaded
    

    (Note: the configure.php file contains both the Configure and App classes.)

    In short, by changing a single line in app/webroot/index.php, you can now replace any part of CakePHP's core without editing it directly.

    You may now be able to make the required changes to App::import but, as this isn't actually documented anywhere, your mileage may vary.