Packaging Your Apps with Phar

Abdullah Abouzekry
Abdullah Abouzekry
Share

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

Frequently Asked Questions (FAQs) about Packaging Your Apps with PHAR

What is the PHAR file format and why is it important in PHP?

PHAR (PHP Archive) is a file format that allows for the packaging of complete PHP applications into a single file for easy distribution and installation. It’s similar to the JAR file format in Java. PHAR files are executable and can be run directly from the command line, making them extremely convenient for distributing PHP applications. They can include PHP files, images, text files, and other types of files needed for the application to run.

How do I create a PHAR file?

Creating a PHAR file involves a few steps. First, you need to create a PHP script that includes all the files you want to package into the PHAR file. Then, you use the Phar class in PHP to create the PHAR file and add the files to it. Finally, you set the stub file, which is the file that will be run when the PHAR file is executed.

Can I add non-PHP files to a PHAR file?

Yes, you can add any type of file to a PHAR file, not just PHP files. This includes images, text files, and other types of files. This makes PHAR files extremely versatile for packaging and distributing PHP applications.

How do I execute a PHAR file?

PHAR files can be executed directly from the command line by using the PHP interpreter. You simply type “php” followed by the name of the PHAR file. The PHP interpreter will execute the stub file in the PHAR file, which in turn can include and run other files in the PHAR file.

Are PHAR files secure?

PHAR files can be signed with a digital signature to ensure their integrity. This means that if the PHAR file is modified in any way after it has been signed, the signature will no longer be valid and the PHAR file will not run. This provides a level of security for PHAR files, but like any other file, they can still be a target for malicious activity if not properly secured.

Can I compress PHAR files?

Yes, PHAR files can be compressed using either gzip or bzip2 compression. This can significantly reduce the size of the PHAR file, making it easier to distribute. The PHP Phar class provides methods for compressing and decompressing PHAR files.

Can I extract files from a PHAR file?

Yes, you can extract files from a PHAR file using the Phar class in PHP. This can be useful if you need to access individual files in the PHAR file without executing it.

Can I add files to an existing PHAR file?

Yes, you can add files to an existing PHAR file using the Phar class in PHP. This can be useful if you need to update the contents of a PHAR file without creating a new one.

Can I delete files from a PHAR file?

Yes, you can delete files from a PHAR file using the Phar class in PHP. This can be useful if you need to remove unnecessary or outdated files from a PHAR file.

Can I run PHAR files on any server?

PHAR files can be run on any server that has PHP installed. However, the server must have the Phar extension enabled in order to execute PHAR files. This is usually the case by default, but if not, you may need to enable it manually.