电竞比分网-中国电竞赛事及体育赛事平台

分享

簡(jiǎn)單了解Phar代碼打包工具的使用

 硬核項(xiàng)目經(jīng)理 2021-05-31

簡(jiǎn)單了解Phar代碼打包工具的使用

Phar 是在 PHP5 之后提供的一種類(lèi)似于將代碼打包的工具。本質(zhì)上是想依照 Java 的 Jar 文件那種形式的代碼包,不過(guò)本身由于 PHP 是不編譯的,所以這個(gè) Phar 實(shí)際上就是將代碼原樣的進(jìn)行打包,不會(huì)進(jìn)行編譯。但是我們可以對(duì)打包的 Phar 包進(jìn)行壓縮操作。

另外,實(shí)際上使用過(guò) Phar 包的人非常少,特別是在 Composer 已經(jīng)成為事實(shí)代碼庫(kù)標(biāo)準(zhǔn)的今天,Phar 就更加難覓蹤影了。不過(guò),Composer 的安裝包本身也是一個(gè) .phar 的打包文件。最主要的原因,一個(gè)是 Phar 這種形式的代碼包安裝并不像 Composer 一樣的簡(jiǎn)單方便,另一方面,早期的程序員,特別是 LAMP/LAMP 的程序員,都喜歡去將開(kāi)源的代碼復(fù)制過(guò)來(lái),而不喜歡直接使用一個(gè)工具包。畢竟,源代碼在手上讓我們更加踏實(shí)一些。其實(shí),就算是 Composer 這樣直接下載的就是源碼,我們也從來(lái)沒(méi)什么人真正的去翻過(guò)。而 Composer 相比 Phar 的最大優(yōu)勢(shì),一個(gè)是代碼的自動(dòng)加載,另一個(gè)就是標(biāo)準(zhǔn)的 PSR 命令空間和目錄規(guī)范。這兩個(gè)在 Phar 中是沒(méi)有的,所以我們要使用 Phar 包都必須要 require 一下。

雖說(shuō)已經(jīng)過(guò)時(shí)了,但我們還是簡(jiǎn)單的來(lái)學(xué)習(xí)了解一下。說(shuō)不定在什么時(shí)候我們就能用上,特別是封裝一些內(nèi)部的公用庫(kù)函數(shù)時(shí),Phar 打包代碼的這種方式還是非常有用的。

代碼打包

我們先按標(biāo)準(zhǔn)格式建立一個(gè)目錄樹(shù)。

在這個(gè)目錄樹(shù)中,src 目錄存放源碼,build 目錄用來(lái)存放生成后的 .phar 代碼包。

// index.php
<?php

require_once "phar://myphar.phar/common.php";

index.php 文件中,我們就是簡(jiǎn)單的引用 common.php 。注意這里使用的是 phar 偽協(xié)議來(lái)加載的 common.php 文件。關(guān)于偽協(xié)議的內(nèi)容我們之前有過(guò)一篇文章進(jìn)行過(guò)講解。

<?php
// common.php
class Manager{

    public static function run($config){
        echo "AAA", PHP_EOL;
        var_dump($config);
    }

    public static function ChineseMobile($mobile){
        if(preg_match("/^1[34578]\d{9}$/", $mobile)){
            return true;
        }
        return false;
    }
}

common.php 文件中只是提供了一個(gè)類(lèi)和兩個(gè)簡(jiǎn)單的方法用來(lái)測(cè)試。run() 方法就是簡(jiǎn)單的輸出打印的內(nèi)容和傳遞過(guò)來(lái)的參數(shù)。ChineseMobile() 方法則是我們提供的一個(gè)判斷我們國(guó)內(nèi)手機(jī)號(hào)的函數(shù)。

[database]
host=localhost
db=dbname
user=myuser
pass=dbpass

config.ini 是一個(gè)配置文件,其實(shí)我們可以在 Phar 的代碼中直接的進(jìn)行配置文件的讀取,也可以讓配置文件隨代碼一起 build 到指定的目錄。

源碼文件準(zhǔn)備好了,接下來(lái)就是要準(zhǔn)備打包的編譯文件了。

// create-phar.php
$srcRoot = "./myphar/src";
$buildRoot = "./myphar/build";
 
$phar = new Phar($buildRoot . "/myphar.phar"
  FilesystemIterator::CURRENT_AS_FILEINFO |       FilesystemIterator::KEY_AS_FILENAME, "myphar.phar");
$phar["index.php"] = file_get_contents($srcRoot . "/index.php");
$phar["common.php"] = file_get_contents($srcRoot . "/common.php");
$phar->setStub($phar->createDefaultStub("index.php"));

copy($srcRoot . "/config.ini", $buildRoot . "/config.ini");

代碼并不復(fù)雜,主要是一個(gè) Phar 類(lèi),這個(gè)類(lèi)要指定生成文件的目錄,文件名,然后使用 createDefaultStub() 方法來(lái)調(diào)用我們包的入口文件 index.php ,這個(gè)方法是用于創(chuàng)建指定的 .phar 文件的存根。其實(shí)就是指定一個(gè)入口文件,就像 Java 中的 main() 方法入口一樣。

然后我們拷貝了 config.ini 文件到發(fā)布目錄 build 中。

接著使用命令行運(yùn)行這個(gè) create-phar.php 文件,就能夠生成這套代碼包了。

# php ./create-phar.php

使用文本編輯器打開(kāi) myphar.phar 文件,我們會(huì)發(fā)現(xiàn)里面竟然還是我們熟悉的 PHP 代碼,拉到最底下,更會(huì)發(fā)現(xiàn) index.php 和 common.php 的內(nèi)容都被編譯在這個(gè)文件中了。上面的那些自動(dòng)生成的代碼就是一些引導(dǎo)或者前置準(zhǔn)備語(yǔ)句,是 Phar 擴(kuò)展為我們準(zhǔn)備好的內(nèi)容,所有用戶(hù)自己寫(xiě)的源碼都會(huì)在這個(gè)文件的底部。也就是說(shuō),大家可以下載 Composer 的安裝包,也就是那個(gè) .phar 文件看看里面都寫(xiě)了什么東西。

接下來(lái)就是使用了,這個(gè)就非常簡(jiǎn)單了。

$config = parse_ini_file("./myphar/build/config.ini");
require './myphar/build/myphar.phar';

Manager::run($config);
// AAA
// array(4) {
//   ["host"]=>
//   string(9) "localhost"
//   ["db"]=>
//   string(6) "dbname"
//   ["user"]=>
//   string(6) "myuser"
//   ["pass"]=>
//   string(6) "dbpass"
// }

var_dump(Manager::ChineseMobile('13811111111'));
var_dump(Manager::ChineseMobile('138111111112'));
// bool(true)
// bool(false)

壓縮能力

前面說(shuō)過(guò),做為代碼庫(kù)來(lái)說(shuō),Phar 已經(jīng)早就敗給了 Composer ,但是它除了能夠做為一些安裝包來(lái)使用之外,本身 Phar 也是一個(gè)壓縮工具??梢杂脕?lái)存檔一些文件、文本、目錄之類(lèi)的內(nèi)容。下面我就來(lái)簡(jiǎn)單看看對(duì)于文本的存檔,Phar 是如何使用的。

unlink('./my.phar');
unlink('./my.phar.bz2');
unlink('./my.phar.gz');
$p = new Phar('./my.phar'0 ,'my.phar');
$p['myfile1.txt'] = 'hi1';
$p['myfile2.txt'] = 'hi2';
$p1 = $p->compress(Phar::GZ);
$p2 = $p->compress(Phar::BZ2);
unset($p);

$decompressPhar  = new Phar('./my.phar'0 ,'my.phar');
foreach($decompressPhar as $file){
    // $file 是返回的 PharFileInfo 對(duì)象
    var_dump($file->getFileName());
    var_dump($file->isCompressed());
    var_dump($file->isCompressed(Phar::BZ2));
    var_dump($file->isCompressed(Phar::GZ));
    var_dump($file->getContent());
}
echo '==================', PHP_EOL;
// string(11) "myfile1.txt"
// bool(false)
// bool(false)
// bool(false)
// string(3) "hi1"
// string(11) "myfile2.txt"
// bool(false)
// bool(false)
// bool(false)
// string(3) "hi2"

首先,依然是實(shí)例化一個(gè) Phar 類(lèi),然后我們給它像數(shù)組一樣增加屬性,這樣,屬性?xún)?nèi)容就被打包進(jìn)了 .phar 文件中。通過(guò)直接查看 my.phar 文件,我們可以看出,myfile1.txt 這兩個(gè)屬性直接被寫(xiě)成了文件進(jìn)行保存了,也就是說(shuō),它幫我們將文本轉(zhuǎn)化成文件并打包在了 my.phar 這個(gè)壓縮包文件中了。

compress() 方法則是將當(dāng)前的這個(gè) Phar 對(duì)象壓縮存儲(chǔ)為某個(gè)格式的文件。這里我們直接壓縮了 Bzip2 和 GZ 文件。調(diào)用這個(gè)方法后直接就會(huì)生成對(duì)應(yīng)的壓縮文件。

Phar 對(duì)象在遍歷時(shí)產(chǎn)生的對(duì)象是 PharFileInfo 對(duì)象,它擁有很多類(lèi)似于 File 的文件操作函數(shù)。大家可能在官方文檔中找到相關(guān)的說(shuō)明。

假設(shè)我們遍歷 my.phar.gz ,內(nèi)容依然可以正常輸出,但循環(huán)中的 isCompressed() 判斷都依然會(huì)是 false ,難道文件沒(méi)有被壓縮嗎?其實(shí),我們需要通過(guò)另一個(gè)函數(shù)來(lái)讓所有文件都進(jìn)行統(tǒng)一格式的壓縮。

$p = new Phar('./my.phar'0 ,'my.phar');
$p->compressFiles(Phar::GZ);
unset($p);

$decompressPhar  = new Phar('./my.phar.gz'0 ,'my.phar');
foreach($decompressPhar as $file){
    // $file 是返回的 PharFileInfo 對(duì)象
    var_dump($file->getFileName());
    var_dump($file->isCompressed());
    var_dump($file->isCompressed(Phar::BZ2));
    var_dump($file->isCompressed(Phar::GZ));
    var_dump($file->getContent());
}
echo '==================', PHP_EOL;

// string(11) "myfile1.txt"
// bool(true)
// bool(false)
// bool(true)
// string(3) "hi1"
// string(11) "myfile2.txt"
// bool(true)
// bool(false)
// bool(true)
// string(3) "hi2"

使用 compressFiles() 對(duì)整個(gè) .phar 中的所有文件進(jìn)行了統(tǒng)一的格式壓縮之后,再打印時(shí) isCompressed() 就會(huì)返回對(duì)應(yīng)格式的 true 了。

數(shù)據(jù)格式 Phar

最后,如果只是為了打包壓縮功能的話(huà),我們沒(méi)必要使用 Phar 類(lèi)。Phar 類(lèi)最主要的還是用來(lái)打包能夠運(yùn)行的 PHP 源碼,也就是它的 createDefaultStub() 方法非常重要。而如果只是打包普通文件的話(huà),我們并不需要這個(gè)方法,這時(shí),我們就可以使用另外一個(gè) PharData 類(lèi)來(lái)進(jìn)行數(shù)據(jù)的打包壓縮。使用方法和 Phar 類(lèi)是一模一樣的。同時(shí),PharData 類(lèi)可以直接打包成 tar 之類(lèi)的文件。

$p = new PharData('./myData.tar');
$p['myfile1.txt'] = 'hi1';
$p['myfile2.txt'] = 'hi2';

foreach($p as $file){
    var_dump($file->getFileName());
    var_dump($file->isCompressed());
    var_dump($file->isCompressed(Phar::BZ2));
    var_dump($file->isCompressed(Phar::GZ));
    var_dump($file->getContent());
}
echo '==================', PHP_EOL;
// string(11) "myfile1.txt"
// bool(false)
// bool(false)
// bool(false)
// string(3) "hi1"
// string(11) "myfile2.txt"
// bool(false)
// bool(false)
// bool(false)
// string(3) "hi2"

總結(jié)

說(shuō)實(shí)話(huà),Phar 真的是一個(gè)冷門(mén)項(xiàng)目,但是在某些情況中又非常有用,比如它雖然在代碼包領(lǐng)域被 Composer 打敗了,但是它又可以成為 Composer 的安裝包,也就是說(shuō),沒(méi)有 Phar 你就安裝不了 Composer 。而做為壓縮工具,雖然有強(qiáng)大的實(shí)力但使用的卻也非常的少。因此,我們還是以了解為目的,如果感覺(jué)某些場(chǎng)景非常合適的話(huà),也完全可以深入的研究拿來(lái)放到我們的實(shí)際項(xiàng)目中使用。畢竟它是 PHP 的一部分,不需要任何的編譯安裝及其它支持,非常原生。

測(cè)試代碼:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202007/source/%E7%AE%80%E5%8D%95%E4%BA%86%E8%A7%A3Phar%E4%BB%A3%E7%A0%81%E6%89%93%E5%8C%85%E5%B7%A5%E5%85%B7%E7%9A%84%E4%BD%BF%E7%94%A8.php

參考文檔:

https://www./manual/zh/book.phar.php

https://www./post/packaging-your-php-apps-with-phar.html

http://www./info-detail-888559.html

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多