背景
在 PHP 开发中,我们经常需要使用各种第三方包来加速开发进程。然而,在某些情况下,我们可能会遇到以下问题:
- 项目依赖的某个包需要更新,但直接更新可能会破坏现有代码的兼容性
- 我们需要将自己的项目打包成一个独立的文件,方便分发给其他开发者使用
- 我们希望在不影响现有项目的情况下,测试某个包的新版本
Phar (PHP Archive) 文件是一种将多个 PHP 文件和资源打包成单个文件的格式,它可以帮助我们解决上述问题。通过将项目或库打包成 Phar 文件,我们可以:
- 保持依赖版本的稳定性
- 方便地分发和部署应用程序
- 在不影响现有项目的情况下测试新的包版本
本工具提供了一个简单的方式来创建自定义的 Phar 文件,无需强制更新现有依赖。
代码实现
以下是 builder.php
文件的完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| <?php
class Builder { /** * @var Phar */ protected $phar; /** * @var string[] */ protected $exclude = [ 'composer.lock', '.idea', '.git', '.vscode' ]; /** * @var string */ protected $index = 'index.php';
/** * 构造函数 * @param string $fname 要创建的 Phar 文件路径 */ public function __construct($fname) { // 如果文件已存在,则删除它 if (is_file($fname)) { unlink($fname); } // 创建新的 Phar 对象 $this->phar = new \Phar($fname, FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME); }
/** * 创建自动加载文件 * 如果入口文件不存在,则创建一个包含自动加载代码的入口文件 */ public function autoloadfile() { if (!is_file($this->index)) { $text = '<?php' . PHP_EOL; $text .= 'if (is_file(__DIR__ . \'/vendor/autoload.php\')) {' . PHP_EOL; $text .= 'require __DIR__ . \'/vendor/autoload.php\';' . PHP_EOL; $text .= '}'; file_put_contents($this->index, $text); } }
public function buildExcludeRegex() { $this->exclude[] = __FILE__; $escaped = array_map(function ($v) { return preg_quote($v, '/'); }, $this->exclude); // 构造正则:排除匹配到任意一个文件名 return '/^(?!.*(' . implode('|', $escaped) . ')).*$/i'; }
/** * 构建 Phar 文件 * @param string $path 要打包的目录路径 */ public function build($path) { // 确保自动加载文件存在 $this->autoloadfile(); // 开始缓冲 Phar 写入操作 $this->phar->startBuffering(); // 从指定目录构建 Phar 文件,排除匹配正则表达式的文件 $pattern = $this->buildExcludeRegex(); $this->phar->buildFromDirectory($path, $pattern); // 设置 Phar 文件的存根(入口点) $this->phar->setStub($this->phar->createDefaultStub($this->index)); // 停止缓冲并写入 Phar 文件 $this->phar->stopBuffering(); } }
if (PHP_SAPI !== 'cli') { fwrite(STDERR, "错误:此脚本必须通过命令行执行!\n"); exit(1); } $pharFile = basename(__DIR__); if (isset($argv[1])) { $pharFile = $argv[1]; } $filename = $pharFile . '.phar'; (new Builder(__DIR__ . DIRECTORY_SEPARATOR . $filename))->build(__DIR__);
|
本地运行生成 Phar 文件
步骤 1: 准备独立SDK(guzzlehttp/guzzle)
1 2
| mkdir guzzle && cd guzzle composer require guzzlehttp/guzzle:^7.0
|
步骤 2:
将上述代码保存为 build.php
文件,并放置在您要打包的 PHP 项目根目录下。
步骤 2: 运行构建脚本
打开终端,导航到项目根目录,运行以下命令:
1 2 3 4 5
| php -d phar.readonly=0 build.php
php -d phar.readonly=0 build.php guzzle
|
步骤 3: 验证生成的文件
运行成功后,您会在项目根目录下看到生成的 .phar
文件(如 guzzle.phar
)。
使用 Phar 文件
生成 Phar 文件后,您可以通过以下方式使用它:
包含到其他项目中
您可以在其他 PHP 项目中包含这个 Phar 文件:
1 2 3 4 5 6 7 8 9
| <?php require 'guzzle.phar'; use GuzzleHttp\Client; $client = new Client();
$response = $client->request('GET', 'https://www.zhiqiang.wang');
$statusCode = $response->getStatusCode(); echo "Status code: $statusCode\n";
|
作为命令行工具使用
如果您的 Phar 文件设计为命令行工具,可以通过以下方式运行:
注意事项
- 运行脚本时需要禁用
phar.readonly
选项(使用 -d phar.readonly=0
) - 确保您有写入当前目录的权限
- 如果您的项目有特殊的自动加载需求,可能需要手动修改生成的
index.php
文件 - Phar 文件打包后,原始代码的路径结构会被保留,因此请确保您的代码中使用的是相对路径
扩展建议
- 添加更多命令行参数选项(如指定排除文件列表、压缩级别等)
- 支持自定义入口脚本内容
- 添加版本号和元数据信息到 Phar 文件中
- 实现增量构建功能,只打包修改过的文件