Phar 아카이브의 개념은 애플리케이션을 실행하는 데 필요한 모든 것이 포함된 단일 파일로 애플리케이션을 패키징할 수 있는 Java™ 기술의 JAR 아카이브에서 비롯되었습니다. 이 파일은 실제로 컴파일된 애플리케이션이 아니라 아카이브 파일이라는 점에서 일반적으로 C와 같은 프로그래밍 언어로 생성되는 단일 실행 파일과 다릅니다. 따라서 JAR 파일에는 실제로 애플리케이션을 구성하는 파일이 포함되어 있지만 이러한 파일은 보안상의 이유로 신중하게 구별되지 않습니다. Phar 확장은 비슷한 개념을 기반으로 하지만 주로 PHP의 웹 환경을 위해 설계되었습니다. 또한 JAR 아카이브와 달리 Phar 아카이브는 PHP 자체에서 처리할 수 있으므로 이를 생성하거나 사용하는 데 추가 도구가 필요하지 않습니다.
Phar 확장은 PHP의 새로운 개념이 아닙니다. 원래는 PHP로 작성되었으며 2005년 PEAR 라이브러리에 추가되기 전에 PHP_Archive라는 이름을 사용했습니다. 그러나 실제로 이 문제에 대한 순수 PHP 솔루션은 매우 느렸기 때문에 2007년에 순수 C 언어 확장으로 다시 작성되었으며 SPL의 ArrayAccess 개체를 사용하여 Phar 아카이브를 탐색하는 지원이 추가되었습니다. 그 이후로 Phar 아카이브의 성능을 개선하기 위한 많은 작업이 이루어졌습니다.
Phar 생성
Phar 파일을 생성하려면 여러 단계가 필요합니다. 아카이브 생성을 위한 독립 실행형 도구가 없으므로 모든 단계에서 생성을 완료하기 위해 어떤 형태의 PHP 명령이 필요합니다. 또한 Phar 파일을 생성하고 수정하려면 php.ini 설정 phar.readonly를 0으로 설정해야 합니다. PHP의 Phar 아카이브 내에서 파일을 열고 참조할 때는 이 설정이 필요하지 않습니다.
애플리케이션 구동에 사용할 수 있는 Phar 파일을 생성하는 데 필요한 단계를 살펴보겠습니다. 응용 프로그램은 웹 브라우저나 명령 프롬프트에서 직접 로드되도록 설계되었습니다. 첫 번째 단계는 Phar 파일을 만드는 것이므로 목록 1에 표시된 Phar 개체를 만듭니다. 객체 참조를 사용하면 Phar 아카이빙의 모든 측면을 제어할 수 있습니다.
예시 1. Phar 객체 생성
$p = new Phar('/path/to/my.phar', CURRENT_AS_FILEINFO | KEY_AS_FILENAME, 'my.phar'); $p->startBuffering();
생성자의 첫 번째 매개변수는 Phar 파일이 저장되는 위치를 나타냅니다. 두 번째 매개변수는 모든 매개변수를 RecursiveDirectoryIterator 상위 클래스에 전달합니다. 세 번째 매개변수는 스트림 컨텍스트에서 Phar 아카이브를 참조하는 별칭입니다. 따라서 목록 1의 경우 phar://my.phar을 사용하여 이 Phar 아카이브의 파일을 참조할 수 있습니다. Phar::stopBuffering() 명령이 실행될 때까지 아카이브 수정 사항을 버퍼링하기 위해 Phar::startBuffering() 메서드 호출을 실행할 수도 있습니다. 위의 작업을 반드시 수행할 필요는 없지만 이렇게 하면 스크립트에서 아카이브가 수정될 때마다 수정 사항을 저장할 필요가 없으므로 아카이브 생성 또는 수정 성능이 향상됩니다.
기본적으로 생성된 Phar는 기본 Phar 기반 아카이브 형식을 사용합니다. 목록 2에 표시된 대로 형식을 ZIP 형식으로 변환하여 Phar 파일에 ZIP 또는 TAR 형식을 사용할 수도 있습니다.
예 2. 저장 형식을 ZIP 형식으로 변환
$p = $p->convertToExecutable(Phar::ZIP);
아카이브 형식 변환에는 장점과 단점이 있습니다. 가장 큰 장점은 ZIP 또는 TAR 파일을 처리하는 도구를 사용하여 아카이브의 내용을 볼 수 있다는 것입니다. 그러나 Phar 아카이브가 기본 Phar 기반 아카이브 형식을 사용하지 않는 경우 ZIP 또는 TAR 형식을 사용하는 Phar 아카이브에 필요한 것처럼 Phar 확장을 사용하여 아카이브를 로드할 필요가 없습니다.
다음으로 Phar 파일을 로드할 때 처음 호출되는 코드인 파일 스텁을 정의해야 합니다.
Phar 파일 스텁
파일 스텁은 Phar 파일이 로드될 때 처음 실행되는 코드의 일부일 뿐이며 항상 __HALT_COMPILER() 태그로 끝납니다. Listing 3은 일반적인 파일 스텁을 보여줍니다.
예제 3. Phar 파일 스텁
<?php Phar::mapPhar(); include 'phar://myphar.phar/index.php'; __HALT_COMPILER();
위에 표시된 Phar::mapPhar() 메서드 호출은 매니페스트 파일을 읽어 Phar 아카이브의 초기화를 수행합니다. 아카이브 내의 파일을 참조하기 전에 phar:// 스트림 래퍼를 사용하여 초기화를 수행해야 합니다. 처음에 로드되는 파일은 애플리케이션이 처음 로드될 때의 파일이 됩니다. 이 경우 index.php입니다.
이 파일 스텁 Phar를 Phar 아카이브에 추가하는 방법은 사용된 아카이브 형식에 따라 다릅니다. Phar 기반 아카이브의 경우 Phar::setStub() 메서드를 사용하세요. 이 메서드는 PHP 코드의 단일 인수를 허용하고 이를 스텁에 문자열로 넣습니다. 목록 4에서는 이 접근 방식을 보여줍니다.
예제 4. Phar::setStub()을 사용하여 파일 스텁 생성
$p->setStub('<?php Phar::mapPhar(); include 'phar://myphar.phar/index.php'; __HALT_COMPILER(); ?>');
작업을 완료하기 위해 index.php 페이지로 리디렉션하는 대신 스텁을 사용하려는 경우 다음을 사용할 수 있습니다. 도우미 메서드 Phar:: createDefaultStub()는 파일 스텁을 빌드합니다. 따라서 파일 스텁에 포함하려는 파일 이름을 전달하면 됩니다. 목록 5에서는 Phar::setStub() 메서드 호출이 도우미 메서드를 사용하도록 재정의되었습니다.
예 5. Phar::createDefaultStub()를 사용하여 파일 스텁 생성
$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 应用程序
Phar 아카이브의 가장 큰 특징은 단일 Phar 아카이브를 사용하여 전체 애플리케이션을 패키징하고 배포할 수 있다는 것입니다. 이 접근 방식의 장점은 성능 저하 없이 애플리케이션 배포를 단순화한다는 점이며 주로 PHP V5.3에 추가된 여러 Phar 개선 사항의 이점을 누릴 수 있습니다. 그러나 Phar에서 실행할 애플리케이션을 설계할 때는 다음 사항을 고려해야 합니다.
구성 파일과 같이 생성해야 하는 모든 애플리케이션 인스턴스별 파일은 아카이브의 일부가 될 수 없으므로 별도의 접근 가능한 위치에 기록하십시오. 애플리케이션이 확장을 구성하는 캐시 파일을 생성하는 경우 해당 파일에도 동일하게 적용됩니다.
최대한의 유연성을 위해 항상 Phar 기반 아카이브 형식을 사용해야 하며 아카이브의 파일을 압축하지 않아야 합니다. ZIP 및 TAR 기반 아카이브를 사용하려면 PHP에 Phar 확장을 설치해야 하는 반면, Phar 기반 아카이브는 Phar 확장을 설치하지 않아도 사용할 수 있습니다.
이전 섹션에 표시된 대로 phar:// 스트림 래퍼와 아카이브 이름을 모두 사용하도록 애플리케이션의 모든 파일 참조를 수정해야 합니다.
PHPMyAdmin은 Phar를 사용하여 패키징된 인기 있는 PHP 애플리케이션으로, Phar 아카이빙 사용의 용이성을 보여줍니다. Phar 아카이브에서 실행되도록 설계되었지만 여전히 Phar 아카이브 외부에 구성 파일을 저장할 수 있습니다.