简单了解Phar代码打包工具的使用

Phar 是在 PHP5 之后提供的一种类似于将代码打包的工具。本质上是想依照 Java 的 Jar 文件那种形式的代码包,不过本身由于 PHP 是不编译的,所以这个 Phar 实际上就是将代码原样的进行打包,不会进行编译。但是我们可以对打包的 Phar 包进行压缩操作。

另外,实际上使用过 Phar 包的人非常少,特别是在 Composer 已经成为事实代码库标准的今天,Phar 就更加难觅踪影了。不过,Composer 的安装包本身也是一个 .phar 的打包文件。最主要的原因,一个是 Phar 这种形式的代码包安装并不像 Composer 一样的简单方便,另一方面,早期的程序员,特别是 LAMP/LAMP 的程序员,都喜欢去将开源的代码复制过来,而不喜欢直接使用一个工具包。毕竟,源代码在手上让我们更加踏实一些。其实,就算是 Composer 这样直接下载的就是源码,我们也从来没什么人真正的去翻过。而 Composer 相比 Phar 的最大优势,一个是代码的自动加载,另一个就是标准的 PSR 命令空间和目录规范。这两个在 Phar 中是没有的,所以我们要使用 Phar 包都必须要 require 一下。

虽说已经过时了,但我们还是简单的来学习了解一下。说不定在什么时候我们就能用上,特别是封装一些内部的公用库函数时,Phar 打包代码的这种方式还是非常有用的。

代码打包

我们先按标准格式建立一个目录树。

在这个目录树中,src 目录存放源码,build 目录用来存放生成后的 .phar 代码包。

// index.php
<?php require_once "phar://myphar.phar/common.php";

index.php 文件中,我们就是简单的引用 common.php 。注意这里使用的是 phar 伪协议来加载的 common.php 文件。关于伪协议的内容我们之前有过一篇文章进行过讲解。

<?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 文件中只是提供了一个类和两个简单的方法用来测试。run() 方法就是简单的输出打印的内容和传递过来的参数。ChineseMobile() 方法则是我们提供的一个判断我们国内手机号的函数。

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

config.ini 是一个配置文件,其实我们可以在 Phar 的代码中直接的进行配置文件的读取,也可以让配置文件随代码一起 build 到指定的目录。

源码文件准备好了,接下来就是要准备打包的编译文件了。

// 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");

代码并不复杂,主要是一个 Phar 类,这个类要指定生成文件的目录,文件名,然后使用 createDefaultStub() 方法来调用我们包的入口文件 index.php ,这个方法是用于创建指定的 .phar 文件的存根。其实就是指定一个入口文件,就像 Java 中的 main() 方法入口一样。

然后我们拷贝了 config.ini 文件到发布目录 build 中。

接着使用命令行运行这个 create-phar.php 文件,就能够生成这套代码包了。

# php ./create-phar.php

使用文本编辑器打开 myphar.phar 文件,我们会发现里面竟然还是我们熟悉的 PHP 代码,拉到最底下,更会发现 index.php 和 common.php 的内容都被编译在这个文件中了。上面的那些自动生成的代码就是一些引导或者前置准备语句,是 Phar 扩展为我们准备好的内容,所有用户自己写的源码都会在这个文件的底部。也就是说,大家可以下载 Composer 的安装包,也就是那个 .phar 文件看看里面都写了什么东西。

接下来就是使用了,这个就非常简单了。

$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)

压缩能力

前面说过,做为代码库来说,Phar 已经早就败给了 Composer ,但是它除了能够做为一些安装包来使用之外,本身 Phar 也是一个压缩工具。可以用来存档一些文件、文本、目录之类的内容。下面我就来简单看看对于文本的存档,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 对象
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"

首先,依然是实例化一个 Phar 类,然后我们给它像数组一样增加属性,这样,属性内容就被打包进了 .phar 文件中。通过直接查看 my.phar 文件,我们可以看出,myfile1.txt 这两个属性直接被写成了文件进行保存了,也就是说,它帮我们将文本转化成文件并打包在了 my.phar 这个压缩包文件中了。

compress() 方法则是将当前的这个 Phar 对象压缩存储为某个格式的文件。这里我们直接压缩了 Bzip2 和 GZ 文件。调用这个方法后直接就会生成对应的压缩文件。

Phar 对象在遍历时产生的对象是 PharFileInfo 对象,它拥有很多类似于 File 的文件操作函数。大家可能在官方文档中找到相关的说明。

假设我们遍历 my.phar.gz ,内容依然可以正常输出,但循环中的 isCompressed() 判断都依然会是 false ,难道文件没有被压缩吗?其实,我们需要通过另一个函数来让所有文件都进行统一格式的压缩。

$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 对象
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() 对整个 .phar 中的所有文件进行了统一的格式压缩之后,再打印时 isCompressed() 就会返回对应格式的 true 了。

数据格式 Phar

最后,如果只是为了打包压缩功能的话,我们没必要使用 Phar 类。Phar 类最主要的还是用来打包能够运行的 PHP 源码,也就是它的 createDefaultStub() 方法非常重要。而如果只是打包普通文件的话,我们并不需要这个方法,这时,我们就可以使用另外一个 PharData 类来进行数据的打包压缩。使用方法和 Phar 类是一模一样的。同时,PharData 类可以直接打包成 tar 之类的文件。

$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"

总结

说实话,Phar 真的是一个冷门项目,但是在某些情况中又非常有用,比如它虽然在代码包领域被 Composer 打败了,但是它又可以成为 Composer 的安装包,也就是说,没有 Phar 你就安装不了 Composer 。而做为压缩工具,虽然有强大的实力但使用的却也非常的少。因此,我们还是以了解为目的,如果感觉某些场景非常合适的话,也完全可以深入的研究拿来放到我们的实际项目中使用。毕竟它是 PHP 的一部分,不需要任何的编译安装及其它支持,非常原生。

测试代码:

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.php.net/manual/zh/book.phar.php

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

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

===============

关注公众号:【硬核项目经理】获取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料

知乎、公众号、抖音、头条搜索【硬核项目经理】

B站ID:482780532

简单了解Phar代码打包工具的使用的更多相关文章

  1. 窥探原理:实现一个简单的前端代码打包器 Roid

    roid roid 是一个极其简单的打包软件,使用 node.js 开发而成,看完本文,你可以实现一个非常简单的,但是又有实际用途的前端代码打包工具. 如果不想看教程,直接看代码的(全部注释):点击地 ...

  2. VS 代码打包工具

    源代码下载地址 https://github.com/loresoft/msbuildtasks

  3. 用winform实现一个B/S代码更新打包工具

    一个.net程序员必须拥有的能力就是可以随时随地写出一个自己需要的小工具,于是记录一下我的个人工具吧. 新建一个窗体应用项目,代码如下: namespace 打包工具 { partial class ...

  4. [.net 面向对象程序设计进阶] (22) 团队开发利器(一)简单易用的代码管理工具VSS

    [.net 面向对象程序设计进阶] (22) 团队开发利器(一)简单易用的代码管理工具VSS 本篇要点:在进阶篇快要结束的时候说说源代码管理器,我们的开发,不是一个人可以完成的事,团队协作很重要,而且 ...

  5. 简单理解 Webpack,以及Web前端使用打包工具的原因

    Java 中的模块 传统的前端开发就是 JS.HTML.CSS 三件套.Web 没有像 Java 一样拥有优秀的模块机制,就是类与类之间可以分装在不同的包下,不同包下的类互相引用时通过import导入 ...

  6. 细说前端自动化打包工具--webpack

    背景 记得2004年的时候,互联网开发就是做网页,那时也没有前端和后端的区分,有时一个网站就是一些纯静态的html,通过链接组织在一起.用过Dreamweaver的都知道,做网页就像用word编辑文档 ...

  7. Webpack:前端资源模块化管理和打包工具

    一.介绍: Webpack 是当下最热门的前端资源模块化管理和打包工具.它可以将许多松散的模块按照依赖和规则打包成符合生 产环境部署的前端资源.还可以将按需加载的模块进行代码分隔,等到实际需要的时候再 ...

  8. Winform打包工具SetupFactory 9 的使用

    写了个WinForm的小程序..以前没打过包..只是直接把Bin里的东西复制出来使用..自己使用是足够.但是发给别人毕竟不太好看(不牛逼)..所以就想着打包.. Vs2012自带的有打包的功能..相信 ...

  9. 【Cocos2d-Js基础教学(5)资源打包工具的使用及资源的异步加载处理】

    TexturePacker是纹理资源打包工具,支持Cocos2dx的游戏资源打包. 如果用过的同学可以直接看下面的资源的异步加载处理 首先为什么用TexturePacker? 1,节省图片资源实际大小 ...

随机推荐

  1. linux 源码搭建Kafka集群,100%有效

    kafka源码编译安装 准备三台服务器 192.168.xxx.xxx 192.168.xxx.xxx 192.168.xxx.xxx 安装kafka前需先安装JDK和zookeeper如下步骤: J ...

  2. docker容器dockerfile详解

    docker公司在容器技术发展中提出了镜像分层的理念,可以说也是这个革命性的理念让原本只不过是整合linux内核特性的容器,开始野蛮生长. docker通过UnionFS联合文件系统将镜像的分层实现合 ...

  3. 面向对象第一单元总结:Java实现表达式求导

    面向对象第一单元总结:Java实现表达式求导 题目要求 输入一个表达式:包含x,x**2,sin(),cos(),等形式,对x求导并输出结果 例:\(x+x**2+-2*x**2*(sin(x**2+ ...

  4. Element Vue 开箱即用框架如何使用-测试开发【提测平台】阶段小结(二)

    微信搜索[大奇测试开],关注这个坚持分享测试开发干货的家伙. 上一篇总结了后端服务接口的开发,这篇我们主要来总结下前后端分离开发中的前端部分,主要是开箱即用的框架介绍和之前章节组件的梳理和部分的扩展内 ...

  5. Git连接github以及gitee等使用教程

    Git连接github以及gitee等使用教程 一.初始化本次仓库 在想要放置仓库的文件夹出git bash输入命令 git init 二.生成ssh 在github或者gitee注册账户, 在本地生 ...

  6. 见微知著 带你透过内存看 Slice 和 Array的异同

    hi, 大家好,我是 hhf. 有这么一个 Go 面试题:请说出 slice 和 array 的区别? 这简直就是送分题.现在思考一下,你咋样回答才能让面试官满意呢? 我这里就不贴这道题的答案了.但是 ...

  7. [转]C# 互操作性入门系列(一):C#中互操作性介绍

    传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...

  8. SpringMVC的拦截器和过滤器的区别

    一 简介 (1)过滤器: 依赖于servlet容器.在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次.使用过滤器的目的是用来做一些过滤操作,获取我们 ...

  9. Hibernate之关联关系

    时间:2017-1-20 16:28 --一对多配置1.第一步:创建实体类    *   客户实体    *   订单实体    示例代码:        /**          * 客户实体    ...

  10. Tensorflow 2.0 深度学习实战 —— 详细介绍损失函数、优化器、激活函数、多层感知机的实现原理

    前言 AI 人工智能包含了机器学习与深度学习,在前几篇文章曾经介绍过机器学习的基础知识,包括了监督学习和无监督学习,有兴趣的朋友可以阅读< Python 机器学习实战 >.而深度学习开始只 ...