2024 年日本 PHP 研討會

使用 Phar 封存檔:簡介

Phar 封存檔的概念類似於 Java JAR 封存檔,但專為 PHP 應用程式的需求和彈性而設計。Phar 封存檔用於在單一檔案中發佈完整的 PHP 應用程式或程式庫。Phar 封存檔應用程式的使用方法與其他任何 PHP 應用程式完全相同。

php coolapplication.phar
  

使用 Phar 封存程式庫與使用任何其他 PHP 程式庫完全相同

<?php
include 'coollibrary.phar';
?>

phar 資料流包裝器是 phar 延伸的核心,並在這裡詳細說明。phar 資料流包裝器允許使用 PHP 的標準檔案函式 fopen()opendir() 以及其他處理一般檔案的函式來存取 phar 封存中的檔案。phar 資料流包裝器支援對檔案和目錄的所有讀/寫操作。

<?php
include 'phar://coollibrary.phar/internal/file.php';
header('Content-type: image/jpeg');
// 可以透過完整路徑或別名存取 phar
echo file_get_contents('phar:///fullpath/to/coollibrary.phar/images/wow.jpg');
?>

Phar 類別實作了用於存取檔案和建立 phar 封存的高階功能。Phar 類別在這裡詳細說明。

<?php
try {
// open an existing phar
$p = new Phar('coollibrary.phar', 0);
// Phar extends SPL's DirectoryIterator class
foreach (new RecursiveIteratorIterator($p) as $file) {
// $file is a PharFileInfo class, and inherits from SplFileInfo
echo $file->getFileName() . "\n";
echo
file_get_contents($file->getPathName()) . "\n"; // display contents;
}
if (isset(
$p['internal/file.php'])) {
var_dump($p['internal/file.php']->getMetadata());
}

// create a new phar - phar.readonly must be 0 in php.ini
// phar.readonly is enabled by default for security reasons.
// On production servers, Phars need never be created,
// only executed.
if (Phar::canWrite()) {
$p = new Phar('newphar.tar.phar', 0, 'newphar.tar.phar');
// make this a tar-based phar archive, compressed with gzip compression (.tar.gz)
$p = $p->convertToExecutable(Phar::TAR, Phar::GZ);

// create transaction - nothing is written to newphar.phar
// until stopBuffering() is called, although temporary storage is needed
$p->startBuffering();
// add all files in /path/to/project, saving in the phar with the prefix "project"
$p->buildFromIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('/path/to/project')), '/path/to/');

// add a new file via the array access API
$p['file1.txt'] = 'Information';
$fp = fopen('hugefile.dat', 'rb');
// copy all data from the stream
$p['data/hugefile.dat'] = $fp;

if (
Phar::canCompress(Phar::GZ)) {
$p['data/hugefile.dat']->compress(Phar::GZ);
}

$p['images/wow.jpg'] = file_get_contents('images/wow.jpg');
// any value can be saved as file-specific meta-data
$p['images/wow.jpg']->setMetadata(array('mime-type' => 'image/jpeg'));
$p['index.php'] = file_get_contents('index.php');
$p->setMetadata(array('bootstrap' => 'index.php'));

// save the phar archive to disk
$p->stopBuffering();
}
} catch (
Exception $e) {
echo
'Could not open Phar: ', $e;
}
?>

此外,可以使用任何支援的對稱雜湊演算法(如果啟用了 ext/hash,則為 MD5、SHA1、SHA256 和 SHA512)以及使用 OpenSSL 的非對稱公鑰/私鑰簽章來驗證 phar 檔案內容。要利用 OpenSSL 簽章,您需要產生一對公鑰/私鑰,並使用私鑰透過 Phar::setSignatureAlgorithm() 設定簽章。此外,使用以下程式碼擷取的公鑰

<?php
$public
= openssl_get_publickey(file_get_contents('private.pem'));
$pkey = '';
openssl_pkey_export($public, $pkey);
?>
必須儲存在它驗證的 phar 封存旁邊。如果 phar 封存儲存為 /path/to/my.phar,則公鑰必須儲存為 /path/to/my.phar.pubkey,否則 phar 將無法驗證 OpenSSL 簽章。

Phar 類別也提供了 3 個靜態方法:Phar::webPhar()Phar::mungServer()Phar::interceptFileFuncs(),這些方法對於打包設計用於一般檔案系統和基於 Web 應用程式的 PHP 應用程式至關重要。Phar::webPhar() 實作了一個前端控制器,用於將 HTTP 呼叫路由到 phar 封存中的正確位置。Phar::mungServer() 用於修改 $_SERVER 陣列的值,以欺騙處理這些值的應用程式。Phar::interceptFileFuncs() 指示 Phar 攔截對 fopen()file_get_contents()opendir() 以及所有基於 stat 的函式(file_exists()is_readable() 等)的呼叫,並將所有相對路徑路由到 phar 封存中的位置。

例如,將熱門的 phpMyAdmin 應用程式版本打包以作為 phar 封存使用,只需要這個簡單的腳本,然後在修改使用者/密碼後,就可以像一般檔案一樣從您的 Web 伺服器存取 `phpMyAdmin.phar.tar.php`。

<?php
@unlink('phpMyAdmin.phar.tar.php');
copy('phpMyAdmin-2.11.3-english.tar.gz', 'phpMyAdmin.phar.tar.php');
$a = new Phar('phpMyAdmin.phar.tar.php');
$a->startBuffering();
$a["phpMyAdmin-2.11.3-english/config.inc.php"] = '<?php
/* 伺服器設定 */
$i = 0;

/* 伺服器 localhost (config:root) [1] */
$i++;
$cfg[\'Servers\'][$i][\'host\'] = \'localhost\';
$cfg[\'Servers\'][$i][\'extension\'] = \'mysqli\';
$cfg[\'Servers\'][$i][\'connect_type\'] = \'tcp\';
$cfg[\'Servers\'][$i][\'compress\'] = false;
$cfg[\'Servers\'][$i][\'auth_type\'] = \'config\';
$cfg[\'Servers\'][$i][\'user\'] = \'root\';
$cfg[\'Servers\'][$i][\'password\'] = \'\';


/* 伺服器設定結束 */
if (strpos(PHP_OS, \'WIN\') !== false) {
$cfg[\'UploadDir\'] = getcwd();
} else {
$cfg[\'UploadDir\'] = \'/tmp/pharphpmyadmin\';
@mkdir(\'/tmp/pharphpmyadmin\');
@chmod(\'/tmp/pharphpmyadmin\', 0777);
}'
;
$a->setStub('<?php
Phar::interceptFileFuncs();
Phar::webPhar("phpMyAdmin.phar", "phpMyAdmin-2.11.3-english/index.php");
echo "phpMyAdmin 必須透過網頁瀏覽器執行\n";
exit -1;
__HALT_COMPILER();
'
);
$a->stopBuffering();
?>

新增筆記

使用者貢獻的筆記 3 則筆記

shaun at shaunfreeman dot co dot uk
14 年前
如果您嘗試在網路應用程式中使用 Phar 卻只得到空白畫面,並且您也啟用了 suhosin,則必須將

suhosin.executor.include.whitelist="phar"

添加到 "/etc/php5/conf.d/suhosin.ini" 檔案或您的 "php.ini" 檔案中。

完成後,一切都會正常運作。
ch1902
10 年前
如果您要從瀏覽器執行 webPhar,例如 https://127.0.0.1/myphar.phar,那麼您可能必須在您的網路伺服器中將 .phar 副檔名與 PHP 關聯起來,以便解譯 PHP 程式碼。在 Apache 中,修改 httpd.conf 以包含

AddType application/x-httpd-php .php .phar
frame86 at live dot com
11 年前
openssl 的範例完全錯誤。公鑰必須從憑證中提取,而 openssl_pkey_export() 僅適用於私鑰。

有效的範例
<?php
$publicKey
= openssl_get_publickey(file_get_contents('certificate.pem'));
$details = openssl_pkey_get_details($publicKey);
file_put_contents('my.phar.pubkey', $details['key']);
?>

不用說,如果使用者沒有根據有效的指紋或公鑰憑證進行檢查,那麼 my.phar/.phar/signature.bin 的最佳和最強加密也是無用的,因為任何人都可以打開、讀取、重新建立並使用新金鑰簽署新的壓縮檔。您會怎麼做?好好想想。
To Top