php-phar打包避坑指南2025

有很多php脚本工具都是打包成phar形式,使用起来就很方便,那么如何自己做一个呢?也找了很多文档,也遇到很多坑,这里就来总结一下

phar安装

现在直接装yum php-cli包就有phar文件,很方便

可通过phar help查看帮助

重要修改:

php配置文件中

;phar.readonly = On

一定要改成

phar.readonly = Off

phar简单打包

先打包跟简单的单文件

先写一个简单的index.php文件

<?php
echo "HelloWorld\n";

打包

phar pack -f helloworld.phar -c gz -b '#!/usr/bin/php'  index.php 

参数说明

选中描述
-f指定生成的phar文件名
-c指定压缩算法
-b在首行添加内容(可直接执行类似于sh)(单文件时未生效)

执行

php helloworld.phar 

则会输出helloworld

如果文件名不是index.php呢?

[root@localhost test]# phar pack -f helloworld.phar -c gz -b '#!/usr/bin/php' helloworld.php
helloworld.php
[root@localhost test]# php helloworld.phar 
PHP Warning:  include(phar:///tmp/test/helloworld.phar/index.php): Failed to open stream: phar error: "index.php" is not a file in phar "/tmp/test/helloworld.phar" in /tmp/test/helloworld.phar on line 9
PHP Warning:  include(): Failed opening 'phar:///tmp/test/helloworld.phar/index.php' for inclusion (include_path='phar:///tmp/test/helloworld.phar:.:/opt/remi/php83/root/usr/share/pear:/opt/remi/php83/root/usr/share/php:/usr/share/pear:/usr/share/php') in /tmp/test/helloworld.phar on line 9
[root@localhost test]# 

打包虽然没报错,但运行找不到index.php文件

通过查看help,看到可以使用-s设置启动文件(使用-s时,后面...文件中可省略该文件,但单文件种情况还是要把单文件加上)

满怀期待的你是不是跃跃欲试了??

root@localhost test]# phar pack -f helloworld.phar -s helloworld.php
PHP Fatal error:  Uncaught PharException: illegal stub for phar "/tmp/test/helloworld.phar" (__HALT_COMPILER(); is missing) in phar:///opt/remi/php83/root/usr/bin/phar.phar/pharcommand.inc:534
Stack trace:
#0 phar:///opt/remi/php83/root/usr/bin/phar.phar/pharcommand.inc(534): Phar->setStub()
#1 phar:///opt/remi/php83/root/usr/bin/phar.phar/pharcommand.inc(592): PharCommand->phar_set_stub_begin()
#2 phar:///opt/remi/php83/root/usr/bin/phar.phar/clicommand.inc(87): PharCommand->cli_cmd_run_pack()
#3 /opt/remi/php83/root/usr/bin/phar.phar(52): CLICommand->__construct()
#4 {main}
  thrown in phar:///opt/remi/php83/root/usr/bin/phar.phar/pharcommand.inc on line 534
[root@localhost test]# 

哈哈报错了

注意-s指定的文件行尾必须要加上__HALT_COMPILER()

helloworld.php代码如下

<?php
echo "Hello Wolrd\n";
__HALT_COMPILER();
[root@localhost test]# phar pack -f helloworld.phar -s helloworld.php helloworld.php
helloworld.php
[root@localhost test]# php helloworld.phar 
Hello Wolrd
[root@localhost test]# 

打包通过运行成功

phar多文件打包

比较单文件打包,坑点比较多,最大的问题是路径问题

先写2个文件

├── helloworld.php
├── lib
│   └── app.php
└── start.php
<?php
//helloworld.php
include __DIR__ . '/lib/app.php';
echo "DIR:" . __DIR__ . "\n";
echo "FILE:" . __FILE__ . "\n";
$app = new app();
$app->show();
echo "HelloWorld\n";
<?php
//lib/app.php
class app {
    function show() {
        echo "AppName:" . __CLASS__ . "\n";
        echo "AppFunc:" . __FUNCTION__ . "\n";
        echo "AppDir:" . __DIR__ . "\n";
        echo "AppFile:" . __FILE__ . "\n";
    }
}

不建议直接改helloworld.php,要改的很多,不是简单的添加__HALT_COMPILER(); 

不死心的小伙伴可以试试

强烈建议重新写一个启动文件

这里经过不断尝试整理出来比较简单的写法,模板化,后面完全可以全部套用

<?php
//start.php
//工作目录切换到文件所在目录,否则不在文件目录执行会报错
chdir(__DIR__);
//Phar::mapPhar();这样也能通过
Phar::mapPhar("helloworld.phar");
//只能用phar路径,否则不通过
require_once 'phar://helloworld.phar/helloworld.php';
//必须要加,否则stub-set会报错
__HALT_COMPILER(); 
[root@localhost test]# phar pack -f bin/helloworld.phar -c gz -b '#!/usr/bin/php' -s start.php helloworld.php lib/app.php
helloworld.php
lib/app.php
[root@localhost test]# chmod a+x bin/helloworld.phar 
[root@localhost test]# ./bin/helloworld.phar 
DIR:phar:///tmp/test/bin/helloworld.phar
FILE:phar:///tmp/test/bin/helloworld.phar/helloworld.php
AppName:app
AppFunc:show
AppDir:phar:///tmp/test/bin/helloworld.phar/lib
AppFile:phar:///tmp/test/bin/helloworld.phar/lib/app.php
HelloWorld
[root@localhost test]# 

多文件的-b参数就有效果

特别注意,如果有多级目录不能只写目录名,否则路径就会不对(-s文件可在文件列表中省略)

-s start.php helloworld.php lib/app.php

支持匹配写法

-s start.php *.php lib/*.php

下面这种绝对不行,虽然help中说可以是目录名

[root@localhost test]# phar pack -f bin/helloworld.phar -c gz -b '#!/usr/bin/php' -s start.php helloworld.php lib
helloworld.php
app.php
[root@localhost test]# ./bin/helloworld.phar 
PHP Parse error:  Unclosed '{' on line 4 in phar:///tmp/test/bin/helloworld.phar/lib/app.php on line 6
[root@localhost test]# 

文件直接找不到了,看打包时的文件名就可以看出,文件路径就不对了

Phar其它注意点

从上面的dir输出就可以看到,里面文件或目录都是一种虚拟路径,

实际过程中涉及到的文件及目录操作都需要特殊注意

坑爹示范

//helloworld.php
include __DIR__ . '/lib/app.php';
echo "DIR:" . __DIR__ . "\n";
echo "FILE:" . __FILE__ . "\n";
echo "PharTrue:" . Phar::running(true) . "\n";
echo "PharFalse:" . Phar::running(false) . "\n";
echo "CWD:" . getcwd() . "\n";
$app = new app();
$app->show();
echo "HelloWorld\n";
$filename=__DIR__."/aaa.txt";
file_put_contents($filename, "HelloWorld");
<?php

//lib/app.php
class app {

    function show() {
        echo "AppName:" . __CLASS__ . "\n";
        echo "AppFunc:" . __FUNCTION__ . "\n";
        echo "AppDir:" . __DIR__ . "\n";
        echo "AppFile:" . __FILE__ . "\n";
        echo "AppPharTrue:" . Phar::running(true) . "\n";
        echo "AppPharFalse:" . Phar::running(false) . "\n";
        echo "AppCWD:" . getcwd() . "\n";
        $filename = __DIR__ . "/bbb.txt";
        file_put_contents($filename, "HelloWorld");
    }

}

使用了file_put_contents

不打包应该这样

[root@localhost test]# php helloworld.php
DIR:/tmp/test
FILE:/tmp/test/helloworld.php
PharTrue:
PharFalse:
CWD:/tmp/test
AppName:app
AppFunc:show
AppDir:/tmp/test/lib
AppFile:/tmp/test/lib/app.php
AppPharTrue:
AppPharFalse:
AppCWD:/tmp/test
HelloWorld

打包后第一次看着通过(有的会报错)

[root@localhost test]# phar pack -f helloworld.phar -c gz -b '#!/usr/bin/php' -a helloworld.phar -s start.php helloworld.php lib/app.php
helloworld.php
lib/app.php
[root@localhost test]# phar list helloworld.phar
Unexpected default arguments to command list, check /usr/bin/phar help
[root@localhost test]# phar list -f helloworld.phar
|-phar:///tmp/test/helloworld.phar/helloworld.php
\-phar:///tmp/test/helloworld.phar/lib
  \-phar:///tmp/test/helloworld.phar/lib/app.php
[root@localhost test]# php helloworld.phar
DIR:phar:///tmp/test/helloworld.phar
FILE:phar:///tmp/test/helloworld.phar/helloworld.php
PharTrue:phar:///tmp/test/helloworld.phar
PharFalse:/tmp/test/helloworld.phar
CWD:/tmp/test
AppName:app
AppFunc:show
AppDir:phar:///tmp/test/helloworld.phar/lib
AppFile:phar:///tmp/test/helloworld.phar/lib/app.php
AppPharTrue:phar:///tmp/test/helloworld.phar
AppPharFalse:/tmp/test/helloworld.phar
AppCWD:/tmp/test
HelloWorld

 第一次运行看着正常

[root@localhost test]# phar list -f helloworld.phar
|-phar:///tmp/test/helloworld.phar/aaa.txt
|-phar:///tmp/test/helloworld.phar/helloworld.php
\-phar:///tmp/test/helloworld.phar/lib
  |-phar:///tmp/test/helloworld.phar/lib/app.php
  \-phar:///tmp/test/helloworld.phar/lib/bbb.txt
[root@localhost test]# php helloworld.phar
PHP Notice:  require_once(): zlib: data error in /tmp/test/helloworld.phar on line 8
PHP Warning:  require_once(phar://helloworld.phar/helloworld.php): Failed to open stream: phar error: internal corruption of phar "/tmp/test/helloworld.phar" (actual filesize mismatch on file "helloworld.php") in /tmp/test/helloworld.phar on line 8
PHP Fatal error:  Uncaught Error: Failed opening required 'phar://helloworld.phar/helloworld.php' (include_path='.:/opt/remi/php83/root/usr/share/pear:/opt/remi/php83/root/usr/share/php:/usr/share/pear:/usr/share/php') in /tmp/test/helloworld.phar:8
Stack trace:
#0 {main}
  thrown in /tmp/test/helloworld.phar on line 8
[root@localhost test]# 

但在看文件列表,多了aaa.txt和bbb.txt

并且再运行直接报错了

注意Phar::running(true)的返回

解决方式

如果未使用则返回空,可以根据这个来处理文件名

        $filename = __DIR__ . "/bbb.txt";
        if (!empty(Phar::running(true))) {
            $filename = str_replace(Phar::running(true), getcwd(), $filename);
            if(!file_exists(dirname($filename))){
                mkdir(dirname($filename), 0777, true);
            }
        }
        file_put_contents($filename, "HelloWorld");

这样不管打不打包都能正常运行

执行php情况

[root@localhost test]# tree
.
├── aaa.txt
├── helloworld.php
├── lib
│   ├── app.php
│   └── bbb.txt
└── start.php

执行phar情况

[root@localhost bin]# tree
.
├── aaa.txt
├── helloworld.phar
└── lib
    └── bbb.txt

有更好的方式可以提出来

还有其它参数-i -x 我暂未试出效果,有兴趣的可以自己尝试一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/960887.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】_顺序表

目录 1. 概念与结构 1.1 静态顺序表 1.2 动态顺序表 2. 动态顺序表实现 2.1 SeqList.h 2.2 SeqList.c 2.3 Test_SeqList.c 3. 顺序表性能分析 线性表是n个具有相同特性的数据元素的有限序列。 常见的线性表有&#xff1a;顺序表、链表、栈、队列、字符串等&#xff1b…

OPencv3.4.1安装及配置教程

来到GitHub上opencv的项目地址 https://github.com/opencv/opencv/releases/tag/3.4.1 以上资源包都是 OpenCV 3.4.1 版本相关资源&#xff0c;它们的区别如下&#xff1a; (1). opencv-3.4.1-android-sdk.zip&#xff1a;适用于 Android 平台的软件开发工具包&#xff08;SDK…

世上本没有路,只有“场”et“Bravo”

楔子&#xff1a;电气本科“工程电磁场”电气研究生课程“高等电磁场分析”和“电磁兼容”自学”天线“、“通信原理”、“射频电路”、“微波理论”等课程 文章目录 前言零、学习历程一、Maxwells equations1.James Clerk Maxwell2.自由空间中传播的电磁波3.边界条件和有限时域…

ZYNQ-IP-AXI-GPIO

AXI GPIO 可以将 PS 端的一个 AXI 4-Lite 接口转化为 GPIO 接口&#xff0c;并且可以被配置为单端口或双端口&#xff0c;每个通道的位宽可以独立配置。 通过使能三态门可以将端口动态地配置为输入或输出。 AXIGPIO 是 ZYNQ PL 端的一个 IP 核&#xff0c;可以将 AXI-Lite Mas…

20.Word:小谢-病毒知识的科普文章❗【38】

目录 题目​ NO1.2.3文档格式 NO4.5 NO6.7目录/图表目录/书目 NO8.9.10 NO11索引 NO12.13.14 每一步操作完&#xff0c;确定之后记得保存最后所有操作完记得再次删除空行 题目 NO1.2.3文档格式 样式的应用 选中应用段落段落→开始→选择→→检查→应用一个一个应用ctr…

为什么应用程序是特定于操作系统的?[计算机原理]

你把WINDOWS程序复制到MAC上使用&#xff0c;会发现无法运行。你可能会说&#xff0c;MAC是arm处理器&#xff0c;而WINDWOS是X86 处理器。但是在2019年&#xff0c;那时候MAC电脑还全是Intel处理器&#xff0c;在同样的X86芯片上&#xff0c;运行MAC和WINDOWS 程序还是无法互相…

LigerUI在MVC模式下的响应原则

LigerUI是基于jQuery的UI框架&#xff0c;故他也是遵守jQuery的开发模式&#xff0c;但是也具有其特色的侦听函数&#xff0c;那么当LigerUI作为View层的时候&#xff0c;他所发送后端的必然是表单的数据&#xff0c;在此我们以俩个div为例&#xff1a; {Layout "~/View…

BurpSuite--暴力破解

一.弱口令 1. 基本概念 介绍&#xff1a;弱口令&#xff08;weak password&#xff09;是指那些容易被他人猜测或通过工具破解的密码。虽然弱口令没有严格的定义&#xff0c;但通常它指的是由简单的数字、字母、常用词语或规律性组合构成的密码。 特点&#xff1a; 密码容易被…

深入探讨防抖函数中的 this 上下文

深入剖析防抖函数中的 this 上下文 最近我在研究防抖函数实现的时候&#xff0c;发现一个耗费脑子的问题&#xff0c;出现了令我困惑的问题。接下来&#xff0c;我将通过代码示例&#xff0c;深入探究这些现象背后的原理。 示例代码 function debounce(fn, delay) {let time…

【PostgreSQL内核学习 —— (WindowAgg(一))】

WindowAgg 窗口函数介绍WindowAgg理论层面源码层面WindowObjectData 结构体WindowStatePerFuncData 结构体WindowStatePerAggData 结构体eval_windowaggregates 函数update_frameheadpos 函数 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊…

RocketMQ消息是如何存储的?

大家好&#xff0c;我是锋哥。今天分享关于【RocketMQ消息是如何存储的&#xff1f;】面试题。希望对大家有帮助&#xff1b; RocketMQ消息是如何存储的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 RocketMQ 使用了一个高性能、分布式的消息存储架构…

MongoDB平替数据库对比

背景 项目一直是与实时在线监测相关&#xff0c;特点数据量大&#xff0c;读写操作大&#xff0c;所以选用的是MongoDB。但按趋势来讲&#xff0c;需要有一款国产数据库可替代&#xff0c;实现信创要求。选型对比如下 1. IoTDB 这款是由清华大学主导的开源时序数据库&#x…

电力晶体管(GTR)全控性器件

电力晶体管&#xff08;Giant Transistor&#xff0c;GTR&#xff09;是一种全控性器件&#xff0c;以下是关于它的详细介绍&#xff1a;&#xff08;模电普通晶体管三极管进行对比学习&#xff09; 基本概念 GTR是一种耐高电压、大电流的双极结型晶体管&#xff08;BJT&am…

蓝桥杯python语言基础(4)——基础数据结构(上)

目录 一、列表与元组 &#xff08;一&#xff09;列表 &#xff08;二&#xff09;操作列表 &#xff08;三&#xff09;元组 习题P502 习题P497 二、字符串 &#xff08;一&#xff09;字符串的基本操作 &#xff08;二&#xff09;字符串的常用方法 &#xff08;三&…

langchain基础(三)

Chain&#xff1a; 关于三个invoke&#xff1a; 提示模板、聊天模型和输出解析器都实现了langchain的runnable接口&#xff0c; 都具有invoke方法&#xff08;因为invoke方法是Runnable的通用调用方法&#xff09; 所以可以一次性调用多次invoke直接得到最终结果&#xff1a;…

数据分析和AI丨应对AI实施挑战,工程领域AI应用的五大方法

工程领域的人工智能 &#xff08;AI&#xff09; 已经开始发挥价值&#xff0c;低代码和无代码工具正在使曾经仅属于专业数据科学家的 AI 能力变得大众化。 然而&#xff0c;并非工程领域的每个人都能从中受益&#xff0c;使用新的便捷的 AI 工具提高工作效率并不难&#xff0c…

【Linux探索学习】第二十七弹——信号(一):Linux 信号基础详解

Linux学习笔记&#xff1a; https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言&#xff1a; 前面我们已经将进程通信部分讲完了&#xff0c;现在我们来讲一个进程部分也非常重要的知识点——信号&#xff0c;信号也是进程间通信的一…

games101-(5/6)

光栅化 投影完成之后&#xff0c;视图区域被确定在从[-1,1]的单位矩阵中&#xff0c;下一步就是光栅化 长宽比&#xff1a;ratio 垂直的可视角度&#xff1a;fild-of-view 可以看到的y 轴的范围&#xff0c;角度越小 越接近正交投影 屏幕坐标系 、 将多边形转化成像素 显示…

Linux之详谈——权限管理

目录 小 峰 编 程 ​编辑 一、权限概述 1、什么是权限 2、为什么要设置权限 3、Linux中的权限类别- 4、Linux中文件所有者 1&#xff09;所有者分类&#xff08;谁&#xff09; 2&#xff09;所有者的表示方法 ① u(the user who owns it)&#xff08;属主权限&…

oracle比较一下统计信息差异吧

统计信息发生了哪些变化&#xff1f; 从上次收集到最近一次收集有什么不同&#xff1f; set long 999999 longc 99999 line 100 select report, maxdiffpct from table(dbms_stats.diff_table_stats_in_history(SYS,T1,to_timestamp(2025-01-22 09:01:46,YYYY-MM-DD hh24:mi:s…