PHP - - By Abdullah Abouzekry

Packaging Your Apps with Phar

Deployment of web applications can be difficult and cumbersome if you don’t have the right tools. If you’ve ever deployed a Java application before, I’m sure you’ve heard of JAR files (which stands for “Java ARchive”). Everything executable/accessible that makes up the application can be bundled in a single JAR file, which is a blessing when it comes time to deploy.

PHAR (“Php ARchive”) is analogous to the JAR file concept but for PHP. If you have PHP 5.3 or greater, the Phar extension is built-in and enabled; you can start using it without any additional requirements.

This article is intended to shed some light on this important feature for those who haven’t used it before. Hopefully you’ll find it a very helpful tool and have a better and faster deployment experience.

PHAR files are treated as read-only by default, and you can use any PHAR file without any special configuration. This is great for deployment. But as you’ll be creating your own PHARs you’ll need to allow write-access which is done through the php.ini file.

Open php.ini, find the phar.readonly directive, and modify it accordingly:

phar.readonly = 0

Now you’re ready to package your libraries and applications as PHARs.

Your First PHAR

Begin by creating the application’s directory structure; somewhere on your system, create the following:

application directory structure

The build directory will hold the PHAR package when it’s created to avoid polluting the source tree with generated artifacts. The src directory holds the source files that make up the application.

index.php will serve as the application’s entry point, common.php will be a pseudo-library of common classes used by the application, and config.ini will be the configuration file for the application.

The contents of index.php looks like:

<?php
require_once "phar://myapp.phar/common.php";
$config = parse_ini_file("config.ini");
AppManager::run($config);

The contents of common.php looks like:

<?php
class AppManager
{
    public static function run($config) {
         echo "Application is now running with the following configuration... ";
         var_dump($config);
     }
}

And the contents of config.ini looks like:

[database]
host=localhost
db=dbname
user=myuser
pass=dbpass

Creating the PHAR

Besides the application structure, you also need a script to generate the PHAR archive. Create a new PHP file named create-phar.php in your myapp root with the following code:

<?php
$srcRoot = "~/myapp/src";
$buildRoot = "~/myapp/build";
 
$phar = new Phar($buildRoot . "/myapp.phar", 
	FilesystemIterator::CURRENT_AS_FILEINFO |     	FilesystemIterator::KEY_AS_FILENAME, "myapp.phar");
$phar["index.php"] = file_get_contents($srcRoot . "/index.php");
$phar["common.php"] = file_get_contents($srcRoot . "/common.php");
$phar->setStub($phar->createDefaultStub("index.php"));

copy($srcRoot . "/config.ini", $buildRoot . "/config.ini");

Then open a terminal window, navigate to the myapp directory and run it:

aabouzekry@platinum:~/myapp$ php create-phar.php

After running the script, you should find the myapp.phar archive in the build directory along with a copy of config.ini file. Copy these two files to your web server’s document root directory (e.g. htdocs).

To access the Phar packaged application you could call the archive directly, but this is not recommended and may require additional configuration of your web server to forward PHAR files to the correct PHP handler. Another approach is to create a run script which includes the archive.

Create a run script called run.php in your web server’s document root with the following:

<?php
require "myapp.phar";

The code does nothing but bypass the hassle of reconfiguring your server to handle PHAR files directly. If you’re hosting your application in a shared hosting environment and don’t have access to your server’s configuration, then this is a perfect solution as well!

After creating the runner, your web root should look like this:

files in document root

Point your browser to the run.php script and you should see the following output:

browser output

Behind the Curtains

Let’s take a closer look at the create-phar.php code to see what it all means. Jave a look at the following line from it:

<?php
$phar = new Phar($buildRoot . "/myapp.phar", 
        FilesystemIterator::CURRENT_AS_FILEINFO |
        FilesystemIterator::KEY_AS_FILENAME, "myapp.phar");

A new Phar object is created which typically takes three arguments. The first argument is the path of the archive file to manipulate. Not only can you create new archives, but you can also manipulate existing ones.

The second argument is a flag to set how the object will handle files. The Phar object subclasses the PHP RecursiveDirectoryIterator class and this argument is simply passed to the parent class. The argument I provided is the default for RecursiveDirectoryIterator anyway, which is fine for now.

The third argument is the alias for the archive which will be used internally when referring to itself using the phar stream wrapper. In other words, all files inside the archive which require including other files from the archive should refer to it explicitly using the stream wrapper and the alias. For example, the code from index.php earlier references common.php file in this manner.

<?php
require_once "phar://myapp.phar/common.php";

After the object is created, index.php and common.php are added to the archive. The code treats the Phar object now as an associative array to specify the contents of the files keyed by their filenames, and file_get_contents() is used to read the file from disk. You can add as many files as you like similarly, but if you need to add a lot of files or an entire directory then you may want to consider using the more convenient buildFromDirectory() method.

<?php
$phar->buildFromDirectory("/path/to/dir",'/.php$/');

The first argument to buildFromDirectory() is the path to the desired directory and the second is an optional regular expression specifying which files to include. To include all of the files within the directory, just skip the second argument.

The setStub() method is called to create the stub file. A stub file tells the archive what to do when its loaded by the compiler.

Finally, the config.ini is copied from the src directory to the build directory alongside the archive.

The Stub File

When you run a PHAR archive, the stub inside is treated as a meta file to initialize the archive and tell it what to do when its called without referring to a particular file. In the example, I left the creation of the stub file up to the Phar object with the createDefaultStub() method which created a default stub containing the following code:

<?php
Phar::mapPhar();
include "phar://myapp.phar/index.php";
__HALT_COMPILER();

The default stub generated with createDefaultStub() illustrates little more than the minimum requirements. Phar::mapPhar() populates the meta-data of the archive and initializes it, and your stub file should end with a call to __HALT_COMPILER() with no trailing white space. This function halts further execution by the PHP interpreter at this point, allowing the possibility of including data after it without the risk of it being executed. This is a requirement by the Phar wrapper, without which the archive won’t work at all.

Alternatively, you can create your own stub file to perform custom initializations of your PHAR archive and read it in like so:

<?php
$phar->setStub(file_get_contents("stub.php"));

Taking Phar Seriously

If you’re developing a class library or other includable code, putting it in a PHAR archive is a neat and tidy solution to distribute it to multiple projects. The Phar extension has been highly optimized to give the same performance if not better than normal file access, so it’s likely you will not compromise the performance of your own application by using it.

To use Phar effectively though you should be aware it does have some limitations. Here are a few tips to help you better understand Phar and get the most out of it:

  • You can package a whole application in a PHAR, but it is not an auto-deploy solution. It’s a single-file access method for the application.
  • You can perform auto-deployment functionality manually inside the archive allowing for more robust deployments, like creating cache and upload directories on the sever, generating common configuration files, etc.
  • You should avoid back-writing to Phars in a real-world deployment. Instead, put all writeable files outside the archive. Writing to the PHAR is disabled in a standard PHP installation because it’s insecure for your application to do so.

Summary

Phar can save you a lot of hassle in packaging and deploying your applications and I recommend you consider using it. This article was intended to introduce you to the main concepts of Phar. You saw how to create the archive and include files, learned a bit about the importance of the stub file, and how PHP can access files within the archive. Unfortunately the resources for working with Phar in the PHP manual are incomplete and the limited amount of resources elsewhere on the Internet aren’t helpful enough. Hopefully in future articles I’ll be able to show you more about leveraging Phar.

Image via Pavel Ignatov / Shutterstock

Sponsors
Login or Create Account to Comment
Login Create Account