Phar: PHP libraries included with a single file
Phar is a php extensions that provides the means for distributing code as a single archive, that does not have to be extracted to a folder before usage.
The concept is similar to JVM Jars: each archive becomes a virtual directory where files can be accessed. However, the virtual folder is not limited to class loading, but you can open and read internal files as if it were decompresse into a directory.
Phar is available for PHP 5.3 and newer. In this article we see a toy example of Phar usage, and a real world one where we put the Doctrine 2 ORM into a single file.
Hello world example
In our toy example, we want to package this file as a Phar (hello.php):
<?php echo "Hello, world of Phar!\n";
A real application would consist of multiple files and folders, but the purpose of this first example is just to get our teeth on the machinery.
Phar packaging happens with PHP code, which we can put in a build script (package.php):
#!/usr/bin/env php <?php $archive = new Phar('hello.phar', 0, 'hello.phar'); $archive->setStub('<?php Phar::mapPhar(); include "hello.php"; __HALT_COMPILER(); '); $archive['hello.php'] = file_get_contents('hello.php'); // use also $archive->buildFromDirectory(); $archive->buildFromIterator();
hello.phar is (respectively) the path and the name of the created archive.
The stub we set as PHP code is what will be executed when the Phar is accessed by external code; it should always start with Phar::mapPhar() and always terminate with __HALT_COMPILER(). We can however do what we want in the middle of the stub, from including files to setup autoloading or executing code.
Now let's execute our build script, and use the new Phar:
[10:52:02][giorgio@Desmond:~/phar_example]$ php build.php [10:52:03][giorgio@Desmond:~/phar_example]$ php hello.phar Hello, world of Phar!
Now our application can be distributed as a single file, for example over HTTP, without worrying about decompression or file permissions.
Practical usage: Doctrine 2
This more practical example uses Phar on real code, consisting of the ORM Doctrine 2. It is currently packaged as a tarball, which should be decompressed before usage; however most of the machinery is in setting up autoloading for the different folders inside the package, Doctrine/ and Symfony/. The latter folder is also inexplicably put under the Doctrine/ one, violating the PSR-0 convention (since it contains Symfony\... classes).
Let's obtain the tarball package and expand it to a folder:
wget http://www.doctrine-project.org/downloads/DoctrineORM-2.1.1-full.tar.gz tar xvzf DoctrineORM-2.1.1-full.tar.gz
Now we can write a build script, phar-package.php:
<?php $archive = new Phar('doctrine-2.1.1.phar', 0, 'doctrine-2.1.1.phar'); $archive->buildFromDirectory('doctrine-orm', '/Doctrine\/(Common|DBAL|ORM)/'); $archive->buildFromDirectory('doctrine-orm/Doctrine', '/Symfony/'); $archive->setStub(file_get_contents('phar-bootstrap.php'));
This script will include the code from a stub to execute at inclusion of the Phar, phar-bootstrap.php:
<?php Phar::mapPhar(); $basePath = 'phar://' . __FILE__ . '/'; require $basePath . 'Doctrine/Common/ClassLoader.php'; $classLoader = new \Doctrine\Common\ClassLoader('Doctrine', $basePath); $classLoader->register(); $classLoader = new \Doctrine\Common\ClassLoader('Symfony', $basePath); $classLoader->register(); __HALT_COMPILER();
An example of using Doctrine 2 as a Phar is much simpler than setting up autoloading:
<?php include_once 'doctrine-2.1.1.phar'; var_dump(class_exists('Doctrine\ORM\EntityManager', true)); var_dump(class_exists('Symfony\Component\Console\Application', true));
Phar is not limited to archiving of files. It offers more features, such as compression based on the zip format, or hashing of the archive with MD5 or SHA1 with automated verification.
Since Phar-packaged code works from an archive, it cannot easily create temporary files, a fact that makes using this method for applications more complex. It also requiresa different way of accessing files for reading and execution, by inserting before them with the phar://archive.phar/ prefix.
However it seems to me a really practical way for distributing libraries or command line utilities, which can be just downloaded and included without issues regarding the include_path or autoloading. In fact, PEAR2's installer is being distributed as a Phar.