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:
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:
Point your browser to the run.php
script and you should see the following 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.