Home > Article > Backend Development > PHP compression and archiving—Phar
Phar The concept of archive comes from Java™ technology's JAR archive, which allows applications to be packaged with a single file that contains everything needed to run the application. This file differs from a single executable file, which is typically generated by a programming language such as C, because the file is actually an archive file rather than a compiled application. So the JAR file actually contains the files that make up the application, but these files are not carefully differentiated for security reasons. The Phar extension is based on a similar concept, but is designed primarily for PHP's web environment. Also, unlike JAR archives, Phar archives can be processed by PHP itself, so no additional tools are required to create or use them.
Phar extensions are not a new concept to PHP. It was originally written in PHP and named PHP_Archive before being added to the PEAR library in 2005. In practice, however, pure PHP solutions to this problem were very slow, so in 2007 it was rewritten as a pure C language extension and support for traversing Phar archives using SPL's ArrayAccess objects was added. Since then, a lot of work has been done to improve the performance of Phar archives.
Creating a Phar
Creating a Phar file requires several steps. All steps require some form of PHP command to complete the creation, as there is no standalone tool for creating archives. Additionally, to create and modify Phar files, the php.ini setting phar.readonly must be set to 0. This setting is not required when opening and referencing files within PHP's Phar archive.
Let’s take a look at the steps required to create a Phar file that can be used to drive an application. The application is designed to be loaded directly from a web browser or command prompt. The first step is to create a Phar file, so we will create the Phar object shown in Listing 1. Object references will allow you to control all aspects of Phar archiving.
Example 1. Create a Phar object
$p = new Phar('/path/to/my.phar', CURRENT_AS_FILEINFO | KEY_AS_FILENAME, 'my.phar'); $p->startBuffering();
The first parameter of the constructor indicates the location where the Phar file is saved. The second parameter passes all parameters to the RecursiveDirectoryIterator parent class. The third parameter is an alias that references the Phar archive in the stream context. So for Listing 1, you can use phar://my.phar to reference the file in this Phar archive. You can also issue a Phar::startBuffering() method call to buffer modifications to the archive until a Phar::stopBuffering() command is issued. Although it is not necessary to do the above, doing so does improve the performance of creating or modifying archives because it avoids having to save the modifications each time the archive is modified in a script.
By default, the created Phar will use the native Phar-based archive format. You can also use ZIP or TAR format with Phar files by converting the format to ZIP format as shown in Listing 2.
Example 2. Convert storage format to ZIP format
$p = $p->convertToExecutable(Phar::ZIP);
There are pros and cons to converting archive formats. The main advantage is the ability to view the contents of the archive using any tool that handles ZIP or TAR files. However, if a Phar archive does not use a native Phar-based archive format, it does not need to use the Phar extension to load the archive, as is required for Phar archives using ZIP or TAR format.
Next, you will need to define the file stub, which is the code that is first called when loading the Phar file.
Phar file stubs
A file stub is just a small part of the code that is initially run when a Phar file is loaded, and always ends with a __HALT_COMPILER() tag. Listing 3 shows a typical file stub.
Example 3. Phar file stub
<?php Phar::mapPhar(); include 'phar://myphar.phar/index.php'; __HALT_COMPILER();
The Phar::mapPhar() method call shown above initializes the Phar archive by reading the manifest file. You need to perform initialization using the phar:// stream wrapper before referencing the file within the archive. The file that is initially loaded will be the file when the application first loads; in this case, index.php.
How to add this file stub Phar to a Phar archive depends on the format of the archive used. For Phar-based archives, use the Phar::setStub() method, which will accept a single argument of PHP code and put it into the stub as a string. Listing 4 demonstrates this approach.
Example 4. Create a file stub using Phar::setStub()
$p->setStub('<?php Phar::mapPhar(); include 'phar://myphar.phar/index.php'; __HALT_COMPILER(); ?>');
If you plan to use a stub instead of redirecting to the index.php page to complete the operation, you can use the helper method Phar::createDefaultStub() to build the file stub. So just pass the name of the file you wish to include in the file stub. In Listing 5, the Phar::setStub() method call is overridden to use the helper method.
Example 5. Use Phar::createDefaultStub() to create a file stub
$p->setStub($p-> createDefaultStub('index.php'));
如果从 Web 服务器加载 Phar,Phar::createDefaultStub() 方法的第二个可选参数允许包含一个不同的文件。这对于设计用于命令行或 Web 浏览器上下文的应用程序非常方便。
对于基于 ZIP 和 TAR 的实现,将以上存根的内容存储到 .phar/stub.php 文件内,而不是使用 setStub() 命令。
将文件添加到归档
Phar 对象使用 ArrayAccess SPL 对象,允许以数组的形式访问归档内容,因此提供了许多方法来向归档添加文件。最简单的方法是直接使用 ArrayAccess 接口。
示例 6. 向归档添加文件
$p['file.txt'] = 'This is a text file'; $p['index.php'] = file_get_contents('index.php');
示例 6 表明文件名被指定为数组键,将内容指定为值。可以使用 file_get_contents() 函数获得现有文件的内容,然后将内容设为值。这样可以更加灵活地向归档添加文件,可以通过引用现有文件或动态构建文件实现。后一种方法可以作为应用程序构建脚本的一部分。
如果存储在 Phar 归档中的文件非常大,可以分别通过 PharFileInfo::setCompressedGZ() 或PharFileInfo::setCompressedBZIP2() 方法使用 gzip 或 bzip2 压缩有选择地压缩归档中的文件。在清单 7 中,您将使用 bzip2 压缩文件。
示例 7. 使用 bzip2 压缩 Phar 归档中的文件
$p['big.txt'] = 'This is a big text file'; $p['big.txt']->setCompressedBZIP2();
要压缩文件或使用包含压缩文件的归档,必须在 PHP 安装中支持 bzip2 或 zlib(用于 gz 压缩文件)扩展。
假设您需要将许多文件加入到归档中。使用 ArrayAccess 接口逐一添加文件是一项非常单调的工作,因此可以使用一些便捷的方法。一种方法就是使用 Phar::buildFromDirectory() 方法,该方法将遍历指定的目录并添加其中的文件。它还支持对添加的文件进行过滤,方法是使用文件的正则表达式模式传递第二个参数,以匹配文件并添加到归档中。清单 8 展示了这一过程。
示例 8. 使用 Phar::buildFromDirectory() 向归档添加文件
$p->buildFromDirectory('/path/to/files','./\.php$/');
示例 8 将指定目录中的 PHP 文件添加到 Phar 归档。如果需要对添加的文件执行任何修改,比如将文件压缩,那么可以使用ArrayAccess 接口返回。
可以使用一个迭代器(iterator)通过 Phar::buildFromIterator() 方法添加文件。支持两种风格的迭代器:一种是将 Phar 中的文件名映射到磁盘文件的名称,另一种是返回 SplFileInfo 对象。RecursiveDirectoryIterator 是一种兼容的迭代器,下面展示如何使用它向归档添加目录文件。
示例 9. 使用 Phar::buildFromIterator() 向归档添加目录文件
$p->buildFromIterator(new RecursiveIteratorIterator (new RecursiveDirectoryIterator('/path/to/files')),'/path/to/files');
Phar::buildFromIterator() 方法接受迭代器对象本身作为惟一的参数。在上例中,您已经使用RecursiveIteratorIterator 对象包装了 RecursiveDirectoryIterator 对象,RecursiveIteratorIterator 对象提供了Phar::buildFromIterator() 方法所需的兼容型迭代器。
我们现在已经创建了一个 Phar 归档,它可以用于任何 PHP 应用程序。让我们看一看如何方便地使用这个归档。
使用 Phar 归档
Phar 归档的一个优点就是可以非常方便地集成到任何应用程序中。如果使用的是原生的基于 Phar 的归档格式,这一点尤其明显。在这种情况下,您甚至不需要安装 Phar 扩展,因为 PHP 天生就可以加载文件并提取文件内容。基于 ZIP 和 TAR 的归档需要加载 Phar 扩展。
Phar 归档在设计时被包括到应用程序中,跟普通的 PHP 文件一样,这使得已经熟悉如何包含其他第三方代码的应用程序开发人员可以非常方便地使用 Phar 归档。让我们看一看在应用程序中集成 Phar 有多么容易。
在应用程序中集成 Phar 归档代码
在 Phar 归档中集成代码的最简单方法就是包含 Phar 归档,然后在 Phar 文件中包含需要使用的文件。phar:// 流包装器可以用来访问已加载 Phar 归档中的文件,如下所示。
示例 10. 在 Phar 归档中加载代码
include 'myphar.phar'; include 'phar://myphar.phar/file.php';
第一个 include 将加载 myphar.phar 归档,包含文件存根中指定的代码。第二个 include 使用流包装器打开 Phar 归档并且仅在归档中包括指定的文件。注意在归档中包含文件之前,您不需要包含 Phar 归档本身,如清单 10 所示。
从 Phar 归档运行 PHP 应用程序
A great feature of Phar archives is that you can use a single Phar archive to package your entire application and publish it. The advantage of this approach is that it simplifies application deployment without sacrificing performance, and it mainly benefits from several Phar enhancements added in PHP V5.3. However, the following points should be considered when designing applications to run in Phar:
Any application instance-specific files that need to be created, such as config files, cannot be part of the archive, so they need to be written to a separate But accessible location. If the application creates cache files that constitute extensions, the same applies to those files.
You should always use Phar-based archive formats and not compress the files in the archive for maximum flexibility. ZIP and TAR based archives require the Phar extension to be installed in PHP, while Phar based archives can be used even without the Phar extension installed.
Any file references in the application need to be modified to use both the phar:// stream wrapper and the archive name, as shown in the previous section.
PHPMyAdmin is a popular PHP application that has been packaged using Phar, demonstrating the ease of using Phar archiving. It has been designed to run from Phar archives, but is still capable of storing configuration files outside of Phar archives.