Thursday, May 27, 2010

Readable PHP code #2 Make your API handle more!

Context

APIs are often designed to operate with scalars. As described in the following example, the function addContact() operates on a single element:

<?php
class User {
    protected 
$contacts = array();

    function 
addContact($contact) {
        
$this->contacts[] = $contact;
    }
}
?>

In a context where many contacts have to be added using the above API, code usually looks like:

<?php
// Some contacts
$contacts = array("Paul""John""Maria");

$user = new User();
// Looping over contacts to add them
foreach ($contacts as $contact) {
    
$user->addContact($contact);
}
?>

Inner looping

A way to avoid repeating this looping everywhere would be to design the API to work with array of contacts:

<?php
class User {
    protected 
$contacts = array();

    
function addContacts($contacts) {
        
foreach ($contacts as $contact) {
            
$this->contacts[] = $contact;
        }
    }

}
?>

This might make the code, where loops are in use, somewhat clearer:

<?php
// Some contacts
$contacts = array("Paul""John""Maria");

$user = new User();
$user->addContacts($contacts);
?>

With the benefit of speeding the processing a little bit as only one function call is issued!

The side effect is that adding only one contact is not as elegant:

<?php
$contact "Julia";

$user = new User();
$user->addContacts(array($contact));
?>

A nice PHP trick

The good news is that PHP provides a nice array cast operator: (array) which will transform a scalar value into an array. As described on the manual page about arrays:

"For any of the types: integer, float, string, boolean and resource, converting a value to an array results in an array with a single element with index zero and the value of the scalar which was converted. In other words, (array)$scalarValue is exactly the same as array($scalarValue)."

Previous example can be transformed to play nicely with both scalars and arrays:

<?php
class User {
    protected 
$contacts = array();

    function 
addContacts($contacts) {
        foreach (
(array) $contacts as $contact) {
            
$this->contacts[] = $contact;
        }
    }
}
?>

Function addContacts() can now be used the following way with scalars:

<?php
$contact 
"Julia";

$user = new User();
$user->addContacts($contact);
?>

OOPs!

This is a nice trick to define APIs to be used with both scalars and arrays, however this will not work when using objects! PHP is able to cast an objects as an array, but this will give you an access to its properties which is not the intended purpose.

If your contacts are objects you will have to modify the addContacts() function to something like:

<?php
class User {
    protected 
$contacts = array();

    function 
addContacts($contacts) {
        
if (is_array($contacts)) {
            foreach (
$contacts as $contact) {
                
$this->contacts[] = $contact;
            }
        } else {
            
$this->contacts[] = $contacts;
        }
    
}
}
?>

This may not be as elegant as the array casting method. However it will enable your API to work seemlessly with both scalars and arrays when objects are used.

The benefit of working with an array capable API is that you might sometimes optimize the operations. For example, in the case of retrieving or deleting elements from a database, you might want to use the "WHERE id IN (...)" syntax to match multiple elements rather than one by one. In the X examples that has been used in this article, an interesting optimization is to use the native array_merge() function which avoids reinventing the wheel by looping over elements using a foreach construct and adding elements one by one:

<?php
class User {
    protected 
$contacts = array();

    function 
addContacts($contacts) {
        if (
is_array($contacts)) {
            
$this->contacts array_merge($this->contacts$contacts);
        
} else {
            
$this->contacts[] = $contacts;
        }
    }
}
?>

Conclusion

In this article we have seen the advantages of creating an API which can handle multiple elements at once. If there is a benefit in terms of speed (which heavely depends on your business logic), don't forget that code readability is of higher importance too! Hopefully, this tip should improve both

For those who mind about performance, doing:

<?php
// Adding 5.000.000 contacts
$user->addContacts(range(15e6));
?>

is about 40% faster than:

<?php
foreach (range(15e6) as $contact) {
    
$user->addContact($contact);
}
?>

Thanks to Paul Dragoonis, Paul Borgermans and Jérôme Renard for reviewing this article

Wednesday, November 4, 2009

eZ Publish 4: Enterprise Web Sites Step-by-Step


Today I received my copy of "eZ Publish 4: Enterprise Web Sites Step-by-Step" by Francesco Fullone and Francesco Trucchia kindly offered by Packt Publishing for review.
I will post my review in the following weeks on this blog, for  the impatients, you way also find this interesting review of this book by Bruce Morrison.

Thursday, October 29, 2009

Coding standards: converts PHP4 style constructors to PHP5 one

A quick way to convert all occurences of old PHP4 constructors like in:

class XYZ {
    /**
     * Constructor of XYZ.
     */
    
function XYZ() {
    }
}


to PHP5's __construct():

class XYZ {
    /**
     * Constructor of XYZ.
     */
    
function __construct() {
    }
}


is done using a quick Perl Regular Expression like in the following Linux shell command:

$ perl -i -e 'undef $/;while($_=<>){s/^(class\s+(\w+)\b.*^\s+function\s+)\2\b/\1__construct/gms;print $_;}' $(find -name "*.php")

Once you have done converting your constructors definition you still may have to fix constructor calls like:

MyClass::MyClass();
parent::MyClass();
$this->MyClass();


First of all, you need to know what classes to search for, because you would be crazy to work without a software revision control tool (like Git, SubVersion, Mercurial,...), use the diff output to extract the changes you just made with previous command. Next command extract class names from the SubVersion diff output:

$ svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/"

The regular expression to convert all three types of constructor call is the following one:

s/((?:parent|\2)::|\$this->)(Class1|Class2|Class3|...)\b/parent::__construct/g

To embed this in the regular expression needed, we modify the output of the command with echo to join all the lines on one line and sed to replace this space separated list of classes with pipes (|):

$ echo 's/((?:parent|\2)::|\$this->)('$(echo $(svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/") | sed 's/ /|/g')')\b/parent::__construct/g'

(green: regular expression, blue: command extracting class names, orange: joining lines with pipes)

Last step is to use this regular expression with perl:

$ perl -pi -e 's/((?:parent|\2)::|\$this->)('$(echo $(svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/") | sed 's/ /|/g')')\b/parent::__construct/g' $(find -name "*.php")

For the one-liners out there, here is the full command you might execute (SubVersion based):

$ phpfiles=$(find -name "*.php") && perl -i -e 'undef $/;while($_=<>){s/^(class\s+(\w+)\b.*^\s+function\s+)\2\b/\1__construct/gms;print $_;}' $phpfiles && perl -pi -e 's/((?:parent|\2)::|\$this->)('$(echo $(svn diff | grep "^-[^-]" | sed -r "s/-\s*function\s*([a-zA-Z0-9_]*).*$/\1/") | sed 's/ /|/g')')\b/parent::__construct/g' $phpfiles

I leave as exercise the reader to port these Linux commands to Microsoft Windows' native command shell.

This article assumes your classes are always declared with the class keyword starting at the beginning of the line and that your files have the .php extension.
Modify the commands to match your standards.