Archive for the ‘PHP’ Category

Integrating Yahoo r3 with Phing

December 21st, 2009 21 comments

Phing Yahoo r3Yahoo r3 is a powerful tool for building websites which could require multiple languages or dimensions. The r3 outputs may be plain text files, web templates, configuration files or even source code. Sometimes maintaining a big site translated into different languages can become a nightmare, but r3 lets you do it on an easy way. It offers a neat and powerful command line interface which will make your life easier. It can also be managed by using a web interface.

I know this could be a somewhat confusing thing to explain, so I’m going to try to give an example of what you can do with it. At Weblogs SL, we have a platform which handles instances of 37 blogs in 2 languages (Spanish and Portuguese), so we have to maintain 37×2 = 74 sites. They almost share the same layout structure, but sometimes we need to make specific changes on certain site (maybe a customized header or sidebar). r3 lets you do all this kind of things by specializing templates for a particular site/language. Coming back to our example, we have two dimensions: blog and language. Once we have created the dimensions, we can generate all the templates. These raw templates are parseable by the template engine included within our platform, so they are processed by the application afterwards. We can also put all the “static variables” for each blog (such as blog titles, URLs, paths, …) during this pre-build process.

So after far too much research, we came up with Yahoo r3, but we still had an itch to scratch: how can we integrate this into our continuous integration workflow? We are using Phing (and Java Ant) for building our sites, so it was nice to have some Phing tasks to work with r3. So I wrote these two tasks:

r3GenerateTask.php

require_once 'phing/Task.php';
/**
 * r3 Generate Task
 *
 * PHP version 5
 *
 * @category  Phing Task
 * @package   phing
 * @author    Alfonso Jimenez
 * license   http://www.gnu.org/licenses/gpl.html GPL
 *
 */

class r3GenerateTask extends Task
{
    /**
     * path to r3 workspace
     *
     * var  string
     */

    protected $r3Workspace;

    /**
     * init method
     *
     * @return boolean
     */

    public function init()
    {
        return true;
    }

    /**
     * main method
     *
     * @return void
     */

    public function main()
    {
        $command = 'r3 -c '. $this->Workspace .' generate -av';
        $output  = array();
        $return  = null;

        try {
            exec($command, $output, $return);

            foreach ($output as $line) {
                $this->log($line, Project::MSG_VERBOSE);
            }

            if ($return !== 0) {
                throw new BuildException('Task exited with code'. $return);
            }
        } catch (BuildException $e) {

            $op = Project::MSG_WARN;

            if ($this->quiet === true) {
                $op = Project::MSG_VERBOSE;
            }

            $this->log($e->getMessage(), $op);
        }
    }
}

r3SetVarTask.php

require_once 'phing/Task.php';

/**
 * r3 setVar Task
 *
 * PHP version 5
 *
 * category Phing Task
 * package phing
 * author Alfonso Jimenez <yo@alfonsojimenez.com>
 * license http://www.gnu.org/licenses/gpl.html GPL
 *
 */

class r3SetVarTask extends Task {
    /**
     * path to r3 workspace
     *
     * var string
     */

    protected $r3Workspace;

    /**                                              
     * dimension                                      
     *                                                
     * @var string                                    
     */
                                             
    protected $dimension;                            

    /**                                              
     * variable key                                  
     *                                                
     * @var string                                    
     */
                                             
    protected $key;                                  

    /**                                              
     * variable value                                
     *                                                
     * @var string                                    
     */
                                             
    protected $value;                                

    /**                                              
     * sets the r3 workspace path                    
     *                                                
     * param string $r3Workspace                    
     *                                                
     * return void                                  
     */
                                             
    public function setr3Workspace($r3Workspace)      
    {                                                
        $this->r3Workspace = $r3Workspace;            
    }                                                

    /**                                              
     * sets dimension                                
     *                                                
     * param string $dimension                      
     *                                                
     * return void                                  
     */
                                             
    public function setDimension($dimension)          
    {                                                
        $this->dimension = $dimension;                
    }                                                

    /**                                              
     * sets the variable key                          
     *                                                
     * param string $key                            
     *                                                
     * return void                                  
     */
                                             
    public function setKey($key)                      
    {                                                
        $this->key = $key;                            
    }                                                

    /**                                              
     * sets the variable value                        
     *                                                
     * param string $value                          
     *                                                
     * return void                                  
     */
                                             
    public function setValue($value)                  
    {                                                
        $this->value = $value;                        
    }                                                

    /**
     * init method
     *            
     * @return boolean
     */
             
    public function init()
    {                    
        return true;      
    }                    

    /**                  
     * main method        
     *                    
     * @return void      
     */
                 
    public function main()
    {                    
        $command = ‘r3 -c ‘. $this>r3Workspace .var set ‘. $this>dimension
                   .’ ‘. $this>key .’ ‘. $this>value;                      
        $output  = array();                                                  
        $return  = null;                                                      

        try {                                                                
            exec($command, $output, $return);                                

            foreach ($output as $line) {                                      
                $this->log($line, Project::MSG_VERBOSE);
            }

            if ($return !== 0) {
                throw new BuildException(‘Task exited with code’. $return);
            }
        } catch (BuildException $e) {

            $op = Project::MSG_WARN;

            if ($this->quiet === true) {
                $op = Project::MSG_VERBOSE;
            }

            $this->log($e->getMessage(), $op);
        }
    }
}

If you want to use these tasks in your Phing file, you have to place them in /usr/share/php/phing/extended/tasks/ first. Then you can call them by writing the following tag-commands:

<taskdef name=”r3-generate” classname=”extended.tasks.r3GenerateTask” />
<taskdef name=”r3-setVar” classname=”extended.tasks.r3SetVarTask” />

<r3-generate r3Workspace=”${workspace.dir}”></r3>
<r3-setVar r3Workspace=”${workspace.dir}” dimension=”${dimension}” key=”var_key” value=”${var_value}”></r3>

Let’s see an example of a real Phing file. Imagine that we have to write a task which needs to do the following steps:

  1. Get a Yahoo r3 workspace from a SVN repository (http://svn.test.com/trunk/r3)
  2. Build the Yahoo r3 workspace
  3. Move the generated templates into a certain directory
  4. Clean up the temporary files

So we can straightforward write a Phing task in order to follow this sequence of steps:

<?xml version="1.0"?>
<project name="Yahoo r3 Templates Build" basedir="." default="build">

   <property name="tmp.dir" value="/tmp/template" />
   <property name="templates.dir" value="/home/r3/templates" />
   <property name="svn" value="http://svn.test.com/trunk/r3" />

   <target name="build" depends="init, generate, clean" />

   <target name="init">
      <taskdef name="r3-generate" classname="extended.tasks.r3GenerateTask" />

      <mkdir dir="${tmp.dir}" />
      <svncheckout svnpath="/usr/bin/svn" repositoryurl="${svn}" todir="${tmp.dir}"/>
   </target>

   <target name="generate">
      <r3-generate r3Workspace="${tmp.dir}"></r3-generate>
   </target>

   <target name="clean">
      <copy todir="${templates.dir}">
        <fileset dir="${tmp.dir}/htdocs">
           <include name="**" />
        </fileset>
      </copy>

      <delete dir="${tmp.dir}" includeemptydirs="true" failonerror="true" />
   </target>
</project>

phpredis, 2 Fast 2 Furious

June 2nd, 2009 5 comments

phpredisRedis (REmote DIctionary Server) is a persistent key-value database with built-in net interface written in ANSI-C for Posix systems. Whilst it may at first seem like the wheel is being reinvented here, the need for something beyond a simple key-value database is pretty clear. It’s possible to use Redis like a replacement of memcached, with the main difference being the dataset is stored persistently – not volatile data – and Redis introduces new data structures such as list and sets. Furthermore, it implements atomic operations in order to interoperate with these data structures.

I released a PHP extension called phpredis a couple of weeks ago, which works as a PHP client API for Redis. The project is hosted at Google Code at the moment, and you can get the code directly from the SVN repository: http://phpredis.googlecode.com/svn/trunk/.

Despite a vanilla PHP client library already exists, I felt the need to write it since a PHP extension normally performs better and I wanted to make the most of Redis potential.

Let’s see a snippet of how to make a simple operation to Redis using the PHP client:

    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $redis->set('key', 27);

    echo $redis->get('key'); // it should print 27

    $redis->incrby('key', 3);

    echo $redis->get('key'); // it should print 30

The code above was quite obvious, it stored a value associate to key and it increments its value by 3. Let’s see another snippet a bit more complex, for example using a list:

    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $redis->lPush('list', 'val');
    $redis->lPush('list', 'val2');
    $redis->lPush('list', 'val3', 1);

    echo $redis->lPop('list', 1); // it should print val3
    echo $redis->lPop('list'); // it should print val2
    echo $this->lPop('list'); //it should print val

Notice that depending on the last optional parameter (0 by default) it’s possible to append/extract an element to/from the tail or to/from the head of the list.

There is a list including all the available methods. At the moment, I’m working on the implementation of more new Redis commands and on the support for complex data structures such as arrays and PHP objects.

Apart from phpredis, there are more client API for languages as Perl, Python, Ruby and Erlang. You can find this and more at the Redis project homepage. Redis has been written by Salvatore Sanfilippo who I’m chuffed to bits with.

PHP.JS, PHP functions in Javascript

January 10th, 2009 4 comments

PHP.JSPHP.JS is a project with the purpose of porting standard PHP functions over to Javascript. The project was taken up by Kevin van Zonneveld, a dutch developer who had developed a small JS library of PHP functions for his job. Kevin shared the library on his blog and came into Open Source. Then many developers made contributions with new PHP functions written in JS and this is how the spark caught flame.

It offers added functionality on top of JS with functions like md5(), file_get_contents(), and utf8_encode().

Official site | PHP.JS

Disclaimer: I’ve contributed to the project with a couple of functions

PHP: GOTO considered harmful

January 10th, 2009 4 comments

Spaghetti

New syntax features have been included in the upcoming version of PHP. Now it’s possible to use new stuff like NOWDOC, the ternary short cut ?: or the controversial reserved word GOTO. This statement has been the target of many debates as it completely breaks the structural programming advantages.

In March 1968, the famous Edsger Dijkstra’s letter called Go To Statement Considered Harmful (PDF) was published. In this letter, Dijkstra criticized the excessive use of the GOTO statement in programming languages of the day.

According to what I have read, the PHP GOTO can only move us within the same execution unit and it’s not possible to jump into loops.

GOTO

Visualization of a Phing buildfile

October 30th, 2008 0 comments

Raphael Stolt published a method to get a visualization of a Phing buildfile a few days ago:

Out of the box the Phing -l option can be used to get a first overview of all available targets in a given buildfile but it doesn’t untangle the target dependencies and sometimes a picture is still worth a thousand words. Luckily the Ant community already provides several tools to accomplish the visualization of Ant buildfiles, reaching from solutions that apply a Xslt stylesheet upon a given buildfile.

He uses the ant2dot tool and the Graphiz library to show the XML from a buildfile into a graphic. The image represents the flow of the build.

Phing

Via | Raphael Stolt

Webgrind: A web frontend for Xdebug

October 17th, 2008 0 comments

Webgrind is a web frontend for the popular Xdebug. Written by Joakim Nygård and Jacob Oettinger, Webgrind parses the Xdebug output and shows it on a dropdown table.

It doesn’t make such a big job, but it’s nice to see the result on a clean interface. The last release is 0.81, and you can get it from Google Code.

Webgrind

Rasmus, Think again!

October 15th, 2008 0 comments

I would like to share with you my friend Arno’s slides. He expounded this presentation during the last PHP Conference in Barcelona. He gave a speech on the usage of frameworks and software components in order to build PHP applications.

The famous Rasmus Lerdorf’s dixit is shown up in these slides, via some amusing examples: Frameworks are performing poorly. Arno prooves this statement using a Hello World application, and yes, Rasmus is right and framework performances suck for Hello Word applications.

Multiple Constructors in PHP

July 29th, 2008 4 comments

Bob the builderAs you probably know, it’s possible to have multiple constructors in Java. They need to have the same name as the class, and they can only be distinguished by the number and type of arguments. In PHP5, you can only have one constructor. You can define it using the reserved word __construct. If the __construct function doesn’t exist, PHP5 will search for the old-style constructor function (by the name of the class). So if we cannot have multiple constructors, how could we create objects with different initial conditions? It’s not a big deal. There’s a pattern called Factory Method, which defines virtual constructors using static methods. Let’s see an example:

class Person
{
    private $name;
    private $email;

    public static function withName($name)
    {
        $person       = new Person();
        $person->name = $name;

        return $person;
    }

    public static function withEmail($email)
    {
        $person        = new Person();
        $person->email = $email;

        return $person;
    }

    public static function fullPerson($name, $email)
    {
        $person        = new Person();
        $person->name  = $name;
        $person->email = $email;

        return $person;
    }
}

We have a class called Person which contains 2 private attributes: name and email. It also has 3 static methods: withName, withEmail and fullPerson. These methods will behave like constructors.

So if we want to create a Person object just with the name value, we can do it using the following statement:

$person = Person::withName('Example');

7 tips to write intelligible code

July 27th, 2008 2 comments

PHPWriting intelligible code helps to increase the productivity of a developing team, even if you are an independent worker. Why is it so important to write intelligible code? How can it improve the productivity of my team? A messed up code could delay your partners understanding, or it could create a barrier for new developers. Even trying to understand your own code could be a real challenge as well. Here are 7 tips of how to make more intelligible PHP code (coding style tips):

  1. 75-85 characters per line: Each line must contains approximately 75-85 characters. If a line exceeds more than 85 characters, consider splitting the line into multiple lines. It will be helpful for code readability. You can see a great post on this topic in Paul Jones’ blog. He talks about why this limitation is not really arbitrary, and he puts some brilliant examples such as this one:

    Bad

    list($foo, $bar, $baz) = array(Zim::getVal('foo'), Dib::getVal('bar'), Gir::getVal('baz', Gir::DOOM));

    Good

    $foo = Zim::getVal('foo');
    $bar = Dib::getVal('bar');
    $baz = Gir::getVal('baz', Gir::DOOM);
  2. Assignment statements: Assignment statements ought be aligned for better readability. Let’s see an example:

    Bad

    $example = 'string_value';
    $anotherExample = 42;
    $exampleInst = new ExampleClass();

    Good

    $example        = 'string_value';
    $anotherExample = 42;
    $exampleInst    = new ExampleClass();
  3. 4-spaces block indentation: Use an indent of 4 spaces with no tabs. It helps to avoid problems with diffs, patches and SVN history. It helps the readability as well. Usually tabs are 8 spaces length, which means a 5th-block indent starts 40 spaces from the left border (nearly to 50% of a code line).
    {block1}
        {block2}
            {block3}
                ...
  4. Control & conditional structures: These statements must have one space between the control keyword (if, else, for, while, …) and the opening parenthesis, to distinguish them from function calls (which obviously don’t have a space). It’s nice to always use curly braces because they help to decreases the likelihood of logic errors.

    if ($display === 1) {
        while ($obj->next()) {
            echo $obj->toString();
        }
    }
  5. Class and function/methods declarations: Class and function/methods declarations have their opening brace on a new line.

    class Foo
    {
        private $_obj;

        public function __construct($obj)
        {
            $this->_obj = $obj;
        }
    }
  6. Comma-space between items of a list: When we list some items, it’s nice to separate them clearly with a comma followed by a space.

    BAD

    $example = array(Foo::get('key',2),$value,true);

    GOOD

    $example = array(Foo::get('key', 2), $value, true);
  7. Use constants properly: If we have a method which returns some status codes, it’s nice to use literal constants. Having descriptive constants helps to make better intelligible code. For example:

    BAD

    class Foo
    {
        public static function getStatus()
        {
            $res = 0;

            if (!self::isValid()) {
                $res = 1;
            }
            ...

            if ($res === 0 && self::isRepeated()) {
                $res = 2;
            }

            return $res;
        }
    }

    GOOD

    class Foo
    {
        const STATUS_SUCCESS  = 0;
        const STATUS_FAILURE  = 1;
        const STATUS_REPEATED = 2;

        public static function getStatus()
        {
            $res = self::STATUS_SUCCESS;

            if (!self::isValid()) {
                $res = self::STATUS_FAILURE;
            }

            ...

            if ($res === self::STATUS_SUCCESS && self::isRepeated()) {
                $res = self::STATUS_REPEATED;
            }

            return $res;
        }
    }

These tips are just some coding style tips. If we really want to write intelligible code, we would keep in mind many more things like to use software patterns, to make use of well established software practises, …

Xinc. Continuous Integration

March 22nd, 2008 1 comment

Continuous integration is a software engineering practice, which helps to integrate changes frequently in a project. It speeds up the delivery of software by decreasing integration times. It’s very important when the project is being developed by more than one person. CruiseControl is a free CI server and probably the most used. Xinc (Xinc Is Not Cruisecontrol) is an alternative free CI server made specially for PHP. The Xinc development is led by Arno Schneider, a german developer currently living in Barcelona. Arno made a wonderful presentation at the Barcelona PHP Conference celebrated last month:

We can install Xinc easily with PEAR.

sudo pear channel-discover pear.xinc.eu
sudo pear install --alldeps xinc/Xinc
sudo pear run-scripts xinc/Xinc

When we run the scripts, we can configure some parameters like:

 1. Directory to keep the Xinc config files: /etc/xinc
 2. Directory to keep the Xinc Projects and Status information: /var/xinc
 3. Directory to keep the Xinc log files: /var/log
 4. Directory to install the Xinc start/stop daemon: /etc/init.d
 5. Do you want to install the SimpleProject example: yes
 6. Directory to install the Xinc web-application: /var/www/xinc
 7. IP of Xinc web-application: 127.0.0.1
 8. Port of Xinc web-application: 8080

Once the installation is complete, we need to include /etc/xinc/www.conf in our apache virtual hosts. We also have to have mod-rewrite enabled.