Anyone Interested in a PHP Fluent Interface for Generating HTML?

For the past few days I’ve been toying with a set of classes in PHP that is basically a fluent interface for generating HTML. A basic usage example is:

echo p(b('This'), ' is a ', i('test'))->addClass('foo')->html();

or alternatively:

echo new Paragraph(
       new Bold('This'),
       ' is a ',
       new Italic('test'))->addClass('foo')->html();

or

echo new Paragraph()->append(new Bold('This'))
                    ->append(' is a ')
                    ->append(new Italic('test')
                    ->addClass('foo')
                    ->html();

which outputs

<p class="foo"><b>This</b> is a <i>test</i></p>

My goals in doing this were to:

  • Create a terse fluent interface for gnerating HTML;
  • Create valid HTML. This code currently implements the HTML 4.01 Transitional standard so it'll generate an error if you try and append a <p> element to an <object> element, for example;
  • Correctly handle HTML escaping implicitly; and
  • Provide a level of jQuery like manipulation functionality.

I did this largely to see how it would turn out. Basically for my own curiosity. I don't even know if I'll be using this with what I'm working on but just in case I have to ask: is this something others might be interested in?

I haven't seen anything like it but that doesn't mean it hasn't already been done. If there is interest in it, even if its just idle curiosity, I'll clean it up and open source it.

PHP Smarty Tutorial: Caching and Versioning Static Content

I am a nascent advocate of using Smarty as a view templating engine in PHP. Some—including myself until recently—might ask “Why use a templating engine for PHP, which is a templating engine?” In this post I hope to give you one example of how Smarty can clean up code in a useful way.

Static Content

Static content refers to primarily images, Cascading Style Sheets (“CSS”) and JavaScript files but can also include such things as videos, audio clips and so on. All of this tends to be labelled “static content” as it is largely unchanging (as opposed to Web pages, which are largely dynamic).

Static content can include things like static HTML files but in practice other methods tend to be more common for caching HTML content.

Caching

Static content can be cached in two places: on the client and on the server.

Caching on the server isn’t typically an issue with truly static content. It’s more an issue with cached content that is generated in some way, such as the dynamically generated Javascript files from Supercharging Javascript in PHP. Serverside caching is not the focus of this post.

Client caching involves telling the browser to keep a copy of certain content and not request it from the server. There are several variations upon this theme:

  1. Caching for a fixed period of time. For example, setting the HTTP Expires header in the future;
  2. Allowing the browser to ask if the content has changed. For example, use of ETags in HTTP/1.1; and
  3. Some combination of the two.

The one we’ll focus on is (1). It is a simple scheme but you must balance setting the Expires header sufficiently far in the future versus making spurious requests to the server when the content hasn’t changed.

Versioning

As a solution to the far futures Expires header problem of content that changes, a common scheme used by modern Websites is to use some form of versioning. What this means is this: the Expires header is set in the far future (one or more years). When the content changes a new URL is generated. This forces the browser to reload the content.

In my previous articles I used URLs like this:

http://example.com/images/logo.1233454569.png

The disadvantage of this is that it requires URL rewriting to work. I have since settled on a simpler scheme: putting the file’s last modified time as the query string. This means a URL like this:

http://example.com/images/logo.png?1233454569

No URL rewriting required and it should work out of the box with Web servers.

Static Content Domain

Another optimization that is encouraged is to split your publicly available static content onto a separate domain. The reason is that you could minimize traffic and overhead by the client not sending unnecessary cookies.

Image Dimensions

Lastly, images in particular can be optimized by specifying their height and width in the HTML. This can speed up how fast the browser renders the page and whether or not there are possibly unsightly visual artefacts as the page loads, such as content jittering or shifting position while it is loading.

Directory Structure

This example will assuming the following directory structure:

/var/www                    Top-level on host
+- /site                    Document root of dynamic domain
+- /static                  Document root of static domain
   +- /images               Images subdirectory
+- /lib                     Third-party code
   +- /Smarty-2.6.26        Smarty install files
+- /include                 PHP files not served by the Web server and thus not under the document root
   +- Mysmarty.class.php    Custom Smarty template generation. See Smarty installation notes.
   +- /templates            Smarty templates
   +- /templates_c          Compiled Smarty templates
   +- /cache                Cached Smarty content
   +- /config               Smarty config files
   +- /plugins              Smarty plugins

Your structure may of course vary. I will say I prefer to put nothing under the document root that isn’t the endpoint of an HTTP request.

The Apache, IIS, nginx or other config to set the far future Expires header is assumed.

Mysmarty.class.php

A common technique with Smarty is to create a custom subclass of Smarty that sets all the correct configuration. This looks something like this:

<?php
define('BASE_DIR',      '/var/www/');
define('LIB_DIR',       BASE_DIR . 'lib/');
define('INCLUDE_DIR',   BASE_DIR . 'include/');

// separate static site
define('STATIC_URL',    'http://mystatic.com/');
define('STATIC_DIR',    BASE_DIR . 'static/');

// use the following if everything is on one site
//define('STATIC_URL',    '/');
//define('STATIC_DIR',    BASE_DIR . 'site/');

// Smarty directories
define('TEMPLATES_DIR', INCLUDE_DIR . 'templates/');
define('CONFIG_DIR',    INCLUDE_DIR . 'config/');
define('COMPILE_DIR',   INCLUDE_DIR . 'templates_c/');
define('CACHE_DIR',     INCLUDE_DIR . 'cache/');
define('PLUGINS_DIR',   INCLUDE_DIR . 'plugins/');

// static content
define('IMAGES_DIR',    STATIC_DIR . 'images/');

// URLs
define('IMAGES_URL',    STATIC_URL . 'images/');

require '../lib/Smarty-2.6.26/libs/Smarty.class.php';

class MySmarty extends Smarty {
  function MySmarty() {
    $this->Smarty();

    $this->template_dir = TEMPLATES_DIR;
    $this->compile_dir  = COMPILE_DIR;
    $this->config_dir   = CONFIG_DIR;
    $this->cache_dir    = CACHE_DIR;
    $this->plugins_dir  = PLUGINS_DIR;
  }
}
?>

Hopefully you should be able to adapt the above to whatever directory structure you use.

Smarty Image Plugin

To demonstrate this, I’ll use just one example: images. You can however easily apply this to other forms of static content.

Assume the following code exists and is accessible to the plugin:

<?php
$standard_attributes = array('class', 'dir', 'id', 'lang', 'style', 'title');

function standard_attributes(array $attributes) {
  global $standard_attributes;
  $ret = '';
  foreach ($standard_attributes as $attr) {
    if (isset($attributes[$attr])) {
      $ret .= attribute($attr, $attributes[$attr]);
    }
  }
  return $ret;
}

function attribute($name, $value) {
  return ' ' . $name . '"' . htmlspecialchars($value) . '"';
}

function build_image($params, &$smarty) {
  $src = $params['src'];
  if (!isset($src)) {
    return "[No src to image]";
  }
  $file = IMAGES_DIR . $src;
  $mtime = filemtime($file);
  if ($mtime === false) {
    return "[Image '$src' not found]";
  }
  $height = $params['height'];
  if (!isset($height)) {
    $size = getimagesize($file);
    $height = $size[1];
  }
  $width = $params['width'];
  if (!isset($width)) {
    if (!isset($size)) {
      $size = getimagesize($file);
    }
    $width = $size[0];
  }
  $alt = htmlspecialchars($params['alt']);
  $attribs = standard_attributes($params);
  $url = IMAGES_URL . htmlspecialchars($src);
  return <<<END
<img alt="$alt" src="$url?$mtime" width="$width" height="$height" $attribs>
END;
}
?>

It makes sense to put the above into Mysmarty.class.php or at least include/require it there.

In the Smarty plugins directory, you need to create a file called function.image.php.

<?php
function smarty_function_image($params, &$smarty) {
  return build_image($params, &$smarty);
}
?>

You may well ask "why not put the code in the plugin directly instead of calling a function?" and you'd be right except for one thing: I will reuse it for another plugin but more on that later.

This plugin does several things:

  1. It verifies that the specified image actually exists in the expected location;
  2. It gets the last modified time (mtime) of the image file;
  3. It prepends the correct URI to the image name including the static domain if there is one;
  4. The plugin user can specify as many or as few attributes of the image tag as they wish. This version is written to handle most that you would expect to encounter and is easily extensible to include others;
  5. It uses the GD library to get the height and width of the image, if required. It is only required if the user does not specify both. The user-supplied values are used in preference.

That’s a lot. In a template it is nothing more than:

{image src="logo.gif"}
{image src="top.png" height="100" width="300" alt="Some alt text"}
etc

You can of course do that in standard PHP. I've previously created functions like auto_version() just for this purpose but it's nowhere near as readable and maintainable. The pure PHP version would have to pass in an array of parameters like so:

<?php echo auto_version(array('src' => 'logo.gif', 'height' => '100', 'width' => '200', 'alt' => 'Some alt test'); ?>

But it gets even better.

Compiler Plugins

The astute reader will point out the inefficiency of this: every page request will access the image file once or twice. Firstly to get the mtime. Secondly to get the height and width (if not specified by the user). What’s more the second operation is arguably more expensive since it necessitates reading in the image file and processing it.

The plugin we just created is, in Smarty terms, a template function. The function is called with every view of the template. An alternative is to use a compiler function.

The basic lifecycle of a template is:

  1. If the template file is newer than the compiled template or there is no compiled template, compile it;
  2. Templates are compiled into PHP files that are put in the ‘compile_dir’ directory (typically ‘templates_c’);
  3. Caching and other config can change this.

A template function is called on each execution. A compiler function is only called when the template is compiled into PHP.

You might say that for many of your images they change very infrequently. As such the overhead of reading the dimensions and the last modified time is overkill. This is a good candidate for a compiler function. These values will only be calculated once.

The downside is that if the image does change you will need to regenerate the compiled templates. This is easily done by simply deleting the relevant contents of the compile directory.

Static Image Plugin

We will create another version of our plugin that will be executed at template compile time. We will call this one ‘static_image’. If it had the same name ('”image”) as the template function, the compiler function would take precedence (ie the other would not be called).

There are several differences with template functions:

  1. The file is named compiler.static_image.php;
  2. The function is named smarty_compiler_static_image();
  3. The raw argument (eg ‘src=”logo.png height=”100”’) is passed as a string to the plugin instead of an array of key-value pairs; and
  4. A compiler function generates PHP statements instead of HTML markup.

Bearing all this in mind, we need two functions: one to parse the raw text argument and another to generate the PHP statements. Also, the build_image() function needs to handle a string parameter.

_parse_attrs($tag_attrs);
  $ret = array();
  foreach ($args as $k => $v) {
    $ret[$k] = $smarty->_dequote($v);
  }
  return $ret;
}

function php_echo(string $text) {
  return 'echo "' . addslashes($text) . "\";";
}

function build_image($params, &$smarty) {
  if (!is_array($params)) {
    $params = build_params($params);
  }
  $src = $params['src'];
  if (!isset($src)) {
    return "[No src to image]";
  }
  $file = IMAGES_DIR . $src;
  $mtime = filemtime($file);
  if ($mtime === false) {
    return "[Image '$src' not found]";
  }
  $height = $params['height'];
  if (!isset($height)) {
    $size = getimagesize($file);
    $height = $size[1];
  }
  $width = $params['width'];
  if (!isset($width)) {
    if (!isset($size)) {
      $size = getimagesize($file);
    }
    $width = $size[0];
  }
  $alt = htmlspecialchars($params['alt']);
  $attribs = standard_attributes($params);
  $url = IMAGES_URL . htmlspecialchars($src);
  return <<<END
<img alt="$alt" src="$url?$mtime" width="$width" height="$height" $attribs>
END;
}
?>

With these changes and additions, the compiler template becomes:

<?php
function smarty_compiler_static_image($tag_attrs, &$smarty) {
  return php_echo(build_image($tag_attrs, &$smarty));
}
?>

Using it is as simple as:

{static_image src="logo.png"}

This is something not easily done in pure PHP.

Conclusion

This is but scratching the surface of what Smarty is capable of but I hope it provides a useful demonstration of what makes Amsarty so good.

What’s more this functionality comes at very low cost since Smarty is simply compiled into straight PHP. Barring the initial compile, the only cost on a per-request basis is one to see if the template has changed (and thus needs a recompile). In production environments this check can even be disabled for further performance.

Windows Git Tutorial: Cygwin, SSH and Projectlocker

Recently I’ve switched from using Subversion to using Git for version control on personal projects. Now as much as Windows annoys me—and believe it me when I say it does annoy me—I still prefer to use it as a development environment (for PHP or Java) over Ubuntu (which I used for that sort of thing for a year or two). There is a better selection of tools. The UI is better. And to top it off, Civilization 4 is easier to run on Windows.

Why Git?

There are quite a few comparisons between Subversion and Git out there. Ultimately they’re just different philosophies and approaches. For me it came down to:

  • I like the Git philosophy of being able to distribute patches without creating history on your central repository. At work, doing a code review usually entails doing a checkin (and yes I know you can create svn diffs);
  • Git allows you to push your changes to several repositories. This is great for redundancy. I’m paranoid about losing source code. Anyone who writes software should be paranoid about becoming the next Avsim;
  • You can use Git for deployment to your hosting provider; and
  • All the cool kids are doing it. :-)

That being said, Git does have some downsides:

  • Subversion has better tooling and IDE integration;
  • Getting Git to work right on Windows is a hair-pulling experience (which is the point of this post);
  • With Subversion you are the user that’s set up for you in the repository. In Git, you are who you say you are, which takes a bit of getting used to;
  • Obviously multiple repositories is by its nature more complicated than a single central repository; and
  • Subversion deals with binary files better and easier than Git does.

One piece of advice I’ll give you is this: If your Windows username has a space in it, change it. I was bashing my head against a brick wall for awhile before I figured out that my Git commands were failing to remotely authenticate because the Python script didn’t handle the case where the username contained a space as best as I could tell.

Why Cygwin?

The nicest environment to have is one where you don’t have to type in your password every time you want to do a pull or push to a remote repository. Good security suggests you should do such things over SSH.

I wasted a lot of time with firstly the bundled git-bash and then with PuTTY. I had problems with git-bash like when doing a git push over ssh it would inexplicably hang. It also seemed to be leaking file descriptors or something because it reached a point where it wouldn’t connect to anything, not even things I’d successfully connected to.

PuTTY I usually use under sufferance. It does the job but I find myself just wanting a good command line (rather than say plink.exe and a command prompt).

Do yourself a favour and use Cygwin from Day One.

I’m currently using Cygwin 1.7.0 even though it’s in beta. When you install it, you have to select the packages you want. SSH (openssh) is not installed by default so select that under Net or search for it and select it.

One of the (many) nice things about Cygwin is that it creates a virtual filesystem for you where you can mount different directories at different points using the mount command. For example:

mount c:/xampp/htdocs /www

creates a convenient spot for your deployment directory.

If you’re not familiar with a Unix/Linux shell like bash and the typical Unix/Linux commands, do yourself a favour and get comfortable with them.

Why Projectlocker?

One of the things I wanted was a remote backup for my source code that is separate to my hosting account. Some may consider this overly paranoid. I think it’s good practice to have a local repository, your production code deployed on your hosting provider and a remote backup of your development work since that won’t necessarily be pushed to the hosting provider.

I compared several private hosting alternatives including the ever-popular Github, Unfuddle and Projectlocker. Their free version for me had the best features:

  • Both Subversion and Git;
  • Multiple active projects;
  • Unlimited repositories;
  • RAID storage and nightly backups;
  • 500MB storage; AND
  • SSL encryption.

These services all differ in what products they integrate (eg Wikis, bug/issue tracking, project management and so on) so you really need to do your own research to find what’s right for you.

The one downside of Projectlocker is their website is rather primitive and at times slow to respond (with certain pages). But once you’re set up you should have almost no need to go to it again.

Setting up SSH

Using public key encryption is far more convenient than being asked for your password every time you want to access the remote repository.

First, create a public key if you don’t have one already:

username@hostname:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa):
Created directory '/home/username/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/username/.ssh/id_rsa.
Your public key has been saved in /home/username/.ssh/id_rsa.pub.
The key fingerprint is:
63:17:a4:06:78:34:9d:4f:2d:4c:7f:8f:84:5a:4b:c4 username@hostname
username@hostname:~$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArGyNxx5nct8vic01GxKPybSxL6ZNBMcOrtwo0dMSU0qN9l4pxAuX0jjJe4Pl/I121NztTDdiIntOTaYiQQXTQ2NP8MD5X1oyr7svs8Rm50zpQwOQ3rt4MvwgotZZjMoETT39fA3soRoQLQS5LzD0W7cvVdSoTGYcL+lfv2f0xQdS+CgCrRTeiwn7KYgD6arZLo6B1wVPCAmaiyo1Hetu1q7UIzh4dCACUy4BymlLAxon0NRWAhEADKltZqMitnPgDqtRXyMLUzEn6AvIotRprK7LoPzvLqz2MgBfzTne13Dz8LFOPhbM2n7cSf/OEUt+TtKZqvIoUb79smKDsf2aXw== username@hostname

Next, go to the Projectlocker Portal, login and if you haven’t done so already, create a Git repository. Your Git URL will be under “User Home” on the left hand menu. Also on the left-hand menu is “Manage Public Keys”. Click “New Key” and enter:

Save that and assuming everything has been done correctly you should be able to go to your local machine and type:

username@host:~$ ssh git-CompanyName@freeN.projectlocker.com
PTY allocation request failed on channel 0
ERROR:gitosis.serve.main:Need SSH_ORIGINAL_COMMAND in environment.
Connection to freeN.projectlocker.com closed.

You don't have shell access but as long as you don't get prompted for your password, it should all be working correctly. You can get more detailed information with:

username@host:~$ ssh -v git-CompanyName@freeN.projectlocker.com

or even more verbose:

username@host:~$ ssh -vv git-CompanyName@freeN.projectlocker.com

Local Git Repository

There are multiple ways of doing this. You can create a local repository and push it to Projectlocker or you can simply clone it, which is what I’ll do:

username@hostname:~$ git config --global user.name "Your Name"
username@hostname:~$ git config --global user.email "youremail@example.com"
username@hostname:~$ git clone git-CompanyName@freeN.projectlocker.com:project.git
Initialized empty Git repository in /home/username/project/.git/
remote: Counting objects: 118, done.
remote: Compressing objects: 100% (96/96), done.
remote: Total 118 (delta 15), reused 111 (delta 15)
Receiving objects: 100% (118/118), 184.08 KiB | 69 KiB/s, done.
Resolving deltas: 100% (15/15), done.

Your output may look different as I'm cloning a populated repository in this case. The first two statements are telling Git who you are. Any commits will be identified by that name and user and this includes pushes to remote repositories. This is what I mean by Subversoin users needing to get used to this. Rather than being who the remote server authorizes you as, you are who you say you are.

An alternative way to set this up is:

username@hostname:~/work$ git init
Initialized empty Git repository in /home/username/work/.git/
username@hostname:~/work$ git remote add project git-CompanyName@freeN.projectlocker.com:project.git
username@hostname:~/work$ git pull project master
remote: Counting objects: 126, done.
remote: Compressing objects: 100% (102/102), done.
remote: Total 126 (delta 17), reused 111 (delta 15)
Receiving objects: 100% (126/126), 184.87 KiB | 78 KiB/s, done.
Resolving deltas: 100% (17/17), done.
From git-CompanyName@freeN.projectlocker.com:project
 * branch            master     -> FETCH_HEAD

Now try the following test:

username@hostname:~/project$ cat > test.txt
This is a test
^D
username@hostname:~/project$ git add test.txt
username@hostname:~/project$ git remote
origin
username@hostname:~/project$ git commit -m "adding test file"
[master 4811b38] adding test file
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 test.txt
username@hostname:~/project$ git push origin master
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 282 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git-CompanyName@freeN.projectlocker.com:project.git
   a6a6bf9..4811b38  master -> master
username@hostname:~/project$ git rm test.txt
rm 'test.txt'
username@hostname:~/project$ git commit -m "getting rid of test"
[master 172d57d] getting rid of test
 1 files changed, 0 insertions(+), 1 deletions(-)
 delete mode 100644 test.txt
username@hostname:~/project$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 243 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
To git-CompanyName@freeN.projectlocker.com:project.git
   3edc309..bccaeeb  master -> master

Here we’ve simply added a test file, committed it, pushed the whole master branch back to Projectlocker and then removed it. The "git remote" class lists our remote repositories

Remote Deployment

To do this cleanly, you require SSH shell access to your hosting provider or remote server. Rather than I write this out myself, I shall point you to excellent Using Git to manage a web site as to how to set this up.

Conclusion

I hope you’ve found this guide useful. It can be a daunting task to figure out exactly where to start and how to begin with a new tool like Git, particularly because there are so many ways you can use it. I truly hope this saves you some grief in getting it set up and working on Windows.

PHP and Smarty: What IDE?

This week I’ve decided to use Smarty for a personal project. I’ve previously been leery of using a template engine with PHP because PHP is a template engine. You can embed code in what are otherwise HTML documents.

Why Smarty?

But this week I’ve come around to the Smarty way of thinking for several reasons.

  1. It has excellent documentation. Every function is documented (as far as I can tell) and there are sections on relevant advanced topics. As regular readers will know, I have been critical of projects lacking documentation;
  2. It’s compiled to PHP anyway so apart from an initial “compile”, there is performance cost for using it.
  3. It simplifies a lot of common task like odd-even table row styling, rendering combo boxes with a particular item selected, etc;
  4. There are extensive variable modifiers;
  5. There is very little magic going on. By “magic” I basically mean auto-loading and other things that introduce code you don’t necessarily expect. Smarty is very straightforward and is obviously designed for high-performance use, which I really appreciate;
  6. Smarty is extensible with it’s plugin architecture and input and output filters.

Ok, so decision made: use Smarty. Now what?

Where are the Tools?

I still haven’t found a PHP IDE that I’m comfortable with. In part, this is probably due to using Intellij IDEA, which as far as I’m concerned is by far the best Java IDE out there. So much so that many Visual Studio users consider Resharper a “must have” extension for .Net development. Resharper and Intellij are both by Jetbrains. Resharper adds a lot of the refactoring tools that Intellij has to Visual Studio.

So what am I looking for?

  1. Syntax highlighting;
  2. Code completion;
  3. Being able to navigate to and from templates, plugins, etc; and
  4. Some form of validation (eg plugin file naming).

The code completion and syntax highlighting should, at a minimum, apply to PHP, HTML, Javascript and CSS. Ideally it would also include good jQuery support but that’s just a nice-to-have.

This isn’t an exhaustive review of these tools, merely an overview based on a fairly narrow set of requirements.

Eclipse PDT

I have never quite grokked the Eclipse way of thinking. The whole perspectives thing where your whole IDE changes when you are, say, debugging never sat right with me. To each their own I guess. You also have the well-known malaise known as Eclipse plugin hell.

Still, I’ve tried it a couple of times and it’s OK but it doesn’t understand Smarty templates. There is a project to add Smarty support to Eclipse but it seems to be abandoned or dead and doesn’t seem to work with Eclipse 3.5.

Netbeans

For PHP I’ve used Netbeans far more than Eclipse. I can’t give you a concrete reason for this other than to say that I just found it less bloated and more straightforward. But unfortunately there is no Smarty support and it appears there are no plans to add it.

Komodo Edit

I only tried the free version (Komodo IDE is commercial) but it has no Smarty support. I’ve read some people rave about this tool. Personally I found it uninspiring.

PhpEd

There are lot of things I like about PhpEd but it is commercial. It has one glaring problem though: You can’t change the font size menus and other UI components. You can change the editing pane but the rest is derived from the OS DPI settings. Apparently this is because of the framework PhpEd is written in from what I could gather from the forums. Still, that’s a problem. There’s a huge difference between a 24” 1920x1200 display and a 15” laptop display that’s also 1920x1200. Changing the DPI setting in the OS is fraught with problems so changing it is not always a viable option.

That being said, I am generally most impressed with this IDE out of all the others. The price (US$210) is that high but it can be a lot for a personal project considering how much you can get for free elsewhere.

One thing I don’t like is that you have to buy a Windows or a Linux version. Why? Compare this to Intellij. You simply buy a license and you can use it on Windows, Mac or Linux. Why should it matter if I want to change my OS? It’s also a greatly different version (3.3.3 on Linux, 5.8 on Windows). Is there a difference? Is Linux lagging behind?

PhpEd does understand Smarty templates though so it has to get a big plus for that.

CodeLobster PHP

I’ve been testing Code Lobster for a few days. It’s an interesting product. What I like about it:

  • It’s lightweight at under 10 megs in size;
  • The UI is modern-looking and snappy;
  • It has support for Smarty, Joomla, Wordpress, CodeIgniter, Drupal and jQuery;
  • It is quite cheap and you only pay for those plugins you use, basically.

That being said, I did come up with a few things that need some more work.

  • No “live” search. I’ve been spoilt by Intellij on this one. This is where the cursor moves as you type so you can stop typing as soon as you get to where you want. I consider this a “must have” feature of any editor. The venerable search box needs to be retired;
  • The search/replace functionality was a bit awkward in how it searches multiple files. It doesn’t list all the matches that you can just click on. Searching a subfolder is awkward;
  • Making changes to the filesystem externally requires a manual ‘Reload Project’. Something like Netbeans or Intellij will just find changed files quickly;
  • There doesn’t seem to be a way of moving files around within the IDE;
  • Some of the auto-formatting takes a bit of getting used to. For example, you type an opening brace and it advances automatically to the next line and indents. That’s not bad. You just need to get used to it.

I can’t say this is an exhaustive list and I’m not saying it’s a bad product. Overall I like it. It’s just a bit rough around the edges.

Web IDE

In the last month, Jetbrains has announced the development of Web IDE in standard and PHP editions. You can download an Early Access Preview release and try it out. To me this is the Great White Hope of the PHP IDE market because I have such enormous respect for Intellij’s code editor, which to me is the most important part of any IDE.

I’ve had a brief look at the PHP support and it still seems like there is a long way to go but I’ll certainly keep an eye on it. Based on Jetbrain’s track record, I have a lot of hope for this one but as Eclipse has shown, making a good Java IDE is different to making a good PHP IDE and as soon as you try to do everything you end up being good at nothing. I usually describe this condition as “doing everything badly”.

Also, it seems Jetbrains will be rolling this functionality into the fully-featured Intellij IDEA 9 (Maia) release so if you happen to get that version anyway (which I will) you’ll get it for “free”.

The good thing about Intellij is that it’s equally usable on Mac, Linux and Windows and you can use your license for any of those. It’s out of the box functionality comes with Subversion integration and pretty much everything you need out of the box. For me, this sure beats the plugin hell of Eclipse like figuring out which Subversion plugin to use (Subclipse or Subversive).

It’s the Little Things

There are plenty of people who happily develop using a text editor such as Textmate, Notepad++, Vim or Emacs. You can but there’s nothing like an IDE that supports left-clicking on a class to open the other class. Or an IDE that understands what framework you use (eg understanding Spring is particularly useful for a Java IDE).

Several of the programs I looked at did things like have code completion for HTML, CSS and Javascript to the point that you could click Ctrl+space and get a list of values for a CSS property. All of those things are useful.

One of the things that really impresses me about Intellij is the little ways the editing makes your life easier. This can be as simple as you type “mysql_query(“ and it inserts a closing parenthesis for you. Lots of programs do that but in Intellij if you then type a closing parenthesis, it moves you cursor after the automatic one. No spurious parenthesis is added and I don’t have to navigate over the automatic one with cursor keys or mouse. This speeds up how fast you can type and helps not break your flow.

That may seem like a small thing but there are literally hundreds of things like that in Intellij to the point that I now look for them in anything else I use, for better or for worse.

Conclusion

I guess the downside of using a dynamic language like PHP is that it’s harder to do anything like validation and navigation.

For my money, at this point in time, I would have to say that PhpEd is the best of the bunch if Smarty is important to you but keep an eye on Web IDE/Intellij IDEA when the next version is released by the end of the year (Q4 2009).