# Phar打包器
仅仅新建2个文件即可以实现webapp一键打包的功能。
phar.php
,herosphp
# 安装
# 1. 新建phar.php
在项目的根目录,即BASE_PATH,新建phar.php
。
<?php
declare(strict_types=1);
define('BASE_PATH', __DIR__ . DIRECTORY_SEPARATOR);
$config = [
'enable' => true,
'phar_file_output_dir' => BASE_PATH . DIRECTORY_SEPARATOR . 'build',
'phar_filename' => 'herosphp.phar',
'signature_algorithm' => Phar::SHA256, //set the signature algorithm for a phar and apply it. The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL.
'private_key_file' => '', // The file path for certificate or OpenSSL private key file.
'exclude_pattern' => '#^(?!.*(LICENSE|.github|.idea|doc|docs|.git|.setting|runtime|test|test_old|tests|Tests|vendor-bin|.md))(.*)$#',
'exclude_files' => [
'process.php','phar.php','boot.php','Monitor.php','.php-cs-fixer.cache','.php-cs-fixer.php','client.php','LICENSE.md', 'README.md', 'web.php'
]
];
// 终端高亮打印红色
function printError(string $message): void
{
printf("\033[31m\033[1m%s\033[0m\n", $message);
exit(1);
}
if (!class_exists(Phar::class, false)) {
printError("The 'phar' extension is required for build phar package");
}
if (ini_get('phar.readonly')) {
printError(
"The 'phar.readonly' is 'On', build phar must setting it 'Off' or exec with 'php -d phar.readonly=0 phar.php'"
);
}
$phar_file_output_dir = $config['phar_file_output_dir'];
if (empty($phar_file_output_dir)) {
printError('Please set the phar file output directory.');
}
if (!file_exists($phar_file_output_dir) && !is_dir($phar_file_output_dir)) {
if (!mkdir($phar_file_output_dir, 0777, true)) {
printError('Failed to create phar file output directory. Please check the permission.');
}
}
$phar_filename = $config['phar_filename'];
if (empty($phar_filename)) {
printError('Please set the phar filename.');
}
$phar_file = rtrim($phar_file_output_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $phar_filename;
if (file_exists($phar_file)) {
unlink($phar_file);
}
$exclude_pattern = $config['exclude_pattern'];
$phar = new Phar($phar_file, 0, 'herosphp');
$phar->compressFiles(Phar::GZ);
$phar->startBuffering();
$signature_algorithm = $config['signature_algorithm'];
if (!in_array($signature_algorithm, [Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, Phar::OPENSSL])) {
throw new RuntimeException('The signature algorithm must be one of Phar::MD5, Phar::SHA1, Phar::SHA256, Phar::SHA512, or Phar::OPENSSL.');
}
if ($signature_algorithm === Phar::OPENSSL) {
$private_key_file = $config['private_key_file'];
if (!file_exists($private_key_file)) {
printError("If the value of the signature algorithm is 'Phar::OPENSSL', you must set the private key file.");
}
$private = openssl_get_privatekey(file_get_contents($private_key_file));
$pKey = '';
openssl_pkey_export($private, $pKey);
$phar->setSignatureAlgorithm($signature_algorithm, $pKey);
} else {
$phar->setSignatureAlgorithm($signature_algorithm);
}
$phar->buildFromDirectory(BASE_PATH, $exclude_pattern);
$exclude_files = $config['exclude_files'];
foreach ($exclude_files as $file) {
if ($phar->offsetExists($file)) {
$phar->delete($file);
}
}
echo 'Files collect complete, begin add file to Phar.' . PHP_EOL;
$phar->setStub("#!/usr/bin/env php
<?php
define('IN_PHAR', true);
Phar::mapPhar('herosphp');
require 'phar://herosphp/herosphp';
__HALT_COMPILER();
");
echo 'Write requests to the Phar archive, save changes to disk.' . PHP_EOL;
$phar->stopBuffering();
unset($phar);
# 2. 新建herosphp文件
同样在根目录新建文件herosphp
#!/usr/bin/env php
<?php
declare(strict_types=1);
use herosphp\core\Config;
use herosphp\GF;
use herosphp\utils\FileUtil;
use herosphp\WebApp;
use Workerman\Worker;
function is_phar(): bool
{
return \class_exists(\Phar::class, false) && Phar::running();
}
define('BASE_PATH', __DIR__ . DIRECTORY_SEPARATOR);
// Phar support.
if (\is_phar()) {
define('RUNTIME_PATH', \dirname(Phar::running(false)).DIRECTORY_SEPARATOR.'runtime/');
} else {
define('RUNTIME_PATH', BASE_PATH . 'runtime/');
}
define('APP_PATH', BASE_PATH . 'app/');
define('CONFIG_PATH', BASE_PATH . 'config/');
define('PUBLIC_PATH', BASE_PATH . 'public/');
define('RUN_WEB_MODE', true);
require BASE_PATH . 'vendor/autoload.php';
//opcache
Worker::$onMasterReload = static function () {
if (function_exists('opcache_get_status') && function_exists('opcache_invalidate')) {
if ($status = opcache_get_status()) {
if (isset($status['scripts']) && $scripts = $status['scripts']) {
foreach (array_keys($scripts) as $file) {
opcache_invalidate($file, true);
}
}
}
}
};
// create dir
FileUtil::makeFileDirs(dirname(GF::getAppConfig('worker_log_path')));
//set worker log
Worker::$pidFile = GF::getAppConfig('pid_path');
Worker::$logFile = GF::getAppConfig('worker_log_path');
// Start Web Application worker
WebApp::run();
// start process worker
// Windows does not support custom processes.
if (str_contains(PHP_OS, 'WINNT') === false) {
$processes = Config::get(name: 'process', default: []);
foreach ($processes as $processName => $config) {
if (!($config['enable'] ?? false)) {
continue;
}
GF::processRun($processName, $config);
}
}
Worker::runAll();
# 使用
- 默认打包
php -d phar.readonly=0 phar.php
# 运行
cd ${BASE_PATH}/build
php herosphp.phar start
# 注意事项
打包后是以 phar
包的形式运行,不同与源代码模式运行,phar
包中的 runtime
目录是不可写的,
所以我们需要重写部分可写的目录位置。