« Return to the blog

Tom - Director

Posted by Tom on 30th March 2012

PHP: How to safely call a dynamic function

  1. How to call a built-in function in PHP (part one)
  2. How to write and call your own function in PHP (part two)
  3. How to safely call a dynamic function in PHP (part three)

Introduction

PHP has a library of built-in functions. Different functions are available depending on the PHP version you are running, and the extensions you enable. You can extend this library at run-time by writing your own functions.

This article demonstrates how to call a built-in function, how to write and call your own function, and how to safely call a dynamic function.

How to safely call a dynamic function in PHP

With PHP, it's also possible to call a functions in a slightly different way. This technique will mostly become useful when you want to start writing very generic code. For example, we use this technique for form processing on all of our websites.

How does it work?

By using a combination of the is_callable() and call_user_func() functions, you can safely call any function. Below is our generic "Functions" object. It allows us to safely call functions dynamically.

<?php

class Functions
{
    private static function 
assertSafe($fn)
    {
        
// Calling an undefined function is a fatal error whereas we
        // prefer exceptions, so we need to see if the function exists.
        // We use is_callable rather than function_exists because the
        // function could be in an object which needs to be autoloaded.
        
if(false == is_callable($fn))
            throw(new 
Exception("The function '{$fn}' does not exist."));
    }
    
    
// Safely call an arbitrary unary function, returning its result.
    // If the function does not exist, an exception is thrown.
    
public static function unary($fn,$data)
    {
        
self::assertSafe($fn);
        return 
call_user_func($fn,$data);
    }
}

// An example of how the function might be used.
$result Functions::unary("SomeClass::doSomething","A text string");

?>

The example shows the function doSomething of class SomeClass is being statically called, and passed "A text string" as the data.

A more practical example

As I mentioned previously, we use this technique for handling the submission of all of our forms. This is where stuff starts to get a bit more advanced, but stick with it because the end result is cool.

Assume the following:

  1. You have two simple forms:
    1. A simple login form with email address and password fields e.g. our login page.
    2. A simple password request form with an email address field e.g. our password request page.
  2. You use three web pages per process: one for showing the form, one for processing the request, and one for displaying a success page.
  3. Both forms are simple form objects, extending a more complex, generic form object.
    1. The simple form objects contain basic information about the form. E.g. The labels to use next to the form fields, and the name of the function to call when the form is being processed.
    2. The parent, generic object contains functions that handle the simple forms being submitted, error handling, etc.

Using this model, I know that when the above two forms are submitted, for 90% of the process, they are being handled exactly the same way. The other 10% is whatever is specific to the form, e.g. logging the user into their account, or emailing the user their password.

If you want to see this in action, try it out for yourself:

  1. Open our login page and retrieve password page.
  2. Don't fill in any details, just hit the login or retrieve password button.
  3. You'll see that the our system gives you a generic error, then highlights specific fields that are in an error state.

By using the generic model above, both forms are handled by more or less the same code. This means that we can ensure that all of our forms are rendered in a similar way, errors look consistent, and importantly that input data is cleansed in just one place in the system before being processed by each form's own function.

The Code

The following code is our retrieve password form object. The login form is basically the same, with exception of the fields, and the values passed to parent::__construct().

<?php

class RetrievePasswordForm extends HtmlForm
{
    public function 
__construct($show_errors,$render_mode)
    {
        
parent::__construct
        
(
            
"retrieve_password_form",
            
HtmlForm::POST,
            
Path::root(true)."retrieve-password-process.php",
            
"CurrentUser::retrievePassword",
            
$show_errors,
            
$render_mode
        
);
    }
    
    public function 
getFieldsForStoring()
    {
        return array(
"email_address");
    }
    
    public function 
getFormStructure()
    {
        
$structure = new FormStructure();
        
$structure->addText("email_address","Email Address","");
        
$structure->addSubmit("retrieve_password_button","Retrieve Password");
        return 
$structure;
    }
}

?>

Notes on the above:

  1. HtmlForm::POST is a constant to indicate what type method the form should use.
  2. Path::root(true) is a generic function that will return the root URL of the website. The "true" indicates that it should be a secure URL if it is available. This is how we can use the exact same form/code for several websites at the same time.
  3. FormStructure is an object used to store different types of form field, and their state.

And a cut-down version of the base class:

<?php

abstract class HtmlForm
{
    public function 
__construct($name,$method,$action,$function,$show_errors_and_auto,$render_mode)
    {
        
// Stores the values passed through.
    
}
    
    
// Force child objects to implement these functions.
    
abstract public function getFieldsForStoring();
    abstract public function 
getFormStructure();
    
    public function 
getCleanseDataIgnoreFields()
    {
        return 
NULL// By default, nothing is ignored.
    
}
    
    public function 
render()
    {
        
// Gets the form structure from the form and renders it nicely.
    
}
    
    
// When the form is submitted to the process page, this function is run.
    // If an exception is thrown, stuff is stored in the session, and the
    // process page redirects back to the initial page with the form on it.
    
public function submit($data)
    {
        
$data self::cleanseData($data,$this->getCleanseDataIgnoreFields());
        
        try
        {
            
// $this->getFunction() gets the function name as a string,
            // e.g. "CurrentUser::retrievePassword".
            
return Functions::unary($this->getFunction(),$data);
        }
        catch(
FieldException $e)
        {
            
// Our system will throw a FieldException if some data is missing.
            // E.g. If you submit the retrieve password form without entering
            // an email address.
        
}
        catch(
DatabaseException $e)
        {
            
// Our system will throw a DatabaseException if there is some
            // sort of problem with the database.
        
}
        catch(
Exception $e)
        {
            
// Our system may throw any generic Exception. Depends entirely
            // on what the form was meant to do. For example, if the function
            // "CurrentUser::retrievePassword" didn't exist, the
            // Functions::assertSafe function above would throw an exception:
            // The function 'CurrentUser::retrievePassword' does not exist.
        
}
    }
    
    private static function 
cleanseData($data,$ignore NULL)
    {
        
// Cleanses the input data to make sure it is secure.
        // Ignores any fields that shouldn't be cleansed for some reason.
    
}
}

?>

When the form is submitted, the HtmlForm::submit function runs. The data from the form is passed through as an array, with the form field as the key, and what the user entered as the value.

A few lines down and you can see Functions::unary. This is where the magic happens. The retrieve password or login forms have told HtmlForm what function to call. HtmlForm calls the function and passes the data from the form. The function can then do whatever it needs to do.

If no errors occur, the user goes to the success destination (which could either be a simple success page, or their account screen if they have just logged in). If an error occurs, the base HtmlForm class handles those for us. Neither the individual form objects, or the function that has been called need to do anything. The user is returned to the original form screen and the errors are displayed.

Cool, huh?

What does this mean?

  1. You're processing data in one location: Imagine if each form implemented the basic processing itself. You have hundreds of forms. Then you discover a weird security issue that needs to be addressed. Oh dear - you have hundreds of forms to update. This model means you do it one place.
  2. Making new forms is faster: You can create new forms that are handled safely very quickly. Yay.
  3. By making the forms generic, you can re-use them (which is not the same as copy & paste). See our login page, Moody's login page, or Annie Hill's login page. The same, right? Right. The exact same code is run. Boring? These sites don't need fancy login screens. Take a look at Extrascents. The exact same thing, just ever so slightly nicer.
  4. We've come a long way from the sausage calculator.

That concludes this three part article, thanks for reading. If you like this article, please show the love by leaving a comment, liking our Facebook page, or following @switchplane on Twitter. You can also follow me personally on Twitter @tom_fielder.

Add A Comment

:
:
:
: Captcha

Recent Blogs

Tom-Director

PHP: How to redirect to another page

Short tutorial explaining how to redirect a user to another page with PHP. Read More »

Tom-Director

PHP: How to call a function - part two

Part two of three articles explaining how to call functions in PHP. This part focuses on writing and calling your functions. Read More »

Tom-Director

PHP: How to call a function - part one

Part one of three articles explaining how to call functions in PHP. This part focuses on calling the built-in functions available in PHP. Read More »

Michael-Developer

Twitter Tools

Do you use Twitter as part of your business marketing strategy. Here are some useful tips and tools to help you succeed. Read More »

Evie-Designer

Gradient Meshes in Adobe Illustrator

There's more to Adobe Illustrator than basic shapes and blocks of colour, and there's more to gradients than linear or radial effects. Read More »

Evie-Designer

Web Design Trends 2011

Web designers are shying away from creating gimmicky tricks, but rather clean, accessible, bug-free coding - that works. Read More »

Chris-Project Manager

Let's Do Business - Eastbourne 2011

Find out what Switchplane did at Let's Do Business Eastbourne 2011. Read More »

Joel-Director

Integrating with Kashflow using PHP

Find out how to use PHP's SoapClient class to integrate with Kashflow. Includes sample code and best practice tips. Read More »

Tom-Director

Improving Search with Levenshtein Distance

Find out how we've made Honey Barrett's document management system search facility handle spelling mistakes. Read More »

Joel-Director

The New Switchplane Website

Our new website is ready - read about what we've updated and why, and get ready for Project Awesome! Read More »

Michael-Developer

Website Accessibility

Have you ever considered the importance of accessibility on your website? Read More »

Chris-Project Manager

Networking Tips

Business networking tips and trips featuring the adventures of Norm! Read More »

Michael-Developer

How Amazing Databases Are

A blog post about the usefulness of databases, using a person management system as an example. Read More »

Christian-Designer

Reasons to use Vector Graphics over Rasters

When should you use vectors or rasters, and does it even matter? Read More »

Michael-Developer

The Importance of Variables

Variables are useful - make sure you use them properly! Read More »

Chris-Project Manager

Some thoughts on writing for your business website...

Read some tips on preparing copy for your website. Read More »

Chris-Project Manager

Let's Do Business - Hastings 2010

Switchplane attended LDB Hastings in 2010 - read about our day. Read More »

Chris-Project Manager

Lets Do Business Eastbourne 2010

Switchplane's experience at Let's Do Business 2010 in Eastbourne. Read More »

Evie-Designer

Web Design Trends 2010 Part 1

A guide to web design trends 2010, including hand-drawn and painted layouts, typefaces, modern vectors and large headers and footers. Read More »

Evie-Designer

Common Printing Problems and how to avoid them

Even if it looks good on your screen, it isn't guaranteed to come out like that! I've selected a few of the most common print problems to watch out for. Read More »

Evie-Designer

Web design trends 2009 Part 2: Old and Torn Paper

Make your website a real piece of personal artwork by collecting and scanning-in torn and crumpled paper and using it for the design of your website (or just take a visit to istock photo!). Read More »

Evie-Designer

Web design trends 2009 Part 1: Badges

Subtle elements and new layout ideas for 2009. My guide on how to make your website look fashionable and unique, packed full of information and further resources. Read More »

Tom-Director

The Joys of JQuery

JQuery is a light-weight, cross-browser and feature packed JavaScript library. To explain why JQuery is so good, first you need to know why not using it is so bad! Read More »

Vanessa-Account Manager

Lets Do Business Eastbourne 2009

Last Thursday (25th June) Switchplane attended their first Lets Do Business event at the Winter Gardens in Eastbourne. Read More »

Vanessa-Account Manager

Top 10 Sales Tips

Times are tough. Sales are down. Morale is low. Sound familiar? Well it needn't be. Below are a few tips which are sure to help you re evaluate and succeed in pushing your sales margins. Read More »

Evie-Designer

Create printable artwork in Indesign and Photoshop

A few useful steps to help you successfully prepare your artwork for print, using Adobe Photoshop and Adobe Indesign. Learn about colour modes, resolution, size and other helpful tips! Read More »

Cron-Administrator

Search engine ranking more important than ever

Search engine optimisation is now an essential part of a successful website. And because of the way search engines now work, the focus of SEO is now about producing quality content and getting your name out there so you get incoming links. Read More »

Cron-Administrator

Four Months at Switchplane

I've been at Switchplane for four months now - to celebrate the launch of our new website I'm sharing some thoughts on what it's like working here. Read More »

Joel-Director

How to run a cron job on the first weekday of the month

Often it's useful to generate automatic reports or perform some other task at the start of the month. Find out how to schedule a cron job to run on the first weekday of each month. Read More »