I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 637 posts at DZone. You can read more from them at their website. View Full User Profile

Phar: PHP libraries included with a single file

09.22.2011
| 10309 views |
  • submit to reddit

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));

I've put the scripts on Github for your convenience. I followed the example of the packaging code of Eric Clemmons for doctrine-migrations.

Want more?

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.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Luis Cordoval replied on Fri, 2011/09/23 - 1:46am

hi Giorgio, nice article, i was wondering why you had to break the PSR-0 standard, hmm it kind of kills the purpose of an orderly thing. I guess silex can give perhaps some help in getting for instance a sf2 app into one phar file. This would be of interest as there could be some apps that lend themselves to this. The real example you provide is of course real but not so real :)... If there is a way to properly set Symfony folders at the level of Doctrine folders then work on resolving the issues for other third party libraries and bundles then perhaps this could be done. Hmm, good intro to phar anyway, it helps me to see now silex phar with different eyes. thanks! http://www.craftitonline.com

Giorgio Sironi replied on Mon, 2011/09/26 - 3:40am in response to: Luis Cordoval

Well, I didn't have to break PSR-0 as it was already not respected by the tarball containing Doctrine 2. The Doctrine/Symfony/ folder contains Symfony\... classes.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.