Java has jar files. PHP has phar files.
Those files are PHP projects packaged. You may know them through usages like composer or PHPUnit. But phar files were designed by taking into consideration the nature of PHP: the web.
Therefore, we're going to try to show how to build a phar either "classic" or "web" oriented in this tutorial.
What is a phar exactly?
A phar is basically many files assembled in only one. But it is composed of the following content:
- A stub: this is the very first piece of code that will be executed when you run your phar. It can be either automatically generated or entirely customized.
This part in the file usually starts with<?php
and ends with__HALT_COMPILER();
.
The stub is some kind of pre-front-controller. It will drive PHP to get the correct thing depending on the "entry". For that to work, it uses the Phar API. - Some binary containing metadata of the package: filenames, their start/end in the current file, some information about the entry point...
- The concatenation of all your files (potentially compressed)
To generate the phar, we will need to make a builder script (in PHP) and we will use the Phar API.
Making a phar for the cli
Before you begin, you may need to enable a special capability in your php.ini
file. The following configuration may be set on your installation:
[Phar]
; http://php.net/phar.readonly
phar.readonly = On
; Set it to Off
It is required to set the option readonly
to Off
.
The goal will be to generate a file app.phar
that is executable from the cli directly. On my side I create a folder named app
that contains the two following files:
<?php
// app/index.php
// This will be the entry point
echo "<h1>Hello from PHP</h1>";
<?php
// app/vendor.php
function hello() {
echo "Im a function part of whatever vendor you may think.";
}
I will then create my builder that uses the class Phar. Here is how to process, I let you go to the documentation to learn more.
<?php
// builder.php
$pharFile = 'app.phar';
// clean up
if (file_exists($pharFile))
{
unlink($pharFile);
}
$phar = new Phar($pharFile);
// First thing to do is to start the buffering
// otherwise no other action will be possible.
$phar->startBuffering();
// Here I get get the default stub for .phar files
// it's allowed to customize it entirely, but it's
// recommended only for advanced usage.
// I also specify my entry point (the front controller)
$defaultStub = $phar->createDefaultStub('index.php');
// Let's all the rest of the files
$phar->buildFromDirectory(__DIR__ . '\\app');
// Customizing the stub
// What we add here allow to execute the file
// without a call to PHP with an unix OS
// *it will not work on windows* (but will not be a problem either)
$stub = "#!/usr/bin/env php \n" . $defaultStub;
// Add the stub
$phar->setStub($stub);
// Generating the file
$phar->stopBuffering();
// Some compression option are available.
// Most common are GZ and ZIP. (for many obvious reason)
// And yes, PHP do it afterwards so it must be at the end.
$phar->compressFiles(Phar::GZ);
# Make the file executable
chmod(__DIR__ . '/app.phar', 0770);
If you run this script, you should see a file app.phar
generated. Success!
Let's use this app.phar. Under Linux, as stated in the comments you should be able to use it this way:
./app.phar
If you use Windows, you need to call directly PHP this way:
php app.phar
And finally, if you want to use this phar as a library, you can use the following code:
<?php
require_once 'phar://app.phar/vendor.php';
hello();
Making a phar file for the web
That was easy, isn't it? Let's take this to the next level. If you want to make a phar file ready for the web it's possible.
If you take the last example, just remove the custom stub and execute the phar file with the following command:
php -S localhost:8000 app.phar
Then go to https://localhost:8000, the magic should happen. But there's more!
What is a website without assets? Yes, you can add them into your archive, but everything will pass through your front controller (in my case, index.php
). So your code must require the assets when PHP asks for them. Here is an example of support for PNG.
<?php
// current file is index.php, the entry point
function index() {
echo "<h1>Hello from PHP</h1>";
echo '<img src="/images/some-image.png" />';
}
// index.php is now mostly a router (a real front controller if you prefer)
switch ($_SERVER['REQUEST_URI'] ?? '/') {
case '/':
case '/index.php':
index();
break;
default:
// PHP will look inside the phar!
if (file_exists(__DIR__ . $_SERVER['REQUEST_URI'])) {
// This simple example is crap, but you got the idea.
if (str_ends_with($_SERVER['REQUEST_URI'], 'png')) {
header('Content-Type: image/png');
}
require __DIR__ . $_SERVER['REQUEST_URI'];
exit;
}
http_response_code(404);
echo "<h1>The content does not exists";
}
If you really want to do something like this, I suggest you to use the component HttpFoundation of Symfony with its router or some vendor that will do the job for you. (yes, you can add vendors to your folder, it's PHP, it will just work fine)
Finally, I wanted to introduce you to something you can't do: edit a file inside the phar. For example, the following code will actually work:
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'tmp.txt', 'yolo');
But it modifies the phar file itself and corrupts it definitely. Thus you simply can't write a file in the current directory.
Other ways to generate a phar
Let me add one last thing. In every test until now, I suggested you to create a folder and add this one to a file. But there are actually 2 other methods to make a phar.
A phar based on an archive
If you have a zip (or gz or whatever) file, no problem. PHP can create a phar from it. You just need to tell PHP at the instantiation of the phar:
<?php
@unlink('app.phar');
copy('app.tar.gz', 'app.phar');
$phar = new Phar('app.phar');
A phar based on nothing
You can generate the phar file entirely from your script, here is an example:
<?php
$phar = new Phar('app.phar');
$phar->startBuffering();
$phar['index.php'] = '<?php include "config.php"; echo $username;';
$phar['config.php'] = '<?php $username = "Nek";';
// ... You already know the end
You can also combine all the methods and finally override some files with this last one.
Hope you liked it! Find out something missing? Feel free to comment!
Follow us on Twitter! https://twitter.com/swagdotind