July 21, 2006

Big News

New Release

A new tarball release of Friendly will be out in a couple of weeks. I’ve completely lost track of the 1.0dx series of numbers: the last “published” release is 1.0d1 (really 1.0d2), and I’ve made a couple dozen revisions since then, so we’ll call the next release 1.0e1 and start over.

New Features!

Among the recent changes:

A Rails-like url_for function (and Smarty helper). Friendly’s always had a function called url_for that dated back over a year, way before the initial developer release and even way before Friendly got coupled with Smarty. It never really got used because, well, PHP is not Rails. In Rails one can generate a URL to a controller or action using a hash (similar to PHP’s associative arrays), and due to Ruby’s beautifully succinct syntax it’s possible to write something like this:

url_for :controller => 'weblog', :action => 'post'

The PHP equivalent, sadly, is a little more arcane:

url_for(array('controller' => 'weblog', 'action' => 'post'));

But in Smarty — where I’d imagine this function will be getting the most play anyway — it gets a little bit cleaner:

{{url_for controller='weblog' action='post'}}

Until I took the hour or so to put together a url_for that made sense, I was having to create links like this:

<a href="{{$cfg.base_url}}admin/microsites/view/22">Some stuff</a>

And God help me if I had to have that trigger an Ajax action:

<a href="{{$cfg.base_url}}admin/microsites/view/22" onclick="new Ajax.Request(this.href,{asynchronous:true,method:'post'}); return false;">Some stuff</a>

The new url_for doesn’t completely solve all URL-related problems, but it at least makes generating portable links a little bit easier.

Database-backed sessions out of the box: We ran into a problem recently on a couple of sites where sessions were being lost after a few minutes (usually 30 minutes, but sometimes as little as five) because PHP on that particular shared box was stashing all the sessions — for every PHP/Friendly app on the machine — in /tmp. We tried for a little while to reconfigure PHP to hold onto the sessions, but we couldn’t get anything to work that wouldn’t have somehow interfered with the other 300 PHP scripts on that machine.

So I decided it was time to bring Friendly’s sessions support into the 21st century: in the current trunk version of Friendly all sessions are now stored in a MySQL table by default, and I plan to expand the session management features really soon to allow for Friendly-managed sessions to be stored on the filesystem or other database engines as well.

The main benefit here, of course, is that now Friendly keeps tabs on its own sessions and isn’t dependent on PHP or the host operating system for garbage collection. A side benefit, however, is speed: the first app I developed the DB sessions support for is now running noticeably faster now that it isn’t waiting for every other PHP script on the machine to finish doing its sessions thing to get started.

There are a few caveats/limitations to this new feature: The username Friendly uses to connect to the database (as specified in config/config.yml) must have the ability to create tables. In most shared environments you only get one MySQL user that has full privileges on a single database, and so this should be no problem. The reason for this is that rather than require a setup involving SQL files and/or shell commands, Friendly simply checks for the _friendly_sessions table and creates it if it doesn’t exist.

And the big limitation: Friendly’s session management features require that session_write_close() be called at the end of the request for any session data to be written. FriendlyMVC now calls this at the end of every request, and redirect_to calls it prior to redirecting and exiting the script. And as I find instances where a call to exit() is causing sessions not to be saved I’m going in and fixing them.

The three of you who’ve written Friendly apps already may be wondering: why on earth would I ever use exit in a Friendly app? Well, it comes in handy when you’re doing Ajax — if you don’t explicitly end the request in your action, Friendly will do its thing and apply the template and layout, which is often not what you want if you’re just sending back some updated data for a small part of the page.

So for now, be advised that you cannot use exit() without first calling session_write_close() unless you want any session changes made on that request to disappear into the ether. I acknowledge that that can really cramp your style, however, so I’ve got a solution: friendly_exit(), a convenience function that behaves identically to exit(), except that it makes sure the session is saved and performs whatever other cleanup may be required.

And this does remind me to mention: Friendly’s Ajax support is next on the list of things to get an overhaul, because the only thing better than this:

<a href="{{url_for action='view' id=$item->id}}" onclick="new Ajax.Request(this.href,{asynchronous:true,method:'post'}); return false;">Some stuff</a>

is this:

{{link_to_remote link_text='Some stuff' action='view' id=$item->id}}

New Server!

Since the launch of friendlyphp.org back in February we’ve been hosted on a shared box at TextDrive (except for the wiki, which lives at StikiPad), and that’s worked out pretty well. But things could be a little bit better. For example, we could stand to have a little more control over the Subversion repository, and a little more memory so we can run Collaboa or Trac without crashing somebody else’s Rails app.

So Jesse at Blue Box has generously offered us the use of one of his new performance VPS servers, and as soon as I have time I’ll be moving the Friendly project and ephemera over to it. For the first time there will be a ticketing system so you — yes, you! — can browse the source or, if you feel the urge, help contribute to Friendly by submitting a patch or two.

Look for more details in this space in about two weeks.