As stated in the PHP Manual, "the phar extension provides a way to put entire PHP applications into a single file called a "phar" (PHP Archive) for easy distribution and installation." When creating a PHAR file, you are therefore expected to include all necessary files in order to get your application up and running without having to install anything else.

Initial Considerations

Thus, when starting off with PHAR files, there are two things to keep in mind:

  1. how to preserve links to included PHP libraries intact within your source files and,
  2. which files must be included in your PHAR file.

Indeed, the first issue is breaking any 'INCLUDE' or 'REQUIRE' URI links from within your PHP source files. The reason for this is due to the fact that the PHAR file will not have access to any other files than those that are included in your archive file. Therefore, when programming an application that is destined to be packaged as a PHAR file, one should remember that the relative paths to the included or required files, such as the 'VENDOR' folder for example, will change once the PHAR file is running.

The easy solution to this problem is to define your base directory depending on the environment within which you are running your code. Here is a simple way to check if you are running the code from within the PHAR file or not:

// index.php
<?php

$path = dirname(\Phar::running(false));

if (strlen($path) > 0) {
    define('BASEDIR', \Phar::running());
} else {
    define('BASEDIR', dirname(__FILE__));
}

You can then easily define a constant that will point to the 'VENDOR' folder depending on the runtime environment:

// index.php
// [...]
define(
    'VENDORFOLDER',
    BASEDIR
    . DIRECTORY_SEPARATOR
    . 'vendor'
);

This way, you can easily include the required autoload files independently of the fact that you are running the code from within the PHAR file or not:

// index.php
// [...]
require VENDORFOLDER . DIRECTORY_SEPARATOR .'autoload.php';

// your PHP source code then goes here

The second issue you might encounter when creating a PHAR file is to include unnecessary files within the archive. This will make your PHAR file bigger in size and will make distribution more difficult for nothing.

The solution to this problem is to create a new RecursiveIteratorIterator that will use RecursiveCallbackFilterIterator to filter out anything that is returned by a RecursiveDirectoryIterator and that matches any value contained within an array of excluded file or directory names. Here is an example:

// build/script_that_builds_the_phar_file.php
<?php

$exclude = [
    'bin',
    'build',
    'data',
    'docs',
    'docs-api',
    'tests',
    '.gitattributes',
    '.gitignore',
    '.travis.yml',
    'composer.json',
    'composer.json.dev',
    'composer.lock',
    'composer.phar',
    'phpcs.xml',
    'phpdoc.xml',
    'phpunit.xml.dist',
    'README.md',
    '.git',
    '.idea',
];

$filter = function ($file, $key, $iterator) use ($exclude) {
    if ($iterator->hasChildren() && !in_array($file->getFilename(), $exclude)) {
        return true;
    }
    return $file->isFile() && !in_array($file->getFilename(), $exclude);
};

$innerIterator = new RecursiveDirectoryIterator(
    BASEDIR,
    RecursiveDirectoryIterator::SKIP_DOTS
);

$iterator = new RecursiveIteratorIterator(
    new RecursiveCallbackFilterIterator($innerIterator, $filter)
);

With these initial considerations out of the way, let's create our first PHAR file!

Setting up the build process

We are now ready to build the PHAR file. We will simply recursively include all our project's files while excluding those that are contained within our previously defined array. Inside the PHP script that will build the PHAR file (build/script_that_builds_the_phar_file.php in our example), please add these lines of code:

// build/script_that_builds_the_phar_file.php

// [...]

/*
* We define the base directory with the presumption that we are within the 'build'
* subdirectory of our project
*/
define('BASEDIR', dirname(dirname(__FILE__)));
define('BUILDDIR', BASEDIR . DIRECTORY_SEPARATOR . 'build');

$phar = new \Phar(mynewpharfile.phar, 0, mynewpharfile.phar);
$phar->setSignatureAlgorithm(\Phar::SHA1);
$phar->startBuffering();
$phar->buildFromIterator($iterator, BASEDIR);
$phar->setStub(file_get_contents(BUILDDIR . DIRECTORY_SEPARATOR . 'stub.php'));
$phar->stopBuffering();

Then, create the stub file that is used on line 16 of the previous code snippet. The stub file should look something like this:

// build/stub.php
#!/usr/bin/env php
<?php

Phar::mapPhar();
require_once "phar://mynewpharfile.phar/index.php";
__HALT_COMPILER();

The last line of the stub file tells the PHAR builder to stop buffering and compiling the new file, and output it to the filesystem. And, don't forget to add the 'shebang' command on the first line of the stub file if you wish to make the PHAR file immediately executable in a UNIX/Linux/Mac environment!

Building the PHAR file

Now, in your favorite terminal window, enter the following command (assuming that the current working directory is your project directory):

php build/script_that_builds_the_phar_file.php

Congrats! You have just built your first PHAR file!

Have a lot of fun!


Loading Conversation