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.

2 comments:

Anonymous said...

Cool Perl Oneliner ;)

Giorgio said...

Since you're already modifying source files, you can also add 'public' :)