Monday, 12 January 2009

So phar so good.

I've just finished converting a small procedural application, designed to be run from the command line, to PHP5 . I wanted a way to give users a single file to execute and as phar is included in PHP5.3 I decided to try to use it.

The source tree for my application looks something like this:


/mnt/workspace/ws_phpscripts/pharsamples/a.php
/sub
/b.php
/c.php


The main code is in a.php, classes 'b' and 'c' are in sub/b.php and sub/c.php respectively and I include them from statements in a.php.

The first step is to create the phar file. By default the ability to create phar files is switched off, to enable it you will need to add the following line to your php.ini file:




phar.readonly=0


The next step is to build a phar file from the source tree, phar makes this easy for you. Here is a script to create a phar:


<?php
$phar = new Phar('/tmp/a.phar');
$phar->buildFromDirectory('/mnt/workspace/ws_phpscripts/pharsamples/src');
?>


In the first line I instantiate a new Phar object, the argument to the Phar constructor is the path to the phar file I'm going to create.


The second line just builds the phar from all the code under my src directory. So, now I have created a phar, but how can I tell that it's worked? The easiest way is to read it from another PHP script, like this:


<?php
new Phar('/tmp/a.phar' , 0, 'myphar.phar')
echo file_get_contents('phar://myphar.phar/sub/b.php');
?>


In the first line of this script I am opening the phar I have just created. In this case the Phar constructor takes three arguments. The full path to the phar is the first argument, the second argument (flags) is zero, at the moment I can't think of any reason why it would ever be anything other than zero. The flags argument is inherited from the class RecursiveDirectoryIterator, so if you want to understand what it does look at the documentation for that class. The third argument is an alias - or the way I want to refer to the phar in my script.

The second line of the script attempts to get the contents of one of my files out of the phar. If I get the contents of file b.php back from the echo I know I have created the phar correctly.

I really want to be able to execute the phar from the command line, just like PHP code. To ensure that my main program (a.php) is run when the phar is executed I need to add a stub to the phar. The full create script looks like this:


<?php

$phar = new Phar('/tmp/a.phar');
$phar->buildFromDirectory('/mnt/workspace/ws_phpscripts/pharsamples/src');

$stub = <<<ENDSTUB
<?php
Phar::mapPhar('a.phar');
require 'phar://a.phar/a.php';
__HALT_COMPILER();
ENDSTUB;

$phar->setStub($stub);

?>


That's all the code that is required. To run a.php, all I have to do is:


cd /tmp
php a.phar


This is just about the most simple use of phar, obviously it can do a lot more than this. The documentation is here.