Developing a Robust Build Process Using Phing

What is Phing?

Phing is a PHP build process that uses XML , ANT style syntax.

At some point in development, the choice is made to begin professional work. If more than 1 developer contributes to the effort, then an the easy mode of keeping in sync with that person and, in the future, others that may join the effort is required. Because not all developers are alike, and not all hosting providers are alike, you must have a solid build process of your code. The build process automates the routines that every developer must do, either daily, hourly, or with every code change as well as when code is pushed into client facing distributions. Phing is an XML to PHP build process that imitates the ANT build process that many development shops are used to. As a PHP developer, Phing is much easier for me to work with and get things done, as well as keeps things simple when needing to update the build process.

Installing Phing

Dependencies :

Straight from the source, “To use Phing you must have installed PHP version 5.2 or above compiled –with-libxml2, as well as –with-xsl if you want to make use of advanced functionality.” I would hope that if you are doing any serious PHP development, you would already meet these requirements. If you are needing to upgrade your PHP, then you really should reconsider what you are using PHP for.

My approach to installing Phing is the simplest solution and it follows exactly what the setup guide describes by using Pear.

1
2
$ pear channel-discover pear.phing.info</span>
$ pear install phing/phing

You should now be able to test Phing by simply typing ‘phing’ from the command line. You should get an error similar to this: ‘Buildfile: build.xml does not exist!’

Copying and Moving Files

One of the most important parts of a build process is the ability to move and copy files to different places. Phing makes this easy and allows you to modify the files as you are moving them. Different reasons to have a build process will determine how you would like to set up your folder paths for Phing to manipulate. Let’s do one that is pretty powerful and pretty simple at the same time: copying all files from a folder, into a single file, located in an entirely different folder.

build.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyWebapp" default="default" basedir=".">
<target name="default">
<foreach param="dirname" absparam="absname" target="compile-folder-js">
<fileset dir="./js">
<type type="dir" />
<depth max="0" min="0" />
</fileset>
</foreach>
</target>
<target name="compile-folder-js">
<mkdir dir="./${dirname}-js" />
<fileset id="filestocompile" dir="js/${dirname}">
<include name="**/*.js" />
</fileset>
<append destFile="./${dirname}-js/${dirname}.js">
<fileset refid="filestocompile" />
</append>
</target>

Phing works using a Build.xml file to run the commands when Phing is called. The only requirements of this file are the project tag and a default target. We have set our default target to run code that dives into the ./js folder path; loops through every folder in this path and passes them to another target (compile-folder-js) for processing; one at a time. Now we can have in our source ./js/jquery/ and ./js/modernizr/ and this will take all JavaScript files send them to the compile-folder-js target.

The compile-folder-js target will loop through the directory that is passed to it and grab all JavaScript files (recursive folder depth because of the **/ ) and append them to a single file under the path ./directory-js/directory.js. This is pretty great when it comes to a production versus a development site. For instance, in a development site, you may have 5 different JavaScript files, but in a Production site, after a build, you could have a single JavaScript file. However, one point to be aware of, the files will be appended in teh order the file system sends them (defaults as alphabetical order).

Sanitizing Files

Now that we are concatenating our JS files, we can take it further and do some sanitizing with JSLint. Adding this to our current project is fairly simple. Start by downloading the most recent version of JSLint for your operating system. Make sure to uncompress the file and remember where you put the uncompressed file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyWebapp" default="default" basedir=".">
<target name="default">

<property name="jslint" value="./path/to/jslint/jsl-0.3.0-mac/jsl" />
<echo msg="Checking JS for errors..." />
<jsllint haltonfailure="true" cachefile="./jslint.cache" executable="${jslint}" conffile="./path/to/jslint/jsl.default.conf">
<fileset dir="./js">
<include name="**/*.js"/>
</fileset>
</jsllint>

<foreach param="dirname" absparam="absname" target="compile-folder-js">
<fileset dir="./js">
<type type="dir" />
<depth max="0" min="0" />
</fileset>
</foreach>
</target>
<target name="compile-folder-js">
<mkdir dir="./${dirname}-js" />
<fileset id="filestocompile" dir="js/${dirname}">
<include name="**/*.js" />
</fileset>
<append destFile="./${dirname}-js/${dirname}.js">
<fileset refid="filestocompile" />
</append>
</target>

We’ve added a JSLint check that runs through all of the JavaScript files and send them through the JSLint downloaded binary for checking. The process will now stop if you right improper JavaScript, immediately letting you know if you have issues with your code.

Compressing Files

Sometimes, checking your JavaScript for errors, and moving into a single folder is just not enough. You still may want to compress them down, obfuscate them, so that they are at the absolute smallest size possible. For this, a good option is Google Closure Compiler. You can get a good task to help with this so that it’s a drop in solution. Just drop this new task definition into your phing path and you are set. Now our code can implement this piece to get a better JavaScript file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<project name="MyWebapp" default="default" basedir=".">
<taskdef name="googleclosurecompiler" classname="phing.customtasks.GoogleClosureCompiler" />

<target name="default">
<property name="jslint" value="./path/to/jslint/jsl-0.3.0-mac/jsl" />
<echo msg="Checking JS for errors..." />
<jsllint haltonfailure="true" cachefile="./jslint.cache" executable="${jslint}" conffile="./path/to/jslint/jsl.default.conf">
<fileset dir="./js">
<include name="**/*.js"/>
</fileset>
</jsllint>

<foreach param="dirname" absparam="absname" target="compile-folder-js">
<fileset dir="./js">
<type type="dir" />
<depth max="0" min="0" />
</fileset>
</foreach>
</target>
<target name="compile-folder-js">
<mkdir dir="./${dirname}-js" />
<fileset id="filestocompile" dir="js/${dirname}">
<include name="**/*.js" />
</fileset>
<append destFile="./${dirname}-js/${dirname}.js">
<fileset refid="filestocompile" />
</append>

<googleclosurecompiler compilationLevel="SIMPLE_OPTIMIZATIONS" targetDir="./${dirname}-js/" failOnError="true" outputFile="${dirname}-compressed.js" merge="true">
<fileset dir="./${dirname}-js/}">
<include name="${dirname}.js" />
</fileset>
</googleclosurecompiler>
</target>

Conclusion

We’ve been able to now create a worthwhile setup that allows a webapp to move,verify and compress all of its JavaScript files whenever the command ‘phing’ is ran on the path. This is a very simple setup, but one that can really be expanded on. For instance, you can use dates or hashes appended to files so that caching is easier. You can setup a multi-directory structure for better handling of JS versions. You can even pre-gzip your code so that it is much faster for downloads and lighter weight on the server-side load.

Hopefully this will get you into the process of using Phing and developing a solid build process in your web coding.