PHP + Phar + Sqlite

August 24th, 2008

Posted at 9:48pm by Stan

I was reminded the other day of cool a mashup of PHP, Phar and Sqlite can be. Hopefully you know what PHP is, but it's possible you haven't heard of Phar yet. Phar is a file format that can contain an entire application in a single file. It is available via the PEAR library through the PHP_Archive package. Think of Phar in terms similar to Jar for Java. It allows you to ship around an entirely functioning application in a single file that can be executed by the interpreter it was written for. Since Phar debuted as a PEAR library it's also been developed into a native extension via the Pecl repository, and I'm happy to report it will be moved into the core for PHP 5.3. Lastly there is Sqlite which beginning with version 5 of PHP has been bundled and embedded into its core. Currently you get the 2.x API via the native methods (sqlite_*()) and the 3.X API via PDO. In 5.3 this will change though, and you'll get access to 5.3 in the native methods as well, which is nothing short of wonderful.

What is it about all these three technologies, when mixed together that become so cool? My first use of Phar's were in developing a distributed mechanism for PageSite updates. I had roughly 10 clients, and I was rapidly updating the source code fixing bugs and adding features. I wanted a quick way of distributing those changes across all 10+ clients without having to FTP into each account and upload new source code. I wrote into a PageSite a series of methods that checked an XML file located remotely on my personal web server, this XML file contained a list of PageSite releases and the md5 hashes to go along with them. When I rolled out a new release, this XML file (which was generated automatically by PHP) would display a new entry for the release. When clients automatically checked the release server and saw the new entry they would prompt the user to update their installation, the user would select yes an AJAX request would fire off that called a PHP page which made a request to my local server with an authentication key to download a new phar file. That new phar file had its md5 hash checked and if all was well it would then move the file over the currently executing one and return true to the AJAX request, which then refreshed the page. In this way the update mechanism was seamless, and I could easily transmit a single file to clients and update their remote installations. I added to this a way of mapping update scripts to release numbers, so that after a new Phar was downloaded if there were any database changes that needed to be made they could get processed automatically as well. The only downside to all of this was that sometimes a particular update introduced functionality that confused an end-user, and it would have been safer for me just not to distribute it to that user... but that's a different topic for a different blog article.

Automatic distribution is really where Phar's shine, they allow for you to move an entire application around without all the worries of uploading 100+ files to a web server. If you automate the process of creating Phar's you can start doing other clever things, like building the Phar for particular situations, say bundling optional modules A, B and C into Phar 1 and B and D into Phar 2. In PHP_Archive there is a fallback class for reading archives which can be added to the stub of a Phar and provides fallback compatibility for installations that do not have the PHP_Archive library or the Phar extension. For awhile, I was able to maintain full backwards compatibility with PHP 4.4.x using Phars and it didn't matter if the user even knew what it was. It just worked, and right out of the box! If you're concerned about the performance of Phar's, which in my experience has been pretty negligible, you can even write your Phar to extract itself so that the files will execute existing outside of the Phar.

Let's switch back to Sqlite. The power and beauty of Sqlite is that you don't have to mess with a database server, it's lite and fast and it works very well with PHP. You won't find all of the fancy visual database design tools for Sqlite like you will for MySQL (though there is a cool Firefox extension that accomplishes this task), but you will find a fully functioning SQL-based database that's ready to work on any installation of PHP 5. One of the things I've just recently started doing is providing sample working copies of databases for applications written in Sqlite. Typically I abstract any database call out into a series of simple classes. Nothing fancy like MDB, DB, AdoDB or the ZF DB stuff, it's just a wrapper to allow me to switch different drivers in and out quickly. You can store non-PHP files in a Phar, like for example a Sqlite database. Just recently I wondered why I couldn't just have the Phar copy of an app install itself. If it has write access to the folder it is in (which normally it should for other tasks), then I should be safe to extract out the sample database and place it in a location I can work with it. If my database is designed correctly, it is such that it will work as soon as my app connects to it. Together with Phar then, I have an automatically distributed and updated application that is capable of installing itself! That's pretty exciting.

A couple of finer points worth mentioning... There is a small performance impact for using Phar. Like I said, though, it's negligible for most users. Higher Things, for example, gets just enough traffic to rationalize the need for extracting an application out of a Phar. This is what I do subsequently, but even that's automated by the Phar stub file I wrote. I do highly recommend any distribution process be tightly designed, meaning you use some kind of authentication key, you always provide ways of verifying the contents of the Phar ( so as not to royally screw up your install), and another way of updating internalized changes (like to the database). Also, be aware that Phar's don't always play nice going between machine types. To this date my buddy Erich is unable to make a Phar on his Windows machine that will upload to his *nix server and run correctly, this has to do with the underlying technology of the Phar and while sometimes is solved by updating to the most recent copies of Phar, PHP_Archive and PHP itself, you can still run into goody situations. FTP'ing a Phar can be quirky as well. Typically I use Transmit for my FTP needs (gotta love the two panes), but for whatever reason it struggles to transmit an entire Phar file so I use Cyberduck when uploading those instead. Your milage may vary. I already mentioned the limited resources of Sqlite in terms of UI's, so if you're not comfortable with the terminal you've been warned. I also highly recommend using a Sqlite extension that is compatible with the 3.x API. Whether this means you install the native Sqlite3 extension via Pecl, wait for PHP 5.3 or use the PDO extension doesn't matter, the bottom line is that the added benefits of Sqlite3 over Sqlite2 come in handy, and converting a database between the two is annoying.

These are some great emerging aspects of PHP that are well worth looking into. They open up new possibilities and opportunities for PHP developers, especially those with larger clientele that spend a lot of time installing, distributing and upgrading code.

Comments:

Posted on October 06, 2008 02:21pm by Greg
interesting post... I'd love to see some of your phar applications or at least the stub files for creating a self extracting/installing app. I've been thinking about doing something like that ever since I saw the post on php.net about the alpha version of 5.3!
Posted on October 06, 2008 05:04pm by Stan Lemon
Greg,
Unfortunately everything I've been working on with phar lately is closed source by the owners (via work). I can tell you the stub usually initializes the routing mechanism for the application. For example something as simple as /CLASS/METHOD.action => $class->method(), this stuff is placed into the stub, usually via an include since ideally I want the application to run identically outside of the phar. As far as self-extracting, we qualify an "installed" copy somehow, whether it be the presence of a config file or whatever, and if that doesn't exist we begin the extraction, first checking to see if the directory is writable and if it is iterating using the Phar class (present with the phar extension) and writing the files with normal file system calls. Important things to pay attention to are file paths, if you need to use the magic-rewrite options in building a phar this won't be very successful. We have a constant that contains the base path of all includes and such, this is determined when the app loads and allows for us to use the same files in and out of a phar, and even to the use the files when extracted from a phar.

Hope this helps, let me know if you have any other more specific questions.

Pax,
SL

Post Comment:


Enter the code you see in the image below.