Leaking Abstraction

Loosely coupled thoughts on web development

Archive for February 2010

Part II: Managing CSS and JavaScript files within a Zend Framework App

leave a comment »

Since my first post seemed to garner a good amount of attention I thought I’d follow up with another post on how to optimize our CSS/JS view helper components a bit.

The biggest suggestion — and rightfully so, was to minify or gzip the extra controller/action Javascript or CSS file.
It doesn’t take a lot of imagination to implement an improvement. There are several easy ways we can prevent the client (our users) from needing to download another CSS or JS file to view a particular controller action.

Firstly, we can get both the CSS and JS files to output in-line instead of loading as a separate file very easily:

File: /application/views/helpers/JavascriptHelper.php

getRequest();
		$file_uri = 'media/js/' . $request->getControllerName() . '/' . $request->getActionName() . '.js';

		if (file_exists($file_uri)) {
			$this->view->headScript()->appendScript('/' . $file_uri);
		}
	}
}

In the CSS helper, we’ll use the HeadStyle helper instead of the HeadLink helper:

File: /application/views/helpers/CssHelper.php

getRequest();
		$file_uri = 'media/css/' . $request->getControllerName() . '/' . $request->getActionName() . '.css';

		if (file_exists($file_uri)) {
			$this->view->headStyle()->appendStyle(file_get_contents($file_uri));
		}

		return $this->view->headStyle();

	}
}

If you were using the previous version of the CSS helper, make sure you update your layout to echo the HeadStyle helper. Be aware that the file references (e.g., background images) in your controller / action CSS files will now have a new base URL, so you will need to update accordingly.

The next level – minifying the inline output

The next obvious step is to minify the additional output. My first inclination was to use the CSS/JS minify library aptly titled minify, however, that does a lot more than we need. I’m not a big fan of adding large libraries for the sake of one function, so let’s instead explore how we can create exactly what we need within our view helper.

A little more googling gets us to this solution. Sure, it’s not perfect, but performing 90% of the necessary packing is good for me.
My CSS view helper now turns into this:

File: /application/views/helpers/CssHelper.php

getRequest();
		$file_uri = 'media/css/' . $request->getControllerName() . '/' . $request->getActionName() . '.css';

		if (file_exists($file_uri)) {
			$css = $this->minify(file_get_contents($file_uri));

			$this->view->headStyle()->appendStyle($css);
		}
		return $this->view->headStyle();
	}

	function minify($css) {
		// 90% minifying from http://www.lateralcode.com/css-minifer/
		$css = preg_replace('#\s+#',' ',$css);
		$css = preg_replace('#/\*.*?\*/#s','',$css);
		$css = str_replace('; ',';',$css);
		$css = str_replace(': ',':',$css);
		$css = str_replace(' {','{',$css);
		$css = str_replace('{ ','{',$css);
		$css = str_replace(', ',',',$css);
		$css = str_replace('} ','}',$css);
		$css = str_replace(';}','}',$css);
		return trim($css);
	}

}

For the JS packer, we’ll use Dean Edward’s Javascript Packer that was ported to PHP by Nicolas Martin. You can download the class here:

Download Javascript Packer by Dean Edward

  •  

I added the JS packer to /library/jspacker/JavaScriptPacker.php. I modify my JavaScript helper by doing the following:

File: /application/views/helpers/JavascriptHelper.php

getRequest();
		$file_uri = 'media/js/' . $request->getControllerName() . '/' . $request->getActionName() . '.js';

		if (file_exists($file_uri)) {
			$javascript = file_get_contents($file_uri);
			require_once('jspacker/JavaScriptPacker.php');

			$packer = new JavaScriptPacker($javascript);
			$this->view->headScript()->appendScript($packer->pack());

			return $this->view->headScript();
		}
	}
}

The major disadvantage to this is debugging can be a pain when you don’t have line numbers or variable names to reference. Thus, I added a conditional statement that only packs the Javascript if not in the development environment:

$javascript = file_get_contents($file_uri);
if (APPLICATION_ENV != 'development') {
	$packer = new JavaScriptPacker($javascript);
	$javascript = $packer->pack();
}
$this->view->headScript()->appendScript($javascript);

Presto! Auto-minifying, auto-packing inline Javascript and CSS only when we need it for our controller/actions.

Next steps? I suspect our next step is to add some server side caching so all this parsing doesn’t happen in real time at the client’s expense. Look for a short part 3 using Zend_Cache very soon.

Questions, comments? Please feel free to leave them below!

Written by Andy Baird

February 15, 2010 at 1:22 am

Managing CSS and JavaScript files within a Zend Framework App

with 6 comments

When I’m creating a large external facing website or application, one of the biggest messes I used to end up making was in my CSS and JavaScript files.

As a developer/designer (jack of all trades, master of none, if you will) I tend to stay away from some of the more common developer solutions that take care of a lot of the design overhead (e.g., jQuery UI framework, Dojo, etc). Flexibility is paramount to good design — boxing yourself into a specific layout for every page is bound to produce a boring, forgettable website. Design is highly underrated to developers, I believe a good aesthetic sense is a skill worth maturing for anyone who develops web applications. After all, you can have the most beautiful underlying code, but if it’s presentation is anything short of beautiful it completely loses its “wow” factor.

Full design control usually means large, thousand plus line CSS files that equal serious pain when it comes to maintenance or building upon. If you don’t namespace your selectors carefully you’ll end up paying for it down the road. And heaven forbid you apply a default HTML tag styling. So, ranting aside, how do you maintain flexibility and still keep tidy CSS and JavaScript? My solution is to keep the very same directory/file structure that is used for the application for my CSS and JavaScript. The best part is by writing a very simple view helper, it’s super easy to automate the proper head link and script file inclusions.

Here’s what my CSS and JavaScript view helpers look like:

File: application/views/helpers/JavascriptHelper.php

getRequest();
		$file_uri = 'media/js/' . $request->getControllerName() . '/' . $request->getActionName() . '.js';

		if (file_exists($file_uri)) {
			$this->view->headScript()->appendFile('/' . $file_uri);
		}
	}
}

File: application/views/helpers/CssHelper.php

getRequest();
		$file_uri = 'media/css/' . $request->getControllerName() . '/' . $request->getActionName() . '.css';

		if (file_exists($file_uri)) {
			$this->view->headLink()->appendStylesheet('/' . $file_uri);
		}

		return $this->view->headLink();

	}
}

With that done, add the helper to your layout in the section:

File: application/layouts/scripts/layout.phtml


	
    <title>My app title</title>
    <?= $this->headTitle() ?>
    <?= $this->headMeta() ?>
    <? $this->headLink()->appendStylesheet('/media/css/global.css') ?>
    <?= $this->headLink()->appendStylesheet('/media/css/iefix.css','screen','lt IE 7') ?>
    <?= $this->cssHelper() ?>
    <?= $this->javascriptHelper() ?>

Now, anytime a controller action is invoked it will look for a javascript and css file in the same controller/action file path. In the above examples I’ve hard-coded the CSS and Javascript parent directories (/public/media for me) directly in to the code, but this would be pretty easy if you wanted to throw it in a config file or something instead. Otherwise just change to your preferred directory and your good to go.

The next thing I’d like to explore is automatically packing/minifying all CSS and Javascript into one file (or possibly outputting them directly to the layout in-line) and then caching the results of that to optimize bandwidth usage. If I ever have the luxury of that being a priority, that is :)

Written by Andy Baird

February 1, 2010 at 5:26 pm

Posted in Tutorials

Tagged with , ,

New domain, mission statement

leave a comment »

I’ve decided to change the name of this blog from zendframeworkhelp.com to leakingabstraction.com (leakyabstractions.com was of course taken).

I did this because the name implied that I am some Zend Framework guru — which I am not, so I have changed the name to something more fitting. Of course, as I code and learn new things, I will post articles/tutorials here for all. Nothing changes are far as that goes.It felt a little bit arrogant to represent this website and myself as an expert when I am still learning so much from the true experts. Truthfully, my goal is to suck up as much knowledge as I can and then spit out all the good parts. The “tutorials” and “articles” section at the top will remain and I will continue to contribute to both, although there may be more general/opinion posts in the future.

If you want to know what inspired the name, have a read of this famous Joel on Software article.

Written by Andy Baird

February 1, 2010 at 4:44 pm

Posted in Uncategorized

Follow

Get every new post delivered to your Inbox.