Lazy PHP: Part 3

Share this article

Following on from the last blog on Laziness in PHP,
some more Lazy Initialization, this time applied to class loading.

In most programming languages, objects are “expensive” in terms of performance. In PHP, the “cost of objects” is felt even more, thanks to PHP ditching it’s memory between page requests (see Part 1) among other things.

To demonstrate the point, here’s a simple form controlling class (based loosely on the form controllers in WACT);


class FormController {
    var $initial_action;
    var $invalid_action;
    var $valid_action;
    var $validator;
    /**
    * This action will be performed if the form has not yet
    * been posted
    */
    function registerInitialAction(& $action) {
        $this->initial_action = & $action;
    }
    /**
    * This action is performed if the form IS posted by
    * invalid data was submitted
    */
    function registerInvalidAction(& $action) {
        $this->invalid_action = & $action;
    }
    /**
    * This action is performed if the form IS posted and
    * the submitted data was validated
    */
    function registerValidAction(& $action) {
        $this->valid_action = & $action;
    }
    /**
    * Registers the object for validation
    */
    function registerValidator(& $validator) {
        $this->validator = & $validator;
    }
    /**
    * Run the form controller
    */
    function run() {
        # Has the form been posted?
        if ( !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') ) {
        
            # No - perform the initial action
            $this->initial_action->perform();
            
        } else {
        
            # Yes - need to validate
            if ( ! $this->validator->isValid() ) {
            
                # Invalid data - perform the invalid action
                $this->invalid_action->perform();
                
            } else {
            
                # All OK - perform the valid action
                $this->valid_action->perform();
            }
        }
    }
}

To use this controller, I define my own “action” classes which contain the method perform e.g.;


class UserUpdateInitial {
    function perform() {
        // display the HTML form
    }
}

and


class UserUpdateValid {
    function perform() {
        // pull data from $_POST and update database
        // display a some HTML saying "success"
    }
}

Then I can use the form controller like;


< ?php
// Load all the necessary classes
require_once 'lib/controller/formcontroller.php';
require_once 'lib/action/userupdateinitial.php';
require_once 'lib/action/userupdateinvalid.php';
require_once 'lib/action/userupdatevalid.php';
require_once 'lib/validator/validator.php';

// Create the FormController
$F = & new FormController();

// Register the actions
$F->registerInitialAction(new UserUpdateInitial());
$F->registerInvalidAction(new UserUpdateInvalid());
$F->registerInvalidAction(new UserUpdateValid());

// Validation
$V = & new Validator();
$V->addRule(new SizeRule('username',1,15));
$V->addRule(new MatchRule('password','confirmpassword'));
$F->registerValidator($V);

// Run the form controller
$F->run();
?>

If this is going over your head, take a moment to examine the FormController::run() method above and you should see how this fits together.

The Validator class and the “Rules” I’ll leave as imaginary code to keep this focused.

So far so good. The form control and validation classes are now code I can re-use for any (simple) form, allowing me to focus on developing the “actions” which are specific to this form.

But when it comes to efficiency, there’s a problem. If you look at the FormController::run() you’ll notice a few things;

  • The Validator class is only being used if the form is actually submitted. In the form’s initial state (displaying the empty form), I don’t need the validator (there’s nothing to valid yet).
  • – Only one action is ever performed for a single page request. But for each page request, I’m creating three “action objects”, two of which aren’t used.
  • Perhaps for this simple example I can get away with it but for a complex form with multiple pages and lots of fields to validate, combined with all sorts of other “infrastructure” classes (db access etc.), it quickly get’s too expensive.
  • So can I improve things? For the answer, enter Jeff who brought up the subject a while ago in Object Composition by Proxy. The result of that discussion can be found in WACT in the form of the ResolveHandle function (found in WACT: framework/util/handle.inc.php);


< ?php
//--------------------------------------------------------------------------------
// Copyright 2003 Procata, Inc.
// Released under the LGPL license (http://www.gnu.org/copyleft/lesser.html)
//--------------------------------------------------------------------------------
/**
* @package WACT_UTIL
* @version $Id: handle.inc.php,v 1.1 2004/03/10 01:53:49 jeffmoore Exp $
*/
/**
* Takes a "handle" to an object and modifies it to convert it to an instance
* of the class. Allows for "lazy loading" of objects on demand.
* @see http://wact.sourceforge.net/index.php/ResolveHandle
* @param mixed
* @return void
* @access public
* @package WACT
*/
function ResolveHandle(&$Handle) {
    if (!is_object($Handle) && !is_null($Handle)) {
        if (is_array($Handle)) {
            $Class = array_shift($Handle);
            $ConstructionArgs = $Handle;
        } else {
            $ConstructionArgs = array();
            $Class = $Handle;
        }
        if (is_integer($Pos = strpos($Class, '|'))) {
            $File = substr($Class, 0, $Pos);
            $Class = substr($Class, $Pos + 1);
            require_once $File;
        }
        switch (count($ConstructionArgs)) {
        case 0:
            $Handle = new $Class();  // =& doesn't work here.  Why?
            break;
        case 1:
            $Handle = new $Class(array_shift($ConstructionArgs));
            break;
        case 2:
            $Handle = new $Class(
                array_shift($ConstructionArgs), 
                array_shift($ConstructionArgs));
            break;
        case 3:
            $Handle = new $Class(
                array_shift($ConstructionArgs), 
                array_shift($ConstructionArgs), 
                array_shift($ConstructionArgs));
            break;
        default:
            // Too many arguments for this cobbled together implemenentation.  :(
            die();
        }
    }
}
?>

What this mysterious looking function does is provide an additional “protocol” for referencing objects in addition to using normal PHP variable assignment. Put another way, rather than this;


require_once 'lib/action/userupdateinitial.php';
//...
$F->registerInitialAction(new UserUpdateInitial());

I can now do just this;


$F->registerInitialAction('lib/action/userupdateinitial.php|UserUpdateInitial');

Instead of registering the object itself, what I’ve registered is just a string containing a filename and a classname which can be “resolved” later, when this object is needed.

I need to update the FormController::run() method to take advantage of ResolveHandle();

(UPDATE): Change to the script below… original usage of ResolveHandle was incorrect


    /**
    * Run the form controller
    */
    function run() {
        if ( !strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') ) {
        
            // Resolve the InitialAction
            ResolveHandle($this->initial_action);
            $this->initial_action->peform();
            
        } else {
            // Resolve the Validator
            ResolveHandle($this->validator);
            
            if ( ! $this->validator->isValid() ) {
            
                // Resolve the InvalidAction
                ResolveHandle($this->invalid_action);
                $this->invalid_action->perform();
                
            } else {
            
                // Resolve the ValidAction
                ResolveHandle($this->valid_action);
                $this->valid_action->perform();
            }
        }
    }

Using the ResolveHandle() function, the objects are only created as they are needed, cutting the performance overhead down to only those objects required to handle the current request.

Note I could have simply “registered” class and file names for this particular example but Jeff’s ResolveHandle() is more flexible in that it supports a number of different types of “reference” (including a normal reference to a real object) as well as passing objects to the constructor. Further examples can be found at http://wact.sourceforge.net/index.php/ResolveHandle.

Although I focused on form processing in this example, the general principle could be extended to other problem domains in PHP applications, from Object Relational Mapping to building a Domain Model. More on those some other time.

Harry FuecksHarry Fuecks
View Author

Harry Fuecks is the Engineering Project Lead at Tamedia and formerly the Head of Engineering at Squirro. He is a data-driven facilitator, leader, coach and specializes in line management, hiring software engineers, analytics, mobile, and marketing. Harry also enjoys writing and you can read his articles on SitePoint and Medium.

Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week