PHP Phar 打包与分发完整指南
Phar 基础概念
Phar (PHP Archive) 是 PHP 特有的归档格式,类似于 Java 的 JAR 文件,允许将整个 PHP 应用程序打包成单个文件。

创建 Phar 文件
基本配置要求
// 在 php.ini 中确保以下设置 phar.readonly = Off // 允许创建/修改 phar 文件
创建 Straw(存根)文件
<?php
// stub.php - Phar 入口文件
Phar::mapPhar('app.phar');
require 'phar://app.phar/index.php';
__HALT_COMPILER();
?>
生成 Phar 文件
<?php
// build.php - 打包脚本
$srcDir = __DIR__ . '/src';
$pharFile = 'app.phar';
// 创建 Phar 对象
$phar = new Phar($pharFile, 0, 'app.phar');
// 设置压缩格式
$phar->compressFiles(Phar::GZ);
// 或使用 BZ2 压缩
// $phar->compressFiles(Phar::BZ2);
// 开始写入文件
$phar->startBuffering();
// 设置默认的 stub,自动识别入口
$defaultStub = $phar->createDefaultStub('index.php', 'index.php');
// 添加文件
$phar->buildFromDirectory($srcDir, '/\.php$/');
// 设置存根
$phar->setStub($defaultStub);
// 添加自定义文件
$phar->addFromString('config.php', file_get_contents('config.prod.php'));
// 结束写入
$phar->stopBuffering();
echo "Phar 文件创建成功: $pharFile\n";
高级打包配置
完整打包脚本示例
<?php
// create-phar.php
class PharBuilder {
public function build() {
try {
// 检查 phar.readonly
if (ini_get('phar.readonly')) {
throw new Exception("请设置 phar.readonly = Off");
}
$pharFile = 'application.phar';
$srcDir = __DIR__ . '/app';
// 删除已存在的 phar 文件
if (file_exists($pharFile)) {
unlink($pharFile);
}
$phar = new Phar($pharFile, FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME, 'application.phar');
$phar->startBuffering();
// 添加所有 PHP 文件
$phar->buildFromDirectory($srcDir, '/\.php$/');
// 排除某些文件
$phar->delete('config.dev.php');
$phar->delete('test/');
// 添加非 PHP 文件
$phar->addFile('public/css/style.css', 'assets/style.css');
$phar->addFile('public/js/app.js', 'assets/app.js');
// 添加目录
$phar->buildFromDirectory('templates/', '/\.html$/');
// 设置文件权限
$phar['config.php']->setMetadata(['permission' => 0644]);
// 自定义 stub
$stub = $this->getCustomStub();
$phar->setStub($stub);
$phar->stopBuffering();
// 压缩所有文件
$phar->compressFiles(Phar::GZ);
echo "打包成功!文件大小: " . $this->formatSize(filesize($pharFile)) . "\n";
} catch (Exception $e) {
echo "打包失败: " . $e->getMessage() . "\n";
exit(1);
}
}
private function getCustomStub() {
return <<<STUB
<?php
// 自定义入口
Phar::mapPhar('application.phar');
// 设置常量
define('PHAR_MODE', true);
define('APP_ROOT', 'phar://application.phar/');
// 设置错误显示
error_reporting(E_ALL);
ini_set('display_errors', 0);
// 设置时区
date_default_timezone_set('Asia/Shanghai');
// 加载自动加载器
require 'phar://application.phar/vendor/autoload.php';
// 运行应用
$app = new Application();
$app->run();
__HALT_COMPILER();
STUB;
}
private function formatSize($bytes) {
$units = ['B', 'KB', 'MB', 'GB'];
$i = 0;
while ($bytes >= 1024 && $i < 3) {
$bytes /= 1024;
$i++;
}
return round($bytes, 2) . $units[$i];
}
}
// 执行打包
$builder = new PharBuilder();
$builder->build();
使用 Composer 打包
<?php
// build-phar-with-composer.php
$pharFile = 'app.phar';
if (file_exists($pharFile)) {
unlink($pharFile);
}
$phar = new Phar($pharFile, 0, 'app.phar');
$phar->startBuffering();
// 添加 Composer 自动加载和项目文件
$phar->buildFromDirectory(__DIR__ . '/vendor', '/\.php$/');
$phar->buildFromDirectory(__DIR__ . '/src', '/\.php$/');
$phar->buildFromDirectory(__DIR__ . '/config', '/\.php$/');
// 添加其他资源文件
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('public')
);
foreach ($files as $file) {
if ($file->isFile()) {
$relativePath = 'public/' . $file->getBasename();
$phar->addFile($file->getPathname(), $relativePath);
}
}
// 设置存根
$stub = "#!/usr/bin/env php\n";
$stub .= $phar->createDefaultStub('bin/console');
$phar->setStub($stub);
$phar->stopBuffering();
// 压缩
$phar->compressFiles(Phar::GZ);
Phar 文件分发
命令行工具分发
# 给予执行权限 chmod +x app.phar # 直接运行 ./app.phar # 或通过 PHP 运行 php app.phar # 查看帮助 php app.phar --help
Web 应用分发
<?php
// deploy.php - 部署脚本
class PharDeployer {
public function deploy($source, $destination, $version = '1.0.0') {
// 验证 Phar 文件
if (!file_exists($source)) {
throw new Exception("源文件不存在");
}
// 验证 Phar 签名
$phar = new Phar($source);
if (!$phar->isFileFormat(Phar::PHAR)) {
throw new Exception("无效的 Phar 文件格式");
}
// 创建备份
if (file_exists($destination)) {
$backup = $destination . '.backup.' . date('YmdHis');
copy($destination, $backup);
echo "备份已创建: $backup\n";
}
// 复制文件
if (copy($source, $destination)) {
echo "部署成功! 版本: $version\n";
echo "文件大小: " . filesize($destination) . " bytes\n";
return true;
}
throw new Exception("部署失败");
}
public function verifySignature($pharFile) {
try {
$phar = new Phar($pharFile);
$signature = $phar->getSignature();
echo "签名算法: " . $signature['hash_type'] . "\n";
echo "签名哈希: " . $signature['hash'] . "\n";
return true;
} catch (Exception $e) {
echo "签名验证失败: " . $e->getMessage() . "\n";
return false;
}
}
}
// 使用示例
$deployer = new PharDeployer();
$deployer->deploy('app.phar', '/usr/local/bin/myapp', '2.0.0');
$deployer->verifySignature('app.phar');
版本管理和自动更新
<?php
// update.php - 自动更新系统
class AutoUpdater {
private $updateUrl;
private $currentVersion;
public function __construct($updateUrl, $currentVersion) {
$this->updateUrl = $updateUrl;
$this->currentVersion = $currentVersion;
}
public function checkForUpdates() {
$response = file_get_contents($this->updateUrl . '/version.json');
$versionInfo = json_decode($response, true);
if (version_compare($versionInfo['version'], $this->currentVersion, '>')) {
return [
'available' => true,
'version' => $versionInfo['version'],
'url' => $versionInfo['download_url'],
'checksum' => $versionInfo['checksum'],
'release_notes' => $versionInfo['release_notes']
];
}
return ['available' => false];
}
public function downloadUpdate($url, $expectedChecksum) {
$tempFile = tempnam(sys_get_temp_dir(), 'phar_update_');
// 下载文件
$downloaded = file_put_contents($tempFile, fopen($url, 'r'));
if ($downloaded === false) {
throw new Exception("下载失败");
}
// 验证校验和
$actualChecksum = md5_file($tempFile);
if ($actualChecksum !== $expectedChecksum) {
unlink($tempFile);
throw new Exception("校验和不匹配");
}
return $tempFile;
}
public function installUpdate($tempFile) {
$currentFile = $_SERVER['SCRIPT_FILENAME'];
// 创建备份
$backupFile = $currentFile . '.backup.' . date('YmdHis');
copy($currentFile, $backupFile);
// 替换文件
if (rename($tempFile, $currentFile)) {
echo "更新成功!旧版本备份至: $backupFile\n";
return true;
}
// 失败时恢复
copy($backupFile, $currentFile);
throw new Exception("更新失败,已恢复原文件");
}
}
// 使用示例
$updater = new AutoUpdater('https://updates.example.com', '1.0.0');
$update = $updater->checkForUpdates();
if ($update['available']) {
echo "发现新版本: {$update['version']}\n";
echo "发布说明: {$update['release_notes']}\n";
$tempFile = $updater->downloadUpdate($update['url'], $update['checksum']);
$updater->installUpdate($tempFile);
} else {
echo "当前已是最新版本\n";
}
性能优化和安全
性能优化
<?php
// 优化 Phar 文件使用
// 1. 使用 APC/OpCache
// 在 php.ini 中配置
opcache.enable=1
opcache.enable_cli=1
opcache.file_cache=/tmp/opcache
// 2. 使用内存缓存
$pharPath = 'phar://app.phar/config.php';
if (function_exists('apc_fetch')) {
$config = apc_fetch('app_config');
if ($config === false) {
$config = require $pharPath;
apc_store('app_config', $config, 3600);
}
}
// 3. 延迟加载
class LazyLoader {
private $pharFile;
public function loadClass($className) {
$pharPath = "phar://{$this->pharFile}/{$className}.php";
if (file_exists($pharPath)) {
require $pharPath;
}
}
}
spl_autoload_register([new LazyLoader('app.phar'), 'loadClass']);
安全加固
<?php
// 添加安全检查
class PharSecurity {
public static function validatePhar($pharFile) {
// 1. 验证文件完整性
if (!self::verifyIntegrity($pharFile)) {
return "文件完整性验证失败";
}
// 2. 检查签名
$phar = new Phar($pharFile);
$signature = $phar->getSignature();
// 3. 检查是否篡改
$expectedHash = 'your_expected_hash_here';
if ($signature['hash'] !== $expectedHash) {
return "文件签名不匹配";
}
// 4. 限制执行环境
if (extension_loaded('suhosin')) {
ini_set('suhosin.executor.include.whitelist', 'phar');
}
return true;
}
public static function secureExecution() {
// 限制危险函数
$disabledFunctions = [
'exec', 'system', 'passthru',
'shell_exec', 'proc_open',
'popen', 'show_source'
];
foreach ($disabledFunctions as $function) {
if (function_exists($function)) {
// 记录或阻止
// 可以使用 disable_functions 在 php.ini 中配置
}
}
// 设置安全目录
define('PHAR_SAFE_MODE', true);
}
private static function verifyIntegrity($file) {
// 实现文件完整性检查
$checksumFile = $file . '.checksum';
if (file_exists($checksumFile)) {
$expected = file_get_contents($checksumFile);
$actual = md5_file($file);
return $expected === $actual;
}
return false;
}
}
// 使用
PharSecurity::validatePhar('app.phar');
PharSecurity::secureExecution();
自动化构建脚本
#!/bin/bash
# build.sh - 自动化打包脚本
echo "📦 开始打包 PHP Phar..."
# 检查 PHP 版本
PHP_VERSION=$(php -r "echo PHP_VERSION;")
echo "PHP 版本: $PHP_VERSION"
# 检查 phar.readonly
PHAR_READONLY=$(php -r "echo ini_get('phar.readonly');")
if [ "$PHAR_READONLY" = "1" ]; then
echo "❌ 需要设置 phar.readonly = Off"
exit 1
fi
# 清理旧文件
echo "清理旧文件..."
rm -f *.phar
rm -rf build/
# 创建构建目录
mkdir -p build
# 运行测试
echo "运行测试..."
php vendor/bin/phpunit tests/ || exit 1
# 生成文档
echo "生成文档..."
php vendor/bin/phpdoc -d src -t build/docs
# 打包 Phar
echo "打包 Phar..."
php create-phar.php
# 测试 Phar
echo "测试 Phar..."
php app.phar --version || exit 1
# 生成校验和
echo "生成校验和..."
md5sum app.phar > app.phar.md5
sha256sum app.phar > app.phar.sha256
# 复制到构建目录
cp app.phar build/
cp app.phar.md5 build/
cp app.phar.sha256 build/
echo "✅ 打包完成!"
echo "文件: app.phar"
echo "大小: $(du -h app.phar | cut -f1)"
最佳实践建议
- 版本控制:在 Phar 文件名中包含版本号
- 签名验证:使用数字签名确保文件完整性
- 自动更新:实现自动更新机制
- 兼容性测试:在不同 PHP 版本上测试
- 错误处理:完善的错误处理机制
- 日志记录:记录安装和更新日志
- 备份机制:更新时自动创建备份
这样,您就可以完整地实现 PHP 项目的 Phar 打包和分发流程了。