5

Phar使用姿势全解析,完美实现php项目打包部署

 2 years ago
source link: http://www.veiking.cn/blog/1072-page.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Phar使用姿势全解析,完美实现php项目打包部署

程序员甲   @Veiking   2021-02-14

使用java玩web的大神们都熟悉,当程序开发好打成war包,放入服务器容器,非常方便;PHP5之后,PHP界大神给大家提供了一个功能类似的工具,就是Phar。虽然许多迹象让Phar看起来都像是过时了,但像javaWeb开发那种war包部署的方式,也确实是香,于是就想着研究研究这个Phar,怎么借助Phar,实现类似于javaWeb开发中那种打包部署的功能

使用java玩web的大神们都熟悉,当程序开发好,准备发布的时候,打成war包,直接放入服务器容器,非常方便;类似,PHP5之后,一众PHP界大神给大家提供了一个功能类似的工具,就是Phar

考虑到PHP本身是不用编译的语言,所以,Phar的打包也不会想java打包那样,生成诸如.class的编译文件,而是将php程序代码直接原样压缩打包,生成一个.phar文件,然后可以像java语言的jar一样调用,也可以像war包一样直接发布运行(这个跟java的war包还是有些不同的,php容器是试图在整个phar文件上,基于设定的访问入口,去整体运行;而java的web容器在运行war包时,实际上是提前做了解压操作的)。

各种机缘,时至今日,从网上的一些教程的情况来看,真正使用Phar去打包的人应该是非常少的,一个是他本身的缺陷,并不能便捷的实现完全类同Java程序打包的那些功能,更重要的是Composer已经事实上成为php项目的代码依赖库标准管理工具,拥有更多相对成熟便捷的方案。除此之外,更多的原因可能是PHP工程师,很多都喜欢直接将代码复制过来复制过去,而不是借助什么工具包,如果非有必要,直接压个.zip文件也就完事儿了,这也就使得Phar的存在比较尴尬。

虽然许多迹象让Phar看起来都像是过时了的老物件,但像java开发那种打war包部署的方式,也确实是香,于是就想着抽空研究研究这个Phar,能不成简单实现一点点类似的功能,毕竟部署项目的时候,来回拷贝源代码这种操作,怎么看都显得不是太成熟太专业(基于PHP的开发,其实也有类的插件,只是笔者的使用场景,不太适合),哈哈哈。

接下来,我们就看看,Phar到底是怎么玩的。

Phar基本操作

关于Phar的打包的使用方法,这里有个例子,感兴趣的可以先看一下:
https://github.com/SegmentFault/phar-sample
基于这个样例项目,我们看看他的基本操作。这里用eclipse随便创建了个vphar的项目,把样例代码考入,可以看到如下的目录结构:

参考一般的程序结构,我们大概可以理解:
文件目录app存放的基本是业务逻辑代码;
文件目录lib是用来存放依赖库的,例如三方插件之类的;
文件目录portal我们打开进去看了看,里面只有一个文件index.php文件,明显是作为项目入口的;
文件目录static从字面意思上理解一般都是用来存放静态文件的,例如js、css,图片视频之类的,这种文件一般可能会单独部署;
文件build.php就是我们要看的打包程序了,接下来我们来看看build.php中的脚本代码:
startBuffering();

// 将后缀名相关的文件打包
foreach ($exts as $ext) {
    $phar->buildFromDirectory($dir, '/\.' . $ext . '$/');
}

// 把build.php本身摘除
$phar->delete('build.php');

// 设置入口
$phar->setStub("");
$phar->stopBuffering();

// 打包完成
echo "Finished {$file}\n";

样例代码的注释写的已经很清楚了,我们留意下设置入口这个东西,这段代码的意思是,设置的默认执行入口是.phar包中的portal/index.php文件,然后我们在窗口执行指令:
php build.php

无意外的话,我们既可以在项目根目录下得到一个名为Sample.phar的文件,然后直接运行:
php Sample.phar

既可以看到窗口输出:Hello World!

好了,程序如期运行。梳理下整体的逻辑,我们也可以基本明白这个phar打包的工作逻辑,这里要特别注意下,他这个打包讲究单一入口,这个有点类似于其他语言的main方法,所以在程序结构上要留意下。

(注:使用phar打包,须在php配置文件处修改;phar.readonly = On,分号;去掉,然后把后面的On改为Off,默认只读,设置为Off即可写入,然后才可以操作)

看完这个例子,我们就可以封装一些功能,打包为phar文件,运行起来也很方便。

但是我们不能每次使用的时候,都去运行指令,那该怎么办呢?

其实也很简单,我们在服务容器根目录下,创建个index.php文件,然后加入如下代码:
require __DIR__ . '/Sample.phar';

这样,只要在访问入口index.php文件时,即可去运行Sample.phar文件,然后去执行文件中的默认入口程序,我们可以看到,在浏览器处已输出:

好了,到此,我们基本能明白Phar实现打包的基本操作原理了;接下来要尝试实现下我们最初的想法:怎么借助Phar的打包部署,实现类似于java开发中类似于war打包部署的功能

实现Laravel项目打包部署

这里我们要打包的php项目是基于laravel框架的。laravel被业内一些大神戏称为活生生写成了java的php框架,这些玩笑从侧面反映了一个事实,laravel框架从java这种编程语言中借鉴了很多理念性的东西,这也使得laravel比较适合相对复杂业务型的项目。当然,各语言各框架都是在互相借鉴吸收互相参考精进的,laravel框架这熟悉的面孔,也使得java程序员非常容易上手。

我们本次就是想基于laravel框架的项目试试。刚开始的时候,我们发现网上有一些教程,提供了一款基于Phar零配置的便捷打包工具:humbug/box ,看了介绍基本可行,但是尝试之后发现这个工具包太过于陈旧,其要求symfony/console版本在4.4之前,而现在laravel8的symfony/console 版本都已经到了5.2,进行版本回退不太可行,类似的很多版本问题,冲突矛盾无法调和;于是我们就想着干脆就绕开这些个东西,基于phar,自己写脚本来实现一下。

说干就干,项目根目录下,我们也写了一个脚本build.php

startBuffering();

// 忽略文件目录
$INGORE_DIR = '/^(?!.*( deploying|static_files|.git|.setting|.externalToolBuilders)).*$/';
$phar->buildFromDirectory(__DIR__, $INGORE_DIR);

// 忽略文件
$INGORE_FILES = array();//array('.env','.htaccess');
array_push($INGORE_FILES, $PHAR, '.buildpath', '.editorconfig', '.env.bak', '.env.example', '.gitattributes', '.gitignore', '.htaccess.bak', '.project', 'artisan', 'build.php','deploy.php', 'composer.json', 'composer.json.txt', 'composer.lock', 'package.json', 'package-lock.json', 'readme.md', 'webpack.mix.js');
foreach ($INGORE_FILES as $file) {
    if($phar->offsetExists($file)){
        $phar->delete($file);
    }
}

// 压缩方式 Phar::GZ  PHAR::BZ2 等等
$phar->compressFiles(Phar::GZ);
// 如部署服务器不支持phar,则也可打成zip包
//$phar->convertToData(Phar::ZIP);
$phar->stopBuffering();

echo "BUILD IS FINISH!";

// 执行指令
//-> php build.php

除了已有注释说明,这里有几个地方需要特别解释:
我们没有采用那种打成单一入口.phar包,然后用index.php文件来运行的方式,我们试了下,那种方式不现实,JavaWeb程序打的war在运行的时候其实是解压了的,而.phar则是尝试在单一文件上去运行的,代码较少的小功能还可以,如果项目稍微大一点,整个.phar文件就会比较大,这就会导致运行起来非常的卡顿,毕竟加载大文件是也是需要很大开销的;所以我们这里只借助Phar工具做压缩打包,然后部署之后再解压。

注意$INGORE_DIR参数,设置了一些需要忽略的文件目录,这里是一些与项目程序部署不相关文件目录;主要还有静态文件目录(static_files),这个我们单独列出,这些文件普遍较大,压进包里不太合适,一般可能会有独立的处理方案,具体还是看咱们各自的情况。

$INGORE_FILES参数,我们肯定不希望打包的时候把许多跟项目运行不相关的文件压进去;还有一些本地开发测试的环境参数文件,这些就不应该出现在部署包中。

上边build.php文件的脚本,运行之后,即可以将所在项目打包成一个.phar包;由于php容器并没有提供解压操作,我们还需要写个脚本deploy.php:

extractTo($deploy_dir);   // 单纯解压至$deploy_dir

// 部署方式2:直接覆盖解压至项目目录
// $deploy_dir = $HOME;
// $phar->extractTo($deploy_dir, null, true);   

// 删除压缩文件
unlink(__DIR__.'\\'.$PHAR);
// 部署时须放开注释,删除解压缩脚本文件
// unlink(__DIR__.'\deploy.php');

echo "DEPLOY IS FINISH!";

// 执行指令
// php deploy.php

////////// FUNCTION THAT CAN BE IGNORED //////////
// 删除文件目录(含之下目录、文件)
function deldir($path){
    //如果是目录则继续
    if(is_dir($path)){
        //扫描一个文件夹内的所有文件夹和文件并返回数组
        $p = scandir($path);
        //如果 $p 中有两个以上的元素则说明当前 $path 不为空
        if(count($p)>2){
            foreach($p as $val){
                //排除目录中的.和..
                if($val !="." && $val !=".."){
                    //如果是目录则递归子目录,继续操作
                    if(is_dir($path.$val)){
                        //子目录中操作删除文件夹和文件
                        deldir($path.$val.'/');
                    }else{
                        //如果是文件直接删除
                        unlink($path.$val);
                    }
                }
            }
        }
    }
    //删除目录
    return rmdir($path);
}

很显然,deploy.php的作用就是实现包解压的。

当我们将打包好的.phar文件连同脚本文件deploy.php一起放置在服务容器根目录,执行命令: php deploy.php 即可将.phar文件解压至相应的目录,完成部署。
(注:在服务器没做限制的情况下,除了输指令,在浏览器输入:www.youdomain.com/deploy.php 亦可运行)

是不是有点JavaWeb程序war包部署的那个意思了,当然,前提是,目标服务器处是要支持phar的,如不支持,我们就别管deploy.php文件了,直接在本地打包的时候,选择zip包的方式,自己传到服务器去手动玩吧。

到此,两个脚本,一个打包一个解压,就可以让我们的代码干干净净的部署在服务器上,操作起来也便捷了很多,phar是不是也开始香了。

本篇内容实现了借鉴JavaWeb程序war包的打包部署形式,利用phar将php(不限于laravel框架)项目,打成独立包,然后再部署至服务器。在脚本的帮助下,部署在服务器的代码纯粹了很多,部署的过程也便捷了很多,写到这里,希望对大家有所帮助。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK