Using Phing to automate JavaScript and CSS Minimization

This article may be too advanced for beginner programmers. Unfortunately, I will not support any code that I do not write (in this case), so if you have troubles installing some of the packages required below, please see the authors of the problem code. Thank you.

PHING! Party on Wayne. Party on Garth!

If you’ve never used Phing, it’s an automation tool for PHP that is a port of Java’s ANT Build tool. But it’s not just for building (obviously, that’d be silly). You can hook all kinds of tasks into Phing: unit tests for your PHP code (you’re doing test driven development, aren’t you?), building your documentation, etc. But today, let’s look at automation of my favorite front end development tasks: packing (minimizing) our JavaScript and CSS.

To do this, I’m going to hook you up with two Filters for Phing that will use the PHP port of Dean Edwards JavaScript Packer, and the PHP class CSSTidy.

Here’s what you’ll be responsible for:

  1. Download both CSSTidy and the PHP port of Packer using the links above.
  2. Download Phing if you don’t already have it, and install it. Note the following change I had to make to my Phing \bin\phing.bat file to get it working: Change set PHP_CLASSPATH="%PHING_HOME%\classes" to remove the quotes: set PHP_CLASSPATH=%PHING_HOME%\classes
  3. Download the two Filters I made for Phing: JSPackerFilter.phps and CssTidyFilter.phps, change the extensions to .php and copy into your phing directory \classes\phing\filters\
  4. Change the path in the include_once declaration at the top of each of the files to point to the csstidy and packer libraries you downloaded above:
    include_once "C:\Lib\packer.php-1.0\class.JavaScriptPacker.php";
    
    include_once "C:\Lib\csstidy-1.3\class.csstidy.php";
  5. Get a working build file set up to point the directories your project is using. Here is a sample I made:
    <project name="testProject" default="pack-all">
    	<target name="pack-all" depends="pack-javascript,pack-css"/>
    	<target name="pack-javascript">
    		<copy todir="./Web/js" overwrite="true">
    			<fileset dir="../Web/js">
    				<include name="*.js" />
    			</fileset>
     
    			<filterchain>
    				<filterreader classname="phing.filters.JSPackerFilter">
    				  <param name="encoding" value="62"/><!-- 0,10,62,95 or 'None', 'Numeric', 'Normal', 'High ASCII'. -->
    				  <param name="fastDecode" value="true"/><!-- include the fast decoder in the packed result -->
    				  <param name="specialCharacters" value="false"/><!-- have you flagged your private and local variables in the script -->
    				</filterreader>
    			</filterchain>
    		</copy>
    	</target>
    	<target name="pack-css">
    		<copy todir="./Web/css" overwrite="true">
    			<fileset dir="../Web/css">
    				<include name="*.css" />
    			</fileset>
     
    			<filterchain>
    				<filterreader classname="phing.filters.CssTidyFilter">
    					<param name="remove_last_;" value="true"/>
    					<param name="preserveCss" value="false"/>
    					<param name="template" value="high_compression"/>
    				</filterreader>
    			</filterchain>
    		</copy>
    	</target>
    </project>

    Templates for CssTidy include: low_compression, default, high_compression (declarations are limited to one line apiece), highest_compression (everything is put on one line).

    The above build file is set up to work with the following directory structure:

    • Build
      • Web
        • css
        • js
      • build.xml
    • Web
      • css
      • js

    Files are copied from the source in /Web to the /Build/Web directory. I hope that you can see from the build.xml file above that the target directory is specified in the copy tag, todir attribute.

    <copy todir="./Web/js" overwrite="true">

    and the source directory is specified in the fileset tag, dir attribute.

    <fileset dir="../Web/css">
    	<include name="*.css" />
    </fileset>

And then, all you need to do is navigate to the directory holding your build.xml and run the phing command (if you have phing in your path. If not, you can use an absolute link to phing, for example c:\software\phing\bin\phing). Your minimized javascript and css will be in the Build directory!

VN:F [1.8.5_1061]
Rating: 4.0/5 (3 votes cast)
Using Phing to automate JavaScript and CSS Minimization4.053
This entry was posted in CSS, JavaScript, PHP and tagged , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
  • Thanks for reading. If you found this article useful, and you've read through some of my previous articles, you might as well Subscribe to my content subscribe to my blog. You'll save me some bandwidth that way.

2 Comments

  1. Posted March 4, 2010 at 5:29 pm | Permalink

    Thanks a lot for these filters! They work quite well so far and saved me a lot of time.

    I know you wrote this article 2.5 years ago, but I ran across a tiny bug with in the CssTidyFilter where the “remove_last_;” property doesn’t get set properly, though. The bug appears for me with Phing 2.4.0 and CssTidy 1.3 on Ubuntu 9.10 Karmic, haven’t tested other versions.

    My build file has this line:
    $lt;param name=”remove_last_;” value=”true”/>

    And putting an echo in CssTidyFilter after calling getParam() shows that somewhere along the way it gets changed:
    var_dump($this->removeLastSemicolon) = string(1) “1″

    So the following assignment fails:
    $css->set_cfg(‘remove_last_;’,$this->removeLastSemicolon==’true’?true:false);

    I will send a patch to the author, but in case he can’t get to it or no longer maintains this project, you can just do this (around line 202):
    $css->set_cfg(‘remove_last_;’,!empty($this->removeLastSemicolon)?true:false);

    This fix may or may not work for other properties, I have not tried it. The other properties I use (‘merge_selectors’, ‘template’ and ‘preserveCss’) seem to work fine.

    Another suggested change would be to use “require_once()” instead of “include_once()” as the script should fail if it can’t find the files it depends on.

  2. Posted March 4, 2010 at 6:28 pm | Permalink

    OK I spoke a little soon :)

    Line 216 (preserveCss) also need the fix from the above comment.

    Also, properties with number values, i.e. lines 199, 203 and 209 (optimizeShortand, propertyCase and mergeSelectors) need to be changed from:
    if(!empty($this->propertyName))

    to:
    if(isset($this->propertyName))

    since they all have number values, empty() will fail for their 0 value.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Additional comments powered by BackType