RSS
 
10. Nov. 2010

Loading Classes on the fly

10 Nov

With the LazyLoading Model from Lorenzo (there are other sources as well) we already speed up the application by 20-40% (depending on the application size and amount of models).

Something i never really liked is that components and helpers can only be added to controllers globally in $components and $helpers. Unlike behaviors (attach/detach). What if we need some of them only in a single action? Seems to be overhead to include it in every action of the controller. I used to patch around that problem with App::import() and all kinds of init stuff in the action itself.

Now I wrote this (and suddenly my code is so much cleaner):

class CommonComponent extends Object {
 
    /**
     * for automatic startup
     * for this helper the controller has to be passed as reference
     * 2009-12-19 ms
     */
    function initialize($Controller) {
        $this->Controller = $Controller;
    }
 
    /**
     * add helper just in time (inside actions - only when needed)
     * aware of plugins
     * @param mixed $helpers (single string or multiple array)
     * 2010-10-06 ms
     */
    function loadHelper($helpers = array()) {
        $this->Controller->helpers = array_merge($this->Controller->helpers, (array)$helpers);
    }
 
    /**
     * add lib just in time (inside actions - only when needed)
     * aware of plugins and config array (if passed)
     * ONLY works if constructor consists only of one param!
     * @param mixed $libs (single string or multiple array)
     * 2010-11-10 ms
     */
    function loadLib($libs = array()) {
        foreach ((array)$libs as $lib => $config) {
            if (is_int($lib)) {
                $lib = $config;
                $config = null;
            }
 
            list($plugin, $libName) = pluginSplit($lib);
            if (isset($this->Controller->{$libName})) {
                continue;
            }
            App::import('Lib', $lib);
            $lib = new $libName($config);
            $this->Controller->{$libName} = $lib;
        }
    }
 
    /**
     * add component just in time (inside actions - only when needed)
     * aware of plugins and config array (if passed)
     * @param mixed $helpers (single string or multiple array)
     * 2010-11-08 ms
     */
    function loadComponent($components = array()) {
 
        foreach ((array)$components as $component => $config) {
            if (is_int($component)) {
                $component = $config;
                $config = null;
            }
            list($plugin, $componentName) = pluginSplit($component);
            if (isset($this->Controller->{$componentName})) {
                continue;
            }
            App::import('Component', $component);
 
            $componentFullName = $componentName.'Component';
            $component = new $componentFullName($config);
 
            if (method_exists($component, 'initialize')) {
                $component->initialize($this->Controller);
            }
            if (method_exists($component, 'startup')) {
                $component->startup($this->Controller);
            }
            $this->Controller->{$componentName} = $component;
        }
    }
 
}

Now the following is possible (inside controller actions):

$this->Common->loadComponent('RequestHandler');
$this->Common->loadComponent('Tools.Currency');
$this->Common->loadComponent(array('RequestHandler', 'Test'=>array('x'=>'y'))); # passing options

$this->Common->loadLib('Tools.RandomLib');
$this->Common->loadLib(array('Tools.RandomLib', 'MarkupLib'));
$this->Common->loadLib(array('Tools.RandomLib', 'MarkupLib'=>array('x'=>'y'))); # passing options

$this->Common->loadHelper('Text');
$this->Common->loadHelper(array('Text', 'Tools.Datetime'));

etc.

Maybe without “Common->” or any other helping component some time in the future :)

I hope the enhancement ticket will soon be patched into the core. Feel free to vote it up: cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/1277

Loading Classes on the fly
0 votes, 0.00 avg. rating (0% score)
 
4 Comments

Posted by Mark in CakePHP

 

Tags:

Leave a Reply

Tip:
If you need to post a piece of code use {code type=php}...{/code}.
Allowed types are "php", "mysql", "html", "js", "css".

Please do not escape your post (leave all ", <, > and & as they are!). If you have encoded characters and need to reverse ("decode") it, you can do that here!
 

 
  1. Costa

    August 27, 2011 at 07:50

    Works well but I seem to have found a glitch. By bypassing the usual Component-&gt;init(), it seems that sub-components are not loaded.

    So if in my Test component I have:

    var $components = array('Session");

    the Session component won't be loaded for Test.

    I narrowed this down to
    Controller-&gt;contructClasses();
    and
    Component-&gt;init()

    They seem to be very intertwined so it's complex to run -&gt;init() by itself from within another component.

    Any ideas?

     
  2. Costa

    August 27, 2011 at 07:53

    Seems like ",  and &  show as is only within code blocks?

    .

     
  3. Costa

    August 27, 2011 at 08:32

    Well, after some testing, I seem to have figured it out:

    function loadComponent($components = array()) {
     
    	    foreach ((array)$components as $component => $config) {
    			if (is_int($component)) {
    	            $component = $config;
    	            $config = null;
    	        }
    	        list($plugin, $componentName) = pluginSplit($component);
    	        if (isset($this->Controller->{$componentName})) {
    	            continue;
    	        }
    	        App::import('Component', $component);
     
    	        $componentFullName = $componentName.'Component';
    	        $component = new $componentFullName();
     
    			$ComponentObj =& new Component();
    			$ComponentObj->__controllerVars = array(
    				'plugin' => $this->Controller->plugin,
    				'name' => $this->Controller->name,
    				'base' => $this->Controller->base
    			);
    			$ComponentObj->_loadComponents($component);
     
     
    	        if (method_exists($component, 'initialize')) {
    	            $component->initialize($this->Controller,$config);
    	        }
    	        if (method_exists($component, 'startup')) {
    	            $component->startup($this->Controller);
    	        }
    	        $this->Controller->{$componentName} = $component;
    	    }
    	}

    I also moved the $config to the component-&gt;initialize() call instead of the component constructor call.

     
  4. Mark

    August 27, 2011 at 09:21

    nice work. i will try it out.
    with cake2.0 most of that stuff is available in the core anyway.
    but for 1.3 we still need those hacks :)