Phar 构建工具

chihqiang chihqiang 2025-08-07 约 11 分钟 4040 字

背景

在 PHP 开发中,我们经常需要使用各种第三方包来加速开发进程。然而,在某些情况下,我们可能会遇到以下问题:

  1. 项目依赖的某个包需要更新,但直接更新可能会破坏现有代码的兼容性
  2. 我们需要将自己的项目打包成一个独立的文件,方便分发给其他开发者使用
  3. 我们希望在不影响现有项目的情况下,测试某个包的新版本

Phar (PHP Archive) 文件是一种将多个 PHP 文件和资源打包成单个文件的格式,它可以帮助我们解决上述问题。通过将项目或库打包成 Phar 文件,我们可以:

  • 保持依赖版本的稳定性
  • 方便地分发和部署应用程序
  • 在不影响现有项目的情况下测试新的包版本

本工具提供了一个简单的方式来创建自定义的 Phar 文件,无需强制更新现有依赖。

代码实现

以下是 builder.php 文件的完整代码:

<?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__);
1
2
3
4
5
6
7
8

## 本地运行生成 Phar 文件

### 步骤 1: 准备独立SDK(guzzlehttp/guzzle)

~~~bash
mkdir guzzle && cd guzzle
composer require guzzlehttp/guzzle:^7.0
### 步骤 2 将上述代码保存为 `build.php` 文件,并放置在您要打包的 PHP 项目根目录下。 ### 步骤 2: 运行构建脚本 打开终端,导航到项目根目录,运行以下命令:
1
2
3
4
5
# 基本用法 - 生成与目录同名的 Phar 文件
php -d phar.readonly=0 build.php

# 自定义文件名 - 生成指定名称的 Phar 文件
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();
// 发送 GET 请求
$response = $client->request('GET', 'https://www.zhiqiang.wang');
// 获取响应状态码
$statusCode = $response->getStatusCode();
echo "Status code: $statusCode\n";
### 作为命令行工具使用 如果您的 Phar 文件设计为命令行工具,可以通过以下方式运行: ## 注意事项 1. 运行脚本时需要禁用 `phar.readonly` 选项(使用 `-d phar.readonly=0`) 2. 确保您有写入当前目录的权限 3. 如果您的项目有特殊的自动加载需求,可能需要手动修改生成的 `index.php` 文件 4. Phar 文件打包后,原始代码的路径结构会被保留,因此请确保您的代码中使用的是相对路径 ## 扩展建议 - 添加更多命令行参数选项(如指定排除文件列表、压缩级别等) - 支持自定义入口脚本内容 - 添加版本号和元数据信息到 Phar 文件中 - 实现增量构建功能,只打包修改过的文件
分享: Twitter Facebook
chihqiang

chihqiang

代码与生活的点滴记录