Skip to content

Lessons in Phar

Dear Reader,

In preparation of an episode of boxlunchtraining.com I have been doing a lot of research into creating and using PHP’s new phar archives. While I’ve not answered all my questions yet, I’ve been able to do most of what I wanted. One of the tasks I wanted to complete was to create a phar archive for Zend Framework. Here is a very quick intro into how I did it. Most of this is just a reminder for me but if you find it useful then I’m glad.

Zend Framework

First, we need a copy of Zend Framework. Luckily, I keep a recent version checked out on my laptop at all times. This zf.phar was built from svn checkout #16836.

Phar

You have to have PHP 5.3 installed for this to work. While it should be possible to use it using PHP 5.2.x and the ext/phar, I’ve not tested that. Also, I do a lot of work from the cli. I’ve tested this phar from the cli but not from a web app yet. Theoretically, it should work just fine. I’m just warning you if it doesn’t.

package.php

I’ve now built 2 phar files and learned a lot about what works and what doesn’t. The one thing I learned, that is not explicitly stated in the manual, is that each project you are going to archive in a phar, will need a program to package it up. I call mine, package.php. Each one is a little different. I still think I can use phing to standardize them. However, for now, I’m building them by hand.

< ?php
/**
 * package.php
 * Create a Zend Framework par
 *
 * @author Cal Evans <cal@calevans.com>
 */
 
/*
 * change these to match your setup.
 */
$zfLocation    = "c:\zf-full\library\Zend";
$zfBasePointer = strpos($zfLocation,'Zend');
 
/*
 * Let the user know what is going on
 */
echo "Creating phar for Zend Framework located at ".$zfLocation."\n";
/*
 * Clean up from previous 
 */
if (file_exists('zf.phar')) {
    Phar::unlinkArchive('zf.phar');
}

If you’ve run this before, make sure and delete the archive before starting again.

 
/*
 * Setup the phar
 */
$p = new Phar('zf.phar', 0, 'zf.phar');
$p->compressFiles(Phar::GZ);
$p->setSignatureAlgorithm (Phar::SHA1);

Now setup the phar object. In this case, I want to use gzip compression and the SHA1 hash signature.

/*
 * Now build the array of files to be in the phar.
 * The first file is the stub file. The rest of the files are built from the directory.
 */
$files = array();
$files['stub.php']='stub.php';
 
$rd = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($zfLocation));
foreach($rd as $file) {
    if (strpos($file->getPath(),'.svn')===false &&
        $file->getFilename() != '..' &&
        $file->getFilename() != '.') {
        $files[substr($file->getPath().DIRECTORY_SEPARATOR.$file->getFilename(),$zfBasePointer)]=$file->getPath().DIRECTORY_SEPARATOR.$file->getFilename();
    }
}

There are several ways to add files to an archive. After experimenting with all of the ones I could find, i decided that the fastest way to do it was build the list of files I want to add to the archive in an array and then add the array to the archive. If you don’t need to do any pre-processing or want to accept everything in a directory, you can use buildFromDirecotry(). In this case however, I needed to strip out all of the subversion files.

/*
 * Now build the archive.
 */
$p->startBuffering();
$p->buildFromIterator(new ArrayIterator($files));
$p->stopBuffering();

Once I had my array built, it was a simple matter of calling buildFromIterator() and passing in an instance of ArrayIterator() with the files array. The manual recommended turning on buffering using startBuffering and stopBuffering() for large archive, however, I didn’t notice a sizable difference in speed.

/*
 * finish up.
 */
$p->setStub($p->createDefaultStub('stub.php'));
$p = null;

Finally, we finish processing.

Stub.php

You probably noticed that the phar has a stub. To get the autoloaded running automatically, I put the autoload code in a file called stub.php and included it in the phar.

< ?php
require_once dirname(__FILE__).'/Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
Zend_Loader_Autoloader::getInstance()->setFallbackAutoloader(true);
__HALT_COMPILER();

Stubs have to end with __HALT_COMPILER();.

Conclusion

Once you’ve built your zf.phar, using is as simple as:

include 'phar://zf.phar';

Now you have the complete Zend Framework in a handy archive. Like I said, this was a quickie. I just needed to get my thoughts down before I forget them. If you have additions to this or if I got something wonr, ge make sure and leave me a comment.

Until next time,
(l)(k)(bunny)
=C=

17 thoughts on “Lessons in Phar

  1. @beberlei,

    I’ve not tested that to know for sure. I do know that the sample application I wrote, a command line twitter status updater ran almost as fast with the phar as it did with just the regular zend framework. The difference seemed to be a one second lag. that could have been when phar opened the phar file.

    If you find a definitive answer, please make sure and post and let us all know.

    =C=

  2. does it load all the code in zf.phar at once then? or is include ‘phar://zf.php’; just importing all the contents into the include path?

  3. It looks like by including a phar file, it automatically prepends the phar’s root to the include_path.

    e.g.
    include ‘/path/to/phar.phar’;

    echo get_include_path();

    returns phar:///path/to/phar.phar:.:/usr/share/pear

    The Autoloader then hooks into the php autoload functions to translate class names into paths which it includes when the class is referenced

  4. Sorry, also forgot to mention a problem with the above script on linux. Since in linux . and .. are referenced as real files, the DirectoryIterator contains them, causing an error.

    I modified the if condition in the foreach to ignore these files as well:

    if (strpos($file->getPath(),’.svn’)===false && $file->getFilename() != ‘..’ && $file->getFilename() != ‘.’) {

  5. @beberlei: No, it wont include the whole library. It will call the “stub” (something like a bootloader/index file of the archive) you have to define for a phar archive. So “include ‘phar://zf.phar’;” will actually call “include ‘phar://zf.phar/stub.php’;” in the given example.

  6. @andrew,

    Thanks. Since I wrote it on Windows and had not yet tested on Linux, I did not see that issue. I’ve updated the sample code.

    @phil,

    Thanks for the clarification. I knew that in the back off my head but it just didn’t trigger when Beberlei asked. You are correct, it only loads and fires the stub at include time. the rest waits intil it is called for.

    =C=

  7. @Andrew,
    use $file->isDot() like php.net/manual/en/directoryiterator.isdot.php.

    Cheers, alex

  8. I am curious if there is a performance penalty due to the uncompression of the included files ?

  9. @alex,
    I added isDot() in but since I’m testing on Windows, I can’t test it. Also, you have to use $r->isDot() instead of $file->isDot() since $file is a SplFileInfo class and does not have the isDot() method.

    Once I can confirm it works on Linux, I’ll update the sample code.

    @Slavi,
    I’ve not experienced one yet but my simple test app just uses one class. it find it pretyt quick though.

    @Andy,
    Yes and no. the only time I ran into that was when I was using the array interface for adding files to the phar, the way I add stub.php. In that case, you have to open a handle to the file and attach the handle to the array. This, of course, will cause you to run out of file handles very quickly.

    Using the ArrayIterator, you only have to give it the name you want it known by, i.e. the array key is ‘Zend/Service/Twitter.php’ and then the full path to the file ‘c:ZendServiceTwitter.php’. I was using a standard Windows XP when I wrote that and ran it without a problem.

    I am not pharing up the entire Zend Framework trunk though, only the library (which does include Zend Tool)

    Thanks for the comments!

    =C=

  10. I have also been playing around with Phar for the ZF on Windows, but I ran in to an issue with the number of files I was trying to include. When trying to include all of ZF it kept throwing an exception, but when I ruled out some components (such as Tool and CodeGenerator) then the Phar was created just fine. did you not experience anything like that, Cal?

    Slavi; when I ran a full application (with lots of components being used) with ZF as normal and then as a Phar document (without APC or any opt-code caching), it was only about 20ms slower using the Phar file (which included the default stub file as generated by createDefaultStub() and all of the included files being gzipped).

  11. nice, but try to make phar for zend framework based application, this is real challenge, one single “executable” phar file with all necessary files, all your application php files, images, js files and some necessary modules from ZF in one phar. This is what I want to archive with ZF and phar, super easy deployment

  12. @optik,

    It is possible. In another example I built for the same episode I actually packages a small (non Zend Framework) application up to be able to distribute it. So yes, it is possible, but it wasn’t the point of this demo. This demo was about packaging up the library for ease of distribution.

    Thanks for the comment,
    =C=

  13. very nice ! I’m waiting for APC

    I think you can use $file->getPathname() instead of $file->getPath().DIRECTORY_SEPARATOR.$file->getFilename()

  14. @xorax

    Sorry, didn’t see your suggestion before publishing the latest update. I’ll look into using getPathname(). Thanks.

    What do you mean “waiting for APC”?

    =C=

  15. Pingback: Packaging Zend Framework As A Phar Revisited – techPortal
  16. Currently I tried to create the phing builder as the phar archive. Still problems with the paths relative to the place where the command line was executed. For example build.xml is not found if not specifying it’s absolute path in the system by -f attribute.

    Is there any quick fix for this?

Comments are closed.