PHP way to explore https:https://cdn.v2ex.com/navatar/44f6/83a8/62_normal.png?m=1644475743 https:https://cdn.v2ex.com/navatar/44f6/83a8/62_large.png?m=1644475743 2025-09-18T05:22:32Z Copyright © 2010-2018, V2EX 求助: PHP 错误,请高手帮我改写下面的 PHP 代码 tag:www.v2ex.com,2025-09-18:/t/1160327 2025-09-18T13:22:32Z 2025-09-18T05:22:32Z hwhtj member/hwhtj `<?php

$nowtime=time();

$pastsec = $nowtime - $_GET["t"];

if($pastsec<600) { exit; //10 分钟更新一次,时间可以自己调整 } ob_start(); //打开缓冲区 include("index.php"); $cOntent= ob_get_contents(); //得到缓冲区的内容 $content .= "\n<script language=Javascript src='http://www.v2ex.com/index/thumb.png' data-original="html.php?t=".$nowtime.""></script>"; //加上调用更新程序的代码

file_put_contents("index.html",$content); if (!function_exists("file_put_contents")) { function file_put_contents($fn,$fs) { $fp=fopen($fn,"w+"); fputs($fp,$fs); fclose($fp);
} } ?>`

以上代码用于生成 typecho 首页 index.html ,运行时提示错误: Warning: Undefined array key "t" in /www/onlineps.top/learn/html.php on line 3 求助高手帮我改写以上代码。

]]>
为什么现在 WEB3、区块链、钱包之类开始用 PHP 了 tag:www.v2ex.com,2025-09-12:/t/1158743 2025-09-12T03:10:19Z 2025-09-17T12:07:58Z fruitmonster member/fruitmonster 看到比较多的区块链、钱包之类远程工作都开始招 PHP 了,请问 PHP 是有什么优势了吗?

]]>
PHP 8.5 加入了 pipe 语法 tag:www.v2ex.com,2025-08-27:/t/1155173 2025-08-27T01:25:32Z 2025-08-27T19:52:20Z cj323 member/cj323
https://php.watch/versions/8.5/pipe-operator

$result = "Hello World" |> strtoupper(...) |> str_shuffle(...) |> trim(...); 

同样语法 JS 那边卡了很久,PHP 直接上了。

]]> 推荐一个优雅的 PHP http 请求工具,仿照了 py 的 requests tag:www.v2ex.com,2025-08-25:/t/1154669 2025-08-25T01:46:48Z 2025-08-24T09:46:48Z tg11 member/tg11 这是我某天突然想到的,python 的 requests 那么好用,为什么 php 要写的这么又臭又长呢?我就结合 claude code 写了一个 composer 包,完全仿照了 requests 模块。求轻喷,欢迎 star 、pr

github 地址 https://github.com/tg111/php-request

直接安装

composer require tg111/php-request 

示例

use PhpRequest\PhpRequest; // 简单的 GET 请求 $respOnse= PhpRequest::get('https://httpbin.org/get'); echo $response->text(); // 带参数的 GET 请求 $respOnse= PhpRequest::get('https://httpbin.org/get', [ 'key1' => 'value1', 'key2' => 'value2' ]); // POST 请求 $respOnse= PhpRequest::post('https://httpbin.org/post', [ 'username' => 'user', 'password' => 'pass' ]); // JSON POST 请求 $respOnse= PhpRequest::post('https://httpbin.org/post', [ 'name' => '张三', 'email' => 'zhangsan@example.com' ], [ 'headers' => ['Content-Type' => 'application/json'] ]); 

使用全局函数

$respOnse= requests_get('https://httpbin.org/get'); $respOnse= requests_post('https://httpbin.org/post', ['key' => 'value']); 

自定义请求头和身份验证

$respOnse= PhpRequest::get('https://httpbin.org/cookies', [], [ 'cookies' => [ 'session_id' => 'abc123456789', 'user_preference' => 'dark_mode' ] ]); # cookies $respOnse= PhpRequest::get('https://httpbin.org/cookies', [], [ 'cookies' => [ 'session_id' => 'abc123456789', 'user_preference' => 'dark_mode' ] ]); 

session 会话管理

会话允许在多个请求之间持久化 Cookie 、请求头和其他配置:

use PhpRequest\PhpRequest; // 创建会话 $session = PhpRequest::session() ->setHeaders([ 'Authorization' => 'Bearer token123', 'Accept' => 'application/json' ]) ->setCookies([ 'session_id' => 'session123' ]) ->setTimeout(60); // 使用会话进行多个请求 $profile = $session->get('/user/profile'); $settings = $session->get('/user/settings'); $updated = $session->post('/user/update', ['name' => '新名称']); 

响应对象

$respOnse= PhpRequest::get('https://httpbin.org/json'); // 获取响应内容 $text = $response->text(); // 原始文本内容 $data = $response->json(); // 解析 JSON 响应 $code = $response->getStatusCode(); // HTTP 状态码 // 检查响应状态 $success = $response->ok(); // 2xx 状态码为 true $isClientError = $response->isClientError(); // 4xx 状态码为 true $isServerError = $response->isServerError(); // 5xx 状态码为 true // 获取头部和元数据 $headers = $response->getHeaders(); $cOntentType= $response->getContentType(); $totalTime = $response->getTotalTime(); $url = $response->getUrl(); // 保存响应到文件 $response->save('/path/to/file.json'); 
]]>
大家好,我又來了,新作品 https://www.freetalkhub.com, PHP 开发 tag:www.v2ex.com,2025-08-17:/t/1153000 2025-08-17T11:41:29Z 2025-08-18T20:01:49Z mimiphp member/mimiphp 本人是 php 独立开发者,以前的话题 t/994939 被同行喷惨了。怎么说呢?本来我的话题都是发在 php 节点,本以为都是 php 自己的开发者讨论。但基于我个人观点,导致被评价为只会 CURD 实属以外。

本人是独立开发者,所以更趋向于了解整个开发流程的每个细节,毕竟除了自己,没有任何合作团队。 我认为说再多也没有拿作品说话比较实在。 本人以前就职于香港找换店和餐饮项目开发的公司。带领团队开发。但就是由于本人的观点是非主流观点,导致手下有部分人是不服从的。

由于 php 在主流程序开发界被嗤之以鼻多次,php 官方的开发团队也被强类型语言开发者参与主导。基于我本人自己的观点,感觉开发过于别扭。所以我自己以自己的作品为目标考虑符合自己的技术栈总结了一套开发思路。

本人就职期间原创开发了一个 PHP 开发框架,原名为 mimiphp ,先改名为 lowphp 由于 PHP-FPM 实在性能太差,基于我的本地电脑 AMD5600X wsl 下的 debian12 系统,也只能弄到 500 左右的并发,注意只是 echo 1;简单脚本测试结果。而如果是静态文件,通过 nginx 处理,可以达到 6 万并发。 所以我修改了框架核心,基于 swoole 的 cli 模式,支持了 swoole 协程模式。并且完整加载 mysql 数据和模板引擎渲染后的并发可以达到 5000 左右。我非常满意。

如果单独开启 redis 缓存可以达到 3 万左右并发。如果单独开启 apcu 缓存后可以达到 5 万并发。这实在是给力了。

其实我并不想跟同行过多争论,因为我发现一个现象,就是整个中文社区,其实任何争论都得不到一个比较好的结果。因为可能由于文化的影响,大家都会以自我的认知为中心,方圆几公里之内天下无敌的心态讽刺挖苦别人为目的,实际上根本不能心平气和的讨论任何话题。特别是程序开发行业从架构-语言-框架-甚至包括变量命名方式,都是毫无意义的讽刺挖苦。其实就是浪费时间罢了。对于技术的提升和理解都毫无用处。

我其实在这里发布过两个帖子,一个是关于 php8.3 的讨论。一个是 ffsou 搜索引擎的讨论,一个 2023 年,一个 2024 年。 我是直到刚才才看到 php8.3 讨论的帖子最后一个楼层居然认为我只是一个菜鸟。。这让我干了 20 年的程序员心里真的感到深深伤害。

其实我们这个行业,本身大环境就非常不好,本来我与香港的一个老板是以技术入股的方式合作,又由于前期没有资金聘请其他语言的程序员,而我又是自信能弄好项目上线的老程序员,所以也就是安心的处理代码了。

但我没有想到的是:2020 年到 2024 年底,这个香港老板一直给我反馈的都是无法正常盈利,只能勉强保本。直到 2024 年底,我从其他途径直到了真相,其实他盈利早过了 600 万以上。而且还把自己家人加入进来吃空饷等等原因,我还是决定主动辞职了。

又基于行业限制规则和法律风险的考虑,我重写了我自己的框架,目前是独立开发者身份。然后考虑直接走国际路线,与国外的程序员沟通交流。发现真的行业文化差异实在太大了。

我希望我们作为简体中文的程序员,应该要自信,和睦相处,多与同行技术交流,而不是讽刺挖苦。

当然,国外同样是以作品说话。目前由于金融行业涉及到严格的资金来源追溯问题,找换店已经不行了,但其他方向,特别是 AI 使用上,国外几乎每天都有几十上百个 Saas 项目发布。当然其中也有我一份功劳,赚了点小钱。

所以基于以上的前提,我想说一下我这个 https://www.freetalkhub.com 是参考了 reddit 和 discuz 以及其他社区后,用我新开发的 lowphp 基于 swoole 高并发开发的 php 框架完成。

其中前端采用到了 vue3.0 的 cdn 模式,以及 pjax 伪单页模式。目前收录和用户体验都非常好。

当然,这是一个试运行站点,主要目的还是宣传为主。 但技术栈我是想弱化前端工程化的主流思想。欢迎体验。

而后端 php 一直在更新,我也采用了最新的 php8.4 版本为核心。我希望用我自己的实践和作品,为 php 的开发环境做一个贡献。希望大家参与讨论。前提是站在平等的位置上。不过你讽刺挖苦也无所谓,对于我来说,早已经锻炼出了死猪不怕开水烫的本事。

]]>
laravel 和 thinkphp 选择哪个? tag:www.v2ex.com,2025-07-26:/t/1147927 2025-07-26T16:26:47Z 2025-08-14T09:55:08Z sxszzhrrt member/sxszzhrrt 202505 最新调研: PHP Opcode 加密混淆哪家强? tag:www.v2ex.com,2025-05-26:/t/1134248 2025-05-26T00:47:27Z 2025-05-29T13:22:10Z heguangyu5 member/heguangyu5 近来看到有网友咨询 PHP 源码保护、防破解的问题, 我也很久没有了解了. 借机更新一下自己的认识, 了解了解市场现状.

PHP 源码保护方案有多种,本文说的是对 opcode 进行加密混淆的方案.一般认为,这种方案的加密强度较强,保护程度也较高.

本文调研了两款 PHP 源码加密产品.调研过程中关注两个重点:

  1. 如何拿到加密混淆后的 opcode.
  2. opcode 能反编译回 PHP 代码吗?

为了不对产品本身造成不好的影响, 我们称这两款产品分别为 AAABBB.

AAA 是国内产品,号称 "最佳 PHP 源代码加密编译器".

BBB 是国外产品,号称 "the most widely trusted PHP protection tool".

先来看 AAA.

AAA

首先,我们需要一段 PHP 代码作为被保护对象.这里选取一个对 PDO 类进行简易封装的 Db 类. 完整源码见: Db.php

然后,使用 AAA 试用版Db.php 加密, 加密时选择 PHP 版本 8.0,加密完成后下载回来,然后将对应的 AAA_loader_80_nts.so也下载回来.

php 的 opcache 扩展有个方便的功能,可以把 php 代码的 opcode dump 出来.

$ ~/tmp/php-8.0.30/bin/php -d 'opcache.enable_cli=1' -d 'opcache.opt_debug_level=0x10000' ../Db.php $_main: ; (lines=1, args=0, vars=0, tmps=0) ; (before optimizer) ; /home/hgy/Downloads/php-opcode-test/Db.php:1-97 ; return [] RANGE[0..0] 0000 RETURN int(1) OurBlog_Db::__construct: ; (lines=36, args=0, vars=0, tmps=18) ; (before optimizer) ; /home/hgy/Downloads/php-opcode-test/Db.php:9-17 ; return [] RANGE[0..0] 0000 V1 = NEW 3 string("PDO") 0001 INIT_FCALL 1 96 string("getenv") 0002 SEND_VAL string("DB_HOST") 1 0003 V2 = DO_ICALL 0004 T3 = CONCAT string("mysql:host=") V2 0005 T4 = CONCAT T3 string(";port=") 0006 INIT_FCALL 1 96 string("getenv") 0007 SEND_VAL string("DB_PORT") 1 0008 V5 = DO_ICALL 0009 T6 = CONCAT T4 V5 0010 T7 = CONCAT T6 string(";dbname=") 0011 INIT_FCALL 1 96 string("getenv") 0012 SEND_VAL string("DB_DATABASE") 1 0013 V8 = DO_ICALL 0014 T9 = CONCAT T7 V8 0015 T10 = CONCAT T9 string(";charset=utf8") 0016 SEND_VAL_EX T10 1 // 由于 V2EX 限制主题内容不能超过 20000 个字符,这里删除了余下的 opcode 

现在我们拿到了 Db.php 未加密混淆的 opcode.

再来看看 AAA 加密混淆过的 Db-AAA.php 的 opcode 长什么样. 将 AAA_loader_80_nts.so 加到 php.ini 里并配置好.

~/tmp/php-8.0.30/bin/php -d 'opcache.enable_cli=1' -d 'opcache.opt_debug_level=0x1000' Db-AAA.php 

什么输出都没有.

可以理解,应该是 AAA_loader_80_nts.so 来接管处理 Db-AAA.php, opcache 扩展不起作用了.

那还有什么办法能拿到 opcode 吗?可以用 phpdbg.

$ ~/tmp/php-8.0.30/bin/phpdbg -p* Db-AAA.php function name: (null) L1-97 {main}() /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php - 0x719eb1e09cb0 + 1 ops L97 #0 FETCH_DIM_W<-1> 1 NEXT user class: OurBlog_Db 10 methods: __construct, __clone, getInstance, fetchOne, fetchRow, fetchAll, fetchCol, insert, update, __call function name: __construct L9-17 OurBlog_Db::__construct() /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php - 0x719eb1e065a0 + 36 ops L11 #0 NEW<3> "PDO" @0 L2147483647 #1 YIELD<1> "VTM]\\V" L1073741823 #2 PRE_INC "ssipe " L12 #3 DO_FCALL @1 L12 #4 FETCH_DIM_W "mysql:host=" @1 ~2 L12 #5 FETCH_DIM_W ~2 ";port=" ~1 L2147483647 #6 MATCH_ERROR<1> "VTM]\\V" L1073741823 #7 JMPZ_EX "ssihe " L12 #8 DO_FCALL @3 L12 #9 FETCH_DIM_W ~1 @3 ~2 L12 #10 FETCH_DIM_W ~2 ";dbname=" ~1 L2147483647 #11 MATCH_ERROR<1> "VTM]\\V" L1073741823 #12 JMPZ_EX "usmru~ eyu" L12 #13 DO_FCALL @3 L12 #14 FETCH_DIM_W ~1 @3 ~2 L12 #15 FETCH_DIM_W ~2 ";charset=utf8" ~1 // 由于 V2EX 限制主题内容不能超过 20000 个字符,这里删除了余下的 opcode [Script ended normally] 

不过 phpdbg 输出的 opcode 没有 opcache 输出的易读,比如最后一个函数OurBlog_Db::__call()里的call_user_func_array()没显示完整,只显示了个call_user_func_ar.

有没有办法让 php-8.0.30 的 phpdbg 输出像 opcache 那种样式的 opcode 呢?

这里只所以要强调 php-8.0.30 的 phpdbg , 是因为 php-8.3 的 phpdbg 输出的 opcode 已经和 opcache 风格统一了.

我们可以对 phpdbg 稍做修改,把 opcache 输出 opcode 的代码用在 phpdbg 里,这样就可以了.

给 phpdbg 新加一个参数-p**,来调用 opcache 里的 dump 相关代码.

$ ~/tmp/php-8.0.30/bin/phpdbg -p** Db-AAA.php $_main: ; (lines=1, args=0, vars=0, tmps=0) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:1-97 ; return [] 0000 FETCH_DIM_W int(1) NEXT OurBlog_Db::__construct: ; (lines=36, args=0, vars=0, tmps=4, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:9-17 ; return [class] RANGE[--..136834057266072] 0000 V0 = NEW 3 string("PDO") 0001 YIELD (function) string("VTM]\V") 0002 PRE_INC string("ssipe") 0003 V1 = DO_FCALL 0004 T2 = FETCH_DIM_W string("mysql:host=") V1 0005 T1 = FETCH_DIM_W T2 string(";port=") 0006 MATCH_ERROR string("VTM]\V") 0007 JMPZ_EX string("ssihe") 0008 V3 = DO_FCALL 0009 T2 = FETCH_DIM_W T1 V3 0010 T1 = FETCH_DIM_W T2 string(";dbname=") 0011 MATCH_ERROR string("VTM]\V") 0012 JMPZ_EX string("usmru~eyu") 0013 V3 = DO_FCALL 0014 T2 = FETCH_DIM_W T1 V3 0015 T1 = FETCH_DIM_W T2 string(";charset=utf8") 0016 OP_242 T1 0017 BOOL_XOR string("VTM]\V") 0018 FE_RESET_RW string("ssimy ") 0019 V1 = DO_FCALL 0020 SEND_USER V1 2 0021 MATCH_ERROR string("VTM]\V") 0022 OP_244 string("usmfuy kxt") 0023 V1 = DO_FCALL 0024 SEND_USER V1 3 0025 DO_FCALL 0026 FETCH_DIM_W string("pdo") 0027 FETCH_DIM_W V0 NEXT 0028 EXT_STMT T0 string("FTY") 0029 CASE T0 string("AVMw@TS)FGLU") 0030 T0 = FETCH_DIM_W string("PDO") string("ATTR_ERRMODE") 0031 SR T0 0032 T0 = FETCH_DIM_W string("PDO") string("ERRMODE_EXCEPTION") 0033 GET_CLASS T0 0034 DO_FCALL 0035 FETCH_DIM_W null NEXT OurBlog_Db::__clone: ; (lines=1, args=0, vars=0, tmps=0) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:19-20 ; return [undef, ref, class] RANGE[--..136834057268056] 0000 FETCH_DIM_W null NEXT OurBlog_Db::getInstance: ; (lines=9, args=0, vars=0, tmps=2, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:22-28 ; return [] RANGE[--..2207613190024] 0000 T1 = FETCH_DIM_W string("instance") NEXT 0001 T0 = FETCH_DIM_W T1 null 0002 JMPZ T0 0007 0003 V0 = NEW 0 (self) (exception) 0004 DO_FCALL 0005 FETCH_DIM_W string("instance") NEXT 0006 FETCH_DIM_W V0 NEXT 0007 T0 = FETCH_DIM_W string("instance") NEXT 0008 FETCH_DIM_W T0 NEXT OurBlog_Db::fetchOne: ; (lines=13, args=2, vars=3, tmps=1, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:30-35 ; return [ref, class] RANGE[--..6262542] 0000 CV0($Sh40) = RECV 1 0001 CV1($Sh41) = RECV_INIT 2 array(...) 0002 OP_204 T3 string("FTY") 0003 FETCH_DIM_W T3 string("AA\GXRD") 0004 SEND_USER CV0($Sh40) 1 0005 V3 = DO_FCALL 0006 CV2($Sh42) = FETCH_DIM_W V3 NEXT 0007 BIND_LEXICAL (ref) CV2($Sh42) string("TK\TLTD") 0008 SEND_USER CV1($Sh41) 1 0009 DO_FCALL 0010 OP_216 CV2($Sh42) string("TTMU_cN,Q_V") 0011 V3 = DO_FCALL 0012 FETCH_DIM_W V3 NEXT OurBlog_Db::fetchRow: ; (lines=15, args=3, vars=4, tmps=1, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:37-42 ; return [class] RANGE[--..136834057270096] 0000 CV0($Sh43) = RECV 1 0001 CV1($Sh44) = RECV_INIT 2 array(...) 0002 CV2($Sh45) = RECV_INIT 3 zval(type=11) 0003 DECLARE_LAMBDA_FUNCTION T4 string("FTY") 0004 OP_216 T4 string("AA\GXRD") 0005 SEND_USER CV0($Sh43) 1 0006 V4 = DO_FCALL 0007 CV3($Sh46) = FETCH_DIM_W V4 NEXT 0008 SWITCH_STRING CV3($Sh46) 0008 string("TK\TLTD") 0009 SEND_USER CV1($Sh44) 1 0010 DO_FCALL 0011 BW_XOR CV3($Sh46) string("_\LVH") 0012 SEND_USER CV2($Sh45) 1 0013 V4 = DO_FCALL 0014 FETCH_DIM_W V4 NEXT OurBlog_Db::fetchAll: ; (lines=15, args=3, vars=4, tmps=1, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:44-49 ; return [class] RANGE[--..136834057271416] 0000 CV0($Sh47) = RECV 1 0001 CV1($Sh48) = RECV_INIT 2 array(...) 0002 CV2($Sh49) = RECV_INIT 3 zval(type=11) 0003 OP_204 T4 string("FTY") 0004 OP_246 T4 string("AA\GXRD") 0005 SEND_USER CV0($Sh47) 1 0006 V4 = DO_FCALL 0007 CV3($Sh410) = FETCH_DIM_W V4 NEXT 0008 OP_220 CV3($Sh410) string("TK\TLTD") 0009 SEND_USER CV1($Sh48) 1 0010 DO_FCALL 0011 YIELD_FROM CV3($Sh410) string("WPMT^aM,") 0012 SEND_USER CV2($Sh49) 1 0013 V4 = DO_FCALL 0014 FETCH_DIM_W V4 NEXT OurBlog_Db::fetchCol: ; (lines=15, args=2, vars=3, tmps=1, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:51-56 ; return [ref, class] RANGE[--..136834057272704] 0000 CV0($Sh411) = RECV 1 0001 CV1($Sh412) = RECV_INIT 2 array(...) 0002 OP_222 T3 string("FTY") 0003 FETCH_DIM_W T3 string("AA\GXRD") 0004 SEND_USER CV0($Sh411) 1 0005 V3 = DO_FCALL 0006 CV2($Sh413) = FETCH_DIM_W V3 NEXT 0007 CASE CV2($Sh413) string("TK\TLTD") 0008 SEND_USER CV1($Sh412) 1 0009 DO_FCALL 0010 BIND_LEXICAL (ref) CV2($Sh413) string("WPMT^aM,") 0011 T3 = FETCH_DIM_W string("PDO") string("FETCH_COLUMN") 0012 OP_231 T3 0013 V3 = DO_FCALL 0014 FETCH_DIM_W V3 NEXT OurBlog_Db::insert: ; (lines=55, args=2, vars=7, tmps=5, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:58-74 ; return [class] RANGE[--..136834057274120] 0000 CV0($Sh414) = RECV 1 0001 CV1($Sh415) = RECV 2 0002 JMPNZ CV1($Sh415) 0011 0003 CONCAT T7 string("FTY") 0004 SWITCH_STRING T7 0004 string("RA][") 0005 T8 = DECLARE_ANON_CLASS string("INSERT INTO `") 0006 T8 = DECLARE_ANON_CLASS T8 CV0($Sh414) 0007 T7 = FETCH_DIM_W T8 string("` VALUES (NULL)") 0008 OP_228 T7 0009 DO_FCALL 0010 FETCH_DIM_W null NEXT 0011 CV2($Sh416) = FETCH_DIM_W array(...) NEXT 0012 DEFINED string("PKKVIJ%]A") 0013 SEND_USER CV1($Sh415) 1 0014 V8 = DO_FCALL 0015 V7 = FETCH_DIM_W V8 NEXT 0016 FETCH_DIM_W V7 CV3($Sh417) 0017 T9 = DECLARE_ANON_CLASS string("`") 0018 T9 = DECLARE_ANON_CLASS T9 CV3($Sh417) 0019 T8 = FETCH_DIM_W T9 string("`") 0020 FETCH_DIM_W CV2($Sh416) NEXT 0021 FETCH_DIM_W T8 NEXT 0022 FETCH_DIM_W NEXT 0023 FE_FREE V7 0024 BW_NOT string("X^I[VDD") 0025 FETCH_OBJ_IS THIS string("") 0026 SEND_USER CV2($Sh416) 2 0027 V7 = DO_FCALL 0028 FETCH_DIM_W CV2($Sh416) V7 0029 MUL string("BMKhBEQ%EF") ")30 FE_RESET_RW string(" 0031 T8 = FETCH_DIM_W CV1($Sh415) NEXT 0032 T7 = FETCH_DIM_W T8 int(1) 0033 SEND_USER T7 2 0034 V8 = DO_FCALL 0035 CV4($Sh418) = FETCH_DIM_W V8 string("?") 0036 T8 = DECLARE_ANON_CLASS string("INSERT INTO `") 0037 T8 = DECLARE_ANON_CLASS T8 CV0($Sh414) 0038 T8 = DECLARE_ANON_CLASS T8 string("` (") 0039 T8 = DECLARE_ANON_CLASS T8 CV2($Sh416) 0040 T8 = DECLARE_ANON_CLASS T8 string(") VALUES (") 0041 T8 = DECLARE_ANON_CLASS T8 CV4($Sh418) 0042 CV5($Sh419) = FETCH_DIM_W T8 string(")") 0043 CONCAT T7 string("FTY") 0044 BIND_LEXICAL (ref) T7 string("AA\GXRD") 0045 SEND_USER CV5($Sh419) 1 0046 V7 = DO_FCALL 0047 CV6($Sh420) = FETCH_DIM_W V7 NEXT 0048 OP_216 CV6($Sh420) string("TK\TLTD") 0049 MATCH_ERROR string("SAKWMW!HG]C") 0050 SEND_USER CV1($Sh415) 1 0051 V7 = DO_FCALL 0052 SEND_USER V7 1 0053 DO_FCALL 0054 FETCH_DIM_W null NEXT OurBlog_Db::update: ; (lines=44, args=3, vars=7, tmps=4, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:76-90 ; return [class] RANGE[--..136834057277472] 0000 CV0($Sh421) = RECV 1 0001 CV1($Sh422) = RECV 2 0002 CV2($Sh423) = RECV_INIT 3 string("1") 0003 JMPNZ CV1($Sh422) 0008 0004 V7 = NEW 1 string("Exception") 0005 OP_226 string("update with empty row is not allowed!") 0006 DO_FCALL 0007 FETCH_DIM_W V7 NEXT 0008 CV3($Sh424) = FETCH_DIM_W array(...) NEXT 0009 MUL string("PKKVIJ%]A") 0010 SEND_USER CV1($Sh422) 1 0011 V8 = DO_FCALL 0012 V7 = FETCH_DIM_W V8 NEXT 0013 FETCH_DIM_W V7 CV4($Sh425) 0014 T9 = DECLARE_ANON_CLASS string("`") 0015 T9 = DECLARE_ANON_CLASS T9 CV4($Sh425) 0016 T8 = FETCH_DIM_W T9 string("` = ?") 0017 FETCH_DIM_W CV3($Sh424) NEXT 0018 FETCH_DIM_W T8 NEXT 0019 FETCH_DIM_W NEXT 0020 FE_FREE V7 0021 FETCH_FUNC_ARG (global) string("X^I[VDD") 0022 OP_205 string("") 0023 SEND_USER CV3($Sh424) 2 0024 V7 = DO_FCALL 0025 FETCH_DIM_W CV3($Sh424) V7 0026 T8 = DECLARE_ANON_CLASS string("UPDATE `") 0027 T8 = DECLARE_ANON_CLASS T8 CV0($Sh421) 0028 T8 = DECLARE_ANON_CLASS T8 string("` SET ") 0029 T8 = DECLARE_ANON_CLASS T8 CV3($Sh424) 0030 T8 = DECLARE_ANON_CLASS T8 string(" WHERE ") 0031 CV5($Sh426) = FETCH_DIM_W T8 CV2($Sh423) 0032 POST_INC T7 string("FTY") 0033 BW_XOR T7 string("AA\GXRD") 0034 SEND_USER CV5($Sh426) 1 0035 V7 = DO_FCALL 0036 CV6($Sh427) = FETCH_DIM_W V7 NEXT 0037 OP_220 CV6($Sh427) string("TK\TLTD") 0038 FETCH_FUNC_ARG string("SAKWMW!HG]C") 0039 SEND_USER CV1($Sh422) 1 0040 V7 = DO_FCALL 0041 SEND_USER V7 1 0042 DO_FCALL 0043 FETCH_DIM_W null NEXT OurBlog_Db::__call: ; (lines=10, args=2, vars=2, tmps=2, dynamic, irreducable, extended_stmt, extended_fcall) ; /home/hgy/Downloads/php-opcode-test/AAA/Db-AAA.php:92-95 ; return [!ref, class] RANGE[--..109071675059458] 0000 CV0($Sh428) = RECV 1 0001 CV1($Sh429) = RECV 2 0002 DECLARE_LAMBDA_FUNCTION T3 string("FTY") 0003 T2 = FETCH_DIM_W T3 NEXT 0004 T2 = DECLARE_ANON_CLASS CV0($Sh428) 0005 NEW 0 string("call_user_func_array") T2 0006 SEND_UNPACK CV1($Sh429) 0007 FETCH_DIM_W NEXT 0008 V2 = DO_FCALL 0009 FETCH_DIM_W V2 NEXT [Script ended normally] 

好,现在我们拿到了 AAA 加密混淆过的 opcode.

接下来就要把这些 opcode 给反编译成 PHP 代码. 这可不好弄. 不过好在有 AI 大模型,是时候展现 AI 真正的实力了!

以下是 Google Gemini 反编译的结果:

@see https://g.co/gemini/share/148782890130

大家可以自行对比一下,反正我是被震惊到了!

也有可能 Db.php 的代码较为常见,被 AI 蒙对了.

腾讯元宝 DeepSeek-R1 反编译的结果如下:

@see https://yuanbao.tencent.com/bot/app/share/chat/KUiqoTNjZalJ

AAA 的调研我们先到这里.

AAA 留给我们的问题是, opcode 到底能不能程序化地反编译成 PHP 代码.

BBB

去 BBB 的网站上把 BBB 的 encoder 试用版 和 loader 都下载回来.

BBB 的 encoder 没有 php-8.0 版本的, 那我们就选最高可用版本 php-8.3 的.

同样对 Db.php 进行加密, 得到加密后的文件 Db-BBB.php

./BBB_encoder_evaluation/BBB_encoder.sh -C -x86-64 -83 ../Db.php -o Db-BBB.php 

接下来我们用同样的办法尝试拿到 Db-BBB.php 的 opcode.

BBB_loader_lin_8.3.so 加到 php.ini 里配置好.

先用 opcache 尝试一下:

$ ~/tmp/php-8.3.21/bin/php -d 'opcache.enable_cli=1' -d 'opcache.opt_debug_level=0x1000' Db-BBB.php 

没有任何输出.

再用 phpdbg 尝试一下:

$ ~/tmp/php-8.3.21/bin/phpdbg -p* Db-BBB.php Segmentation fault (core dumped) 

直接 segfault 了.

我们使用 gdb 来调试一下.

$ gdb ~/tmp/php-8.3.21/bin/phpdbg (gdb) b phpdbg_compile_file (gdb) r -p* ../Db.php (gdb) n (gdb) 250 ret = PHPDBG_G(compile_file)(file, type); (gdb) 251 if (ret == NULL) { (gdb) set print pretty on (gdb) p *ret $1 = { type = 2 '\002', arg_flags = "\000\000", fn_flags = 100663296, function_name = 0x0, scope = 0x0, prototype = 0x0, num_args = 0, required_num_args = 0, arg_info = 0x0, attributes = 0x0, run_time_cache__ptr = 0x0, T = 0, cache_size = 0, last_var = 0, last = 1, opcodes = 0x7ffff5002450, static_variables_ptr__ptr = 0x0, static_variables = 0x0, vars = 0x0, refcount = 0x7ffff5004008, last_live_range = 0, last_try_catch = 0, live_range = 0x0, try_catch_array = 0x0, filename = 0x7ffff505e3c0, line_start = 1, line_end = 97, doc_comment = 0x0, last_literal = 1, num_dynamic_func_defs = 0, literals = 0x7ffff5002470, dynamic_func_defs = 0x0, reserved = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} } (gdb) c Continuing. ... [Script ended normally] [Inferior 1 (process 187677) exited normally] (gdb) r -p* Db-BBB.php (gdb) n (gdb) 250 ret = PHPDBG_G(compile_file)(file, type); (gdb) 251 if (ret == NULL) { (gdb) p *ret $2 = { type = 2 '\002', arg_flags = "\000\000", fn_flags = 100663296, function_name = 0x0, scope = 0x0, prototype = 0x0, num_args = 0, required_num_args = 0, arg_info = 0x0, attributes = 0x0, run_time_cache__ptr = 0x7ffff5004030, T = 1, cache_size = 0, last_var = 0, last = 0, opcodes = 0x1, static_variables_ptr__ptr = 0x0, static_variables = 0x0, vars = 0x0, refcount = 0x7ffff5004020, last_live_range = 0, last_try_catch = 0, live_range = 0x0, try_catch_array = 0x0, filename = 0x0, line_start = 1, line_end = 0, doc_comment = 0x0, last_literal = 0, num_dynamic_func_defs = 0, literals = 0x0, dynamic_func_defs = 0x0, reserved = {0x0, 0x0, 0x0, 0x7ffff5081460, 0x0, 0x0} } (gdb) quit 

对比两次 p *ret 不难发现, 未加密的 Db.php:

opcodes = 0x7ffff5002450, reserved = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}

已加密的 Db-BBB.php:

opcodes = 0x1, reserved = {0x0, 0x0, 0x0, 0x7ffff5081460, 0x0, 0x0}

0x1 显然不是一个有效的内存地址,现在 opcode 在哪儿,不好找了.

再次 gdb 关注一下 zend_compile_filezend_execute_ex:

$ gdb ~/tmp/php-8.3.21/bin/php (gdb) watch zend_compile_file (gdb) watch zend_execute_ex (gdb) r Db-BBB.php ... (gdb) Continuing. Hardware watchpoint 1: zend_compile_file Old value = (zend_op_array *(*)(zend_file_handle *, int)) 0x555555786ae0 <phar_compile_file> New value = (zend_op_array *(*)(zend_file_handle *, int)) 0x7ffff40e5041 0x00007ffff40571d4 in ?? () from /home/hgy/Downloads/php-opcode-test/BBB/BBB/BBB_loader_lin_8.3.so (gdb) Continuing. Hardware watchpoint 2: zend_execute_ex Old value = (void (*)(zend_execute_data *)) 0x55555596adf0 <execute_ex> New value = (void (*)(zend_execute_data *)) 0x7ffff40f2784 0x00007ffff40571de in ?? () from /home/hgy/Downloads/php-opcode-test/BBB/BBB/BBB_loader_lin_8.3.so 

可以看到 BBB_loader_lin_8.3.so 既接管了 zend_compile_file, 又接管了 zend_execute_ex. 这样 opcodes 就成了个黑盒子, 我们既不知道在哪儿,也不知道内容.

这怎么办呢? 是不是说 BBB 这个产品加密强度非常强,值得信赖呢?

别急,网上搜索一下. 很快就找到了这个 https://dezender.xyz/

在 DECODERS 菜单里,就有 BBB PHP 8.3, 可以在线试用,只不过只能 decode 10 行,我们试一下.

成功解密.

<?php /* * @ https://dezender.xyz - BBB Decoder Online * @ Decoder version: 3.0.0 * @ Release: 2025/04/09 */ class OurBlog_Db { protected static $instance = null; protected $pdo = null; protected function __construct(){ $this->pdo = new PDO("mysql:host=" . getenv("DB_HOST") . ";port=" . getenv("DB_PORT") . ";dbname=" . getenv("DB_DATABASE") . ";charset=utf8", getenv("DB_USER"), getenv("DB_PASSWORD")); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } private function __clone(){ } // This is the demo version. Demo version decode 10 lines only. 

简直太强了!!!

总结

  1. AAA 可以比较容易拿到加密混淆过的 opcodes, 但没有成熟可用的反编译工具.
  2. BBB 不容易拿到加密混淆过的 opcodes, 但有成熟的反编译工具.
]]>
有做过基于 webrtc 技术的多人视频会议系统的吗 tag:www.v2ex.com,2025-04-29:/t/1128998 2025-04-29T14:01:38Z 2025-05-01T18:55:06Z tangknox1 member/tangknox1
多人视频会议:主持人(视频直播)+ 其他人多人语音连麦互动,或者 主播直播 + 多人语音连麦与主播互动。 ]]>
2025 年 PHP 路在何方 tag:www.v2ex.com,2025-04-28:/t/1128749 2025-04-28T14:55:52Z 2025-04-27T22:54:52Z Tinywan member/Tinywan 求助 CodeIgniter 输出的 html 格式很乱,缩进乱七八糟的。。。 tag:www.v2ex.com,2025-04-28:/t/1128732 2025-04-28T13:26:59Z 2025-04-27T21:26:59Z TESTFLIGHT2021 member/TESTFLIGHT2021 请问这怎么解决啊? ]]> 有个关于 PHP 的小疑惑 tag:www.v2ex.com,2025-04-26:/t/1128194 2025-04-26T08:09:13Z 2025-05-05T12:46:22Z seansong member/seansong
"is_completed": 0
"is_completed": "0"
"is_completed": false

同一个接口返回的完全一样的同一份数据,为什么每次请求,得到的数据类型都不一样呢,而且是随机的,有时候接口返回的数组中,不同元素里面的这个字段,都会出现不一样的类型,什么场景下会出现这种情况,个别程序员搞成这样的,还是 php 生态中就是这样

弱类型,好像能理解,但就是好奇罢了 ]]>
workman 与阿里云实时语音通信的问题。 tag:www.v2ex.com,2025-04-23:/t/1127661 2025-04-23T17:33:31Z 2025-04-24T17:43:43Z awanganddong member/awanganddong /** * 项目 socket 连接时触发(一次) * 当客户端与 Workerman 建立连接时(TCP 三次握手完成后)触发的回调函数 */ public static function onConnect($client_id) { // 定义连接函数 $cOnnectWs= function () { $ws = new AsyncTcpConnection('ws://dashscope.aliyuncs.com/api-ws/v1/inference'); $ws->headers = [ "Authorization" => "Bearer " . getenv("DASHSCOPE_API_KEY"), ]; $ws->transport = "ssl"; // 连接成功回调 $ws->OnConnect= function ($ws) { self::$aliWs = $ws; echo "connected success:" . $ws->id . "\n"; }; // 当收到消息时 $ws->OnMessage= function ($ws, $data) { var_dump("ali 返回消息", $data); $msg = json_decode($data, true); $channel = self::$sendWsChan; Coroutine::create( function () use ($channel, $msg) { $channel->push($msg); }, ); }; // 连接关闭时进行重连 $ws->OnClose= function ($ws) { echo "连接关闭,尝试重新连接...\n"; self::$aliWs = null; $ws->reConnect(1); }; $ws->OnError= function ($ws) { echo "错误输出" . $ws->error; }; $ws->connect(); }; //定时器触发 if (self::$aliWs) { var_dump("发送事件"); self::$aliWs->send(json_encode($data)); }

初次建立长连接可以发送成功,但是如果阿里云长连接断线重连几次后,发送事件,在 onMessage 里边没有响应。不明白为什么出现这种情况。var_dump("发送事件"); 可以打印数据。但是 onMessage 不存在数据。

]]>
目前 PHP 中比较好用的工作流引擎有哪些? tag:www.v2ex.com,2025-04-18:/t/1126479 2025-04-18T07:18:46Z 2025-05-06T21:49:52Z xmlf member/xmlf
各位大佬帮忙推荐推荐。。。你们平时都用什么工作流引擎? ]]>
有老哥熟悉 OpenCart 的吗? tag:www.v2ex.com,2025-04-10:/t/1124523 2025-04-10T09:06:02Z 2025-04-10T12:58:02Z carl92 member/carl92 开源 PHP Composer 私有 package 管理平台 tag:www.v2ex.com,2025-04-08:/t/1123931 2025-04-08T05:37:40Z 2025-04-07T21:37:40Z akagishigeru member/akagishigeru 在做的一些项目的 package 都是用私有包分发的,用户使用私有包的授权码获取更新包。当然这个项目的主要功能是分发包,而不是保护包的源代码。

感兴趣的可以关注下 😂 Packvault 开源地址

做一些 PHP 项目 然后给用户做分发还是挺有用的

]]>
推荐个好用的集成环境 tag:www.v2ex.com,2025-04-06:/t/1123546 2025-04-06T09:51:52Z 2025-04-06T14:09:14Z flowerwow0316 member/flowerwow0316 https://flyenv.com/ 第一次见这么叼的,能什么都集成在一起的。

]]>
phpinfo(); logo 变成了绿色的大象 tag:www.v2ex.com,2025-04-01:/t/1122589 2025-04-01T07:34:00Z 2025-04-01T08:13:09Z hukei member/hukei PHP 项目有类似前端 monorepo 的概念吗? tag:www.v2ex.com,2025-03-09:/t/1117090 2025-03-09T13:45:55Z 2025-03-10T15:08:18Z tlerbao member/tlerbao
有前端 monorepo 的概念吗哈哈 ]]>
想用 PHP 做微服务开发,有偿求指导 tag:www.v2ex.com,2025-03-05:/t/1116197 2025-03-05T15:52:24Z 2025-03-07T19:42:23Z fan88 member/fan88 目前技术栈是 thinkphp8 + swoole ,现在客户有要求不准我们做单体应用,必须要做微服务开发。 对微服务的开发没什么经验,想咨询有经验的师傅指指门道,有偿 同时,我也考虑部分的服务进行外包,欢迎有经验的师傅来联系

]]>
PHP 语言已经过气了吗 tag:www.v2ex.com,2025-02-18:/t/1112469 2025-02-18T19:12:07Z 2025-03-14T14:16:47Z AndrewHenry member/AndrewHenry 国内 PHP 卷王是鸟哥 tag:www.v2ex.com,2025-01-30:/t/1108309 2025-01-30T00:30:17Z 2025-02-26T10:20:56Z zhouyin member/zhouyin 最近学了 python 原生协程 async await 以及 asyncio 库

回头重新看了 php 协程 发现 php 协程实在太垃圾了 好像有个官方文档 提供了大量示例 协程也可以取消 但它是把所有 job 放入一个队列 取消就是从队列删除 实在太垃圾了 跟 python asyncio 比都不能比 python asyncio 可以直接把函数创建为 task 然后超时的话 在主控制函数里直接 task.cancel()

php 的协程代码也是看起来太啰嗦 一团糟

这几年 php 的份额不断被 js go 蚕食 php 要推出 jit 主要作者是鸟哥 其实鸟哥是国内 php 卷王 必须要维持自己的生存 不断给 php 进化 不然 php 被抛弃了 被扫进历史垃圾堆了 他的日子也要不好过了

]]>
mysql update 更新失败的原因。 tag:www.v2ex.com,2025-01-15:/t/1105356 2025-01-15T11:24:45Z 2025-01-15T13:37:36Z awanganddong member/awanganddong 后台有一个批量操作的功能。

用户审核通过,然后给他的邀请人加优惠价。

伪代码 foreach(users as user){ //通过审核(事务保证) // 给邀请人加优惠卷(事务保证) } 

现在出现的问题是用户加优惠价这个过程,对用户信息更新优惠卷总数失败。

给用户加优惠卷有 2 个插入操作,一个更新操作。

刚才进行批量操作,执行了 20 个用户,其中几个 update 操作失败。所以对这个问题没想明白,mysql rr 级别。

]]>
PHP 程序员是往运维还是前端发展作为拓展呢 tag:www.v2ex.com,2025-01-01:/t/1101729 2025-01-01T01:57:24Z 2025-01-01T13:59:09Z hkui2012 member/hkui2012 php 做了七八年了,之前没分离的时候,jquery 一把梭,现在前端技术 都 vue,nuxt 等前后端分离的那一套,很久没写前端了, 会点 go,公司之前转 java,写过一点,后来公司放弃了,又重回 php 了,运维也懂一点

如果想继续待下去,是学前端还是运维呢 或者卷其它,目前基本的运维会,k8s 懂一些

]]>
求大佬解决一下一个问题 tag:www.v2ex.com,2024-11-27:/t/1093198 2024-11-27T11:50:07Z 2024-11-27T14:34:19Z SEoSean member/SEoSean 我需要 利用 php 来获取访问的蜘蛛 需要在其他页面调用 代码应该怎么写? 求大佬给一个参考 谢谢

]]>
[2024-11-21] PHP 8.4 Released! tag:www.v2ex.com,2024-11-25:/t/1092397 2024-11-25T06:11:17Z 2024-11-27T08:51:44Z coderzhangsan member/coderzhangsan RT

本以为这个版本之后,下个版本会直升 PHP 9.0 ,带来一些新的东西,没想到还是小版本 PHP 8.5 ,虽然 PHP 正在没落,但还是希望她能够越来越完善。

PHP 8.4 Release

]]>
如何获得抖音视频播放量?各位大牛支支招~ tag:www.v2ex.com,2024-11-13:/t/1089278 2024-11-13T09:31:01Z 2024-11-19T15:30:11Z ruobingm member/ruobingm 想问下各位技术大咖,如何获得抖音播放量? 首先,我通过官方通道,h5 分享后获取 item_id,然后调用抖音开放平台的# 查询特定视频的视频数据接口。 这里第一个坑,就是此接口需要通过 PC 扫码获得经营权限作用域,才可以调用。如果是 js 授权,或者 web 授权用 h5 页面授权,都不能拿到带 bind 的作用域。所以只能放弃这个路径。 然后就换了别一个,抖音开放平台数据开放服务,视频数据-“获取视频基础数据” 这个接口,可以获得到播放量,点赞数数据。原本挺好的,就这样结束。 然而,11 月 5 号,把 h5 分享到抖音能力 回收了。 突然回收,我只能做成手动不发,不分享了。但是这样的失去了获得视频 item_id 的途径。所以上面视频数据-“获取视频基础数据” 这个接口废了。。(还有其他途径,1 月份回收了,1000 粉丝以下的数据不返回,接抖音开放平台,心真的很累,说回收就回收~。) 然后我再去通过第三方,tikhub,调用了接口能获得,但是不知道为什么,在本地调用可以,到服务器就不行。服务器调用 20 多分钟就成功 1 2 次。问了那边客服,说也有腾讯云、阿里云服务器的客户出现这样的问题,让买台美国的服务器。。。。我不可能买美国的,我没这个预算,。。 所以又断掉了。。各位大神有没啥渠道能获得指定视频播放等数据,付费的都 ok ,别太贵的。。

]]>
如何优雅地切换 composer 镜像 tag:www.v2ex.com,2024-11-04:/t/1086502 2024-11-04T07:55:26Z 2024-11-04T11:07:39Z yanwushu7 member/yanwushu7 总所周知,使用 composer 的时候,切换镜像是一个刚需。但是操作频率说高不高,说低也不低,往往需要切换镜像的时候偏偏记不住命令。这时,我都会到网上搜索切换镜像的命令,以及有哪些镜像站点可用。

在 Xserver 中优雅地实现了这个功能。只需要鼠标点击一下即可快速切换镜像,即时生效。

Xserver 内置了阿里云、华为云、腾讯云和默认镜像,你可以在这些镜像之间随意快速切换。

xserver

]]>
与银行对接 sm4 国密算法 tag:www.v2ex.com,2024-10-31:/t/1085268 2024-10-31T03:51:14Z 2024-10-31T11:59:45Z cbasil member/cbasil php 相关国密算法的教程很少,找了好久才找到 github 上的包[https://github.com/lizhichao/sm]( https://github.com/lizhichao/sm)
配置好后,一运行就报错秘钥长度为 16 位。跟对方沟通后才知道他们给的是 16 进制的 32 位的 key,在 php 中需要用 hex2bin 转成 16 位。
如果对方给的加密模式是 sm4-cbc,还需要配置 iv 。默认 iv 用 hex2bin('00000000000000000000000000000000')生成。不然解密后的字符串前后会有乱码。填充方法一般常用 pkcs5 和 pkcs7 。这二种填充方式概念上没有什么区别,只是 pkcs5 在 blockSize 上固定为 8 bytes,即数据始终会被切割成 8 个字节的数据块,然后计算需要填充的长度.
加密后的字符编码也有 hex 和 base64 区分。

如果 openssl 版本大于 1.1.1 ,就可以用 openssl_decrypt($data, "sm4", $key, $optiOns=OPENSSL_RAW_DATA,$iv)来解密。 ]]>
Laravel 二手项目,语言切换问题,求解 tag:www.v2ex.com,2024-10-28:/t/1084412 2024-10-28T12:41:30Z 2024-10-28T17:00:08Z Yesr00 member/Yesr00 RT,创建了一个 SetLang 中间件, 代码如下:

if($request->input('lang')){ app()->setLocale($request->input('lang')); }

return $next($request);

首次设置生效,切换页面不带'lang'参的时候在控制器里用 app()->getLocale()就还是只能拿到默认语言。使用 session 也是一样。检查了中间件的顺序,应该是没什么问题,也没有其他的地方有设置语言操作。求 debug 思路

]]>
两道 PHP 题目,都是求 flag 值 tag:www.v2ex.com,2024-10-18:/t/1081563 2024-10-18T08:36:16Z 2024-10-18T12:00:08Z yxzblue member/yxzblue 1 、http://106.15.186.69:8061/

2 、http://106.15.186.69:8097/

]]>
帮忙推荐一个开源论坛源码? tag:www.v2ex.com,2024-09-21:/t/1074541 2024-09-21T01:28:13Z 2024-10-19T21:31:31Z RunningRabbit member/RunningRabbit 最近打算搭建一个论坛,功能可以简单点,界面差不多的论坛类源码帮忙推荐下,谢谢

]]>
Laravel 完成了 A 轮融资🎉 tag:www.v2ex.com,2024-09-05:/t/1070582 2024-09-05T15:02:27Z 2024-09-05T07:02:27Z akagishigeru member/akagishigeru @Taylor

I'm excited to announce that Laravel has raised a $57M Series A in partnership with Accel. I believe that Laravel is the most productive way to build full-stack web applications, and Laravel Cloud will be the platform for shipping those applications that this community deserves. 

首轮融资 5700 万美金。

可以用上免费的 Laravel Cloud 产品咯

]]>
PHP 虽然没落了,但是 PHP 的东西是真的好用 tag:www.v2ex.com,2024-08-30:/t/1069110 2024-08-30T09:45:00Z 2024-11-17T14:37:30Z pixcai member/pixcai 不得不说PHPLaravel框架(或者ThinkPHP)是真的好用啊,很多功能开箱即用,为什么现在的Web框架反而不这么做了呢?

比如GoGinNodeexpressPythonFlask,都是定义路由、返回JSON、模板渲染、静态文件服务这几个基础功能,想要别的需求还得自己装,自己装数据库驱动、搞注册登录、搞邮件发送什么的,更别说结合前端的VueReactTailwind CSS这些了,不会前端的搞配置都要搞半天。

反观Laravel系列,上面的功能要么自带,要么装个库运行下命令就自动全配置好了,简直不要太方便!

狠狠地被惊艳到了!

]]>
世界上最好的 serverless 平台! tag:www.v2ex.com,2024-08-27:/t/1068087 2024-08-27T03:25:31Z 2024-08-28T09:45:38Z est member/est 有这样一个 serverless 平台:

  1. 它支持超级流行的编程语言,语法简单,功能丰富,10 分钟就能学会
  2. 通过 FTP 之类的标准工具部署,而不是脑残的命令行
  3. 不用管理麻烦的服务器。基于非常成熟的 scaling 方案。也有大量网站验证过
  4. 虽然不是 docker ,但是 vhost 够用了。而且要容器化 vhost 也很容易
  5. 让人梦寐以求的“热更新”技术
  6. 超级便宜的月租
  7. 不会吊死在一颗树( vendor lock-in )上。全球遍地的厂商都支持

这么好的 serverless 是哪家?请看本贴所属节点 😆😆😆😆😆

同步发表于我的 blog

]]>
imgur 是怎么做到一个链接新窗口打开 302,但是还能作为图片显示的? tag:www.v2ex.com,2024-08-14:/t/1064947 2024-08-14T07:49:40Z 2024-08-14T18:36:17Z shendaowu member/shendaowu



点击图片会在新窗口打开,然后会 302 到网页。

图这里看到的: t/1064815

没找到合适和节点。谷歌和 AI 都不满意。 ]]>
PHP 编译器 BPC 编译 ThinkPHP8 + PHPUnit 测试 的视频来了 tag:www.v2ex.com,2024-08-13:/t/1064519 2024-08-13T01:22:19Z 2024-08-13T19:07:31Z heguangyu5 member/heguangyu5 视频有点长,1.5 倍速看也没问题.

从一个新装好的 Ubuntu 24.04 Desktop 的虚拟机开始:

  1. composer + PHP 运行项目没问题

  2. 去掉 composer 运行项目没问题

  3. PHP 跑通 PHPUnit 测试

  4. BPC 编译跑通 PHPUnit 测试

  5. PHP 环境下项目各项功能测试没问题

  6. BPC 编译运行起来项目各项功能测试没问题

[ PHP 编译器 BPC 编译 ThinkPHP8 + PHPUnit 测试] https://www.bilibili.com/video/BV1Laece6Epr/?share_source=copy_web&vd_source=fb7701afa4e3c4faaffabbe49ab11ac2

]]>
PHP 编译器 BPC 7.3 发布,成功编译 ThinkPHP8 tag:www.v2ex.com,2024-08-12:/t/1064349 2024-08-12T06:14:04Z 2024-08-12T08:47:58Z heguangyu5 member/heguangyu5 BPC 是一个 PHP Native Compiler,可以将 PHP 源码最终转译成 C 语言,然后编译成动态链接库或可执行程序.

BPC 还内置软件授权机制,最终可实现源码保护、软件授权、二进制打包三合一!

彻底解决 PHP 项目的交付问题.

How BPC Works

  1. 官网 bpc.dev
  2. 安装使用文档

写在前面:

  1. bpc 不是一个开源项目,项目历史可以翻看之前的文章和帖子.
  2. ThinkPHP8 的编译只是一个可行性验证,请勿用于生产环境.

1. 了解 ThinkPHP

之前就有网友提过能不能编译 ThinkPHP,我也翻看过几次 ThinkPHP 的代码,虽然 ThinkPHP 带有测试用例,但是一眼看去很少,不清楚能覆盖到多少功能点.

再加上我自己从没写过 ThinkPHP 的项目,对 ThinkPHP 很陌生,所以一直没有尝试编译.

由于 php 的动态性和 bpc 与 php 的高兼容特点,bpc 编译通过不代表就没问题了,如果有测试用例保障的话,通过运行测试用例可以验证编译后的二进制可执行文件是否与原来的 php 等同.

2. 迁移 OurBlog 到 ThinkPHP

之前写的一本关于 PHPUnit 的电子书《 PHPUnit in Action --- The Easy Way 》里有一个博客项目 OurBlog,虽然功能很简单,但基本的 CURD 都涉及到了,测试也非常完整.

于是就想着把 OurBlog 迁移到 ThinkPHP 试一下,由于有测试保障,这个迁移应该比较好做.

一番折腾之后,迁移成功了! 源码见: bpc-thinkphp8-ourblog

3. BPC 编译: 理清依赖

在使用 composer 创建 ThinkPHP 项目时,可以看到一个 ThinkPHP8 项目有以下依赖:

league/mime-type-detection (1.15.0) league/flysystem (2.5.0) psr/container (2.0.2) psr/http-message (1.1) psr/simple-cache (3.0.0) psr/log (3.0.0) symfony/polyfill-mbstring (v1.29.0) symfony/var-dumper (v7.1.1) topthink/think-helper (v3.1.6) topthink/think-orm (v3.0.20) topthink/framework (v8.0.3) topthink/think-filesystem (v2.0.2) topthink/think-trace (v1.6) 

进一步地,跑通 OurBlog 测试用例, 只需要搞定 3 个依赖就可以了:

  1. psr/simple-cache (3.0.0)
  2. topthink/think-helper (v3.1.6)
  3. topthink/think-orm (v3.0.20)

最后,ourblog 的前端界面能正常运行,不需要搞定所有依赖,只需要搞定下边 4 个就行了:

  1. psr/container (2.0.2)
  2. psr/http-message (1.1)
  3. psr/log (3.0.0)
  4. topthink/framework (v8.0.3)

由于 topthink/framework 和 think-orm 里都包含了 think\Facadethink\Exception , 需要把 think-orm/stubs 独立出来, 再加上 ourblog 项目本身,一共 9 个 repo, 见这里.

4. BPC 编译: 调整代码

要想一行代码不动就能编译成功,几乎是不可能的.

代码调整主要集中在 3 个方面:

  1. 语法: bpc 不支持的语法可以通过 phptobpc 做转换, 转换也不支持的,就需要手动调整代码了.
  2. 判断 php 代码文件是否存在: bpc 编译后都是二进制了,不能使用 is_file/is_dir/file_exists/glob 来判断,要换用 bpc 自己的专有函数.
  3. Reflection: bpc 不支持 Reflection,使用 Reflection 实现的功能要调整成 bpc 的方式.

代码调整的细节可以查看每个 repo 的 commit 历史.

5. 运行

  1. 创建数据库
  2. 运行 tp8-ourblog-althttpd-ubuntu-24.04-amd64

详见: bpc-thinkphp8-ourblog release v0.1

后边可以出个视频来演示一下整个编译运行的过程.

]]>
lumen 框架是不是不更新了, laravel11 都出来这么久了 tag:www.v2ex.com,2024-08-08:/t/1063401 2024-08-08T02:08:12Z 2024-08-24T21:29:49Z fengshils member/fengshils Dux Next 中后台管理系统正式版发布啦 tag:www.v2ex.com,2024-08-06:/t/1062922 2024-08-06T06:23:20Z 2024-08-06T23:05:48Z 349865361 member/349865361 先来个介绍

Dux Next 是一款简单、易用、易开发、独立部署的应用式管理系统,该系统采用各类成熟开源的三方库进行集成封装,免去后续维护的大量工作,自带开箱即用的 CMS 模块,适合长期开发和维护。配合不同的应用可以很轻松的实现您想要的站点,诸如公司网站、博客、CRM 、ERP 、商城等等。

系统组成

后端使用 SlimPHP 框架作为基础,同时整合了 Eloquent ORM 作为主要的数据驱动。同时还集成了各大 Psr 规范化的主流组件,例如 PSR-7 、PSR-11 、PSR-15 等,以确保框架具有高度的可扩展性和互操作性,该封装框架名为 Duxlite 。

前端使用 Refine 无头框架与 TDesign 组件库进行结合,快速开发美观优雅的操作界面。同时提供丰富的 Api 接口,可轻松将数据分享给三方或前台业务,该封装框架名为 Duxrefine 。

目标用户

各类 php 和全栈开发者,开箱即用快速上线项目

授权协议

MIT

仓库地址+开发文档

https://github.com/duxweb/duxcms 麻烦给个 star

https://next.dux.cn/

]]>
m1 brew 安装的 PHP @7.4 使用 pgsql 提示 SIGTRAP 错误 tag:www.v2ex.com,2024-08-06:/t/1062846 2024-08-06T02:56:11Z 2024-08-08T14:04:37Z ytvertigo0927 member/ytvertigo0927 需要适配pgsqltap用的是shivammathur/php/php@7.4 php -m 包含了基本的扩展,pdo_pgsqlpgsql扩展直接编译进了内核

➜ Formula git:(master) php -m [PHP Modules] bcmath bz2 calendar Core ctype curl date dba dom exif FFI fileinfo filter ftp gd gettext gmp hash iconv intl json ldap libxml mbstring mysqli mysqlnd odbc openssl pcntl pcre PDO pdo_dblib pdo_mysql PDO_ODBC pdo_pgsql pdo_sqlite pgsql Phar phpdbg_webhelper posix pspell readline Reflection session shmop SimpleXML soap sockets sodium SPL sqlite3 standard sysvmsg sysvsem sysvshm tidy tokenizer xml xmlreader xmlrpc xmlwriter xsl Zend OPcache zip zlib [Zend Modules] Zend OPcache 

但是new PDO()后直接提示

WARNING: [pool www] child 69580 exited with code 124 after 795.206755 seconds from start 

有人遇到过吗

]]>
有什么 PHP 分类信息网站管理源码? tag:www.v2ex.com,2024-08-03:/t/1062219 2024-08-03T05:18:17Z 2024-08-11T10:40:42Z MuskLee member/MuskLee
1 、发布企业数据库
2 、每个企业下面可以发布产品库

企业和产品都可以自定义字段,比如企业的地区,行业等,产品的各种参数。

有后台就行,前端展示可以自己做。主要不想做后台。

感谢。 ]]>
gpt-4o-mini 回答的答案和自己本机运行的结果不一致,应该相信谁? tag:www.v2ex.com,2024-07-31:/t/1061421 2024-07-31T03:28:05Z 2024-08-01T00:02:47Z frozenway member/frozenway 在 PHP 中,strpos() 函数的第一个参数是要搜索的字符串(即“haystack”),第二个参数是要查找的子字符串(即“needle”)。根据 PHP 的官方文档,strpos() 函数的第一个参数可以是空字符串。

行为说明 当第一个参数( haystack )为空字符串时: strpos() 函数会返回 0 ,表示子字符串( needle )在空字符串的起始位置找到(位置为 0 )。这是因为在 PHP 中,空字符串的起始位置被视为 0 。 示例:

$haystack = ""; $needle = "a"; $position = strpos($haystack, $needle); var_dump($position); // 输出 int(0) 

我在我本地运行得到的结果确是:

$address = ""; $word = 'a'; // $rs = strpos($address, $word); var_dump($rs); //输出 bool(false) 

这是 PHP 版本的问题还是 gpt 回答错了?

]]>
phpMyAdmin 导出 csv 性能为什么感觉强于 PHP 手写导出 csv tag:www.v2ex.com,2024-07-31:/t/1061394 2024-07-31T02:17:14Z 2024-07-31T03:22:25Z zero3412 member/zero3412 而使用如框架 thinkphp 导出相同数据时,就必须要使用分页或其它方法,否者会引起超时或内存不足等乱七八糟的问题;
如果使用原生 PHP 会比框架要好些,但依然没有 phpMyAdmin 那么丝滑。


GPT 的回复
在比较 phpMyAdmin 导出 CSV 的性能与用 PHP 手写导出 CSV 时,以下几点可能解释了为什么 phpMyAdmin 的性能更好:

优化和效率:
phpMyAdmin 是一个成熟的工具,专门设计用于与 MySQL 数据库交互。它在处理大数据集时进行了很多优化,使得导出操作更高效。
手写的 PHP 脚本可能没有进行类似的优化,尤其是如果没有充分考虑内存管理和数据库查询的效率。

使用 LOAD DATA:
在导出 CSV 文件时,phpMyAdmin 可以使用 MySQL 的 LOAD DATA 功能,这比 PHP 手写代码更高效

数据库查询优化:
phpMyAdmin 在执行查询时可能使用了一些最佳实践,例如分页查询以避免内存溢出。
手写的 PHP 脚本如果没有适当的查询优化,可能会导致性能瓶颈,尤其是在处理大数据集时。

内存管理:
phpMyAdmin 可能使用了流式处理( streaming )技术,即逐行读取数据并写入 CSV 文件,从而降低内存占用。
如果手写的 PHP 脚本一次性读取大量数据并存储在内存中,再进行写入操作,内存消耗会非常高,导致性能下降。

文件处理:
phpMyAdmin 可能使用了更高效的文件写入方式,例如使用内置的函数进行批量写入。
手写的 PHP 脚本如果使用的是逐行写入且没有进行缓冲操作,会导致 I/O 操作频繁,从而影响性能。

错误处理和重试机制:
phpMyAdmin 内置了错误处理和重试机制,可以在出现错误时有效地处理和恢复,从而保证导出过程的顺利进行。
手写的 PHP 脚本如果没有完善的错误处理机制,在出现错误时可能会中断导出过程,导致性能不稳定。
缓存机制:

phpMyAdmin 可能利用了缓存机制来提高数据读取和写入的效率。
手写的 PHP 脚本如果没有使用缓存,数据的读写效率可能会较低。
总结来说,phpMyAdmin 在导出 CSV 的过程中,利用了很多性能优化技术和最佳实践,使得其导出性能优于一般的手写 PHP 脚本。如果希望提高手写 PHP 脚本的导出性能,可以参考 phpMyAdmin 的一些优化策略和技术。 ]]>
网站遭劫持了,被注入恶意代码,请教各位大佬如何清除并再次预防? tag:www.v2ex.com,2024-07-29:/t/1060934 2024-07-29T08:34:19Z 2024-08-28T11:39:56Z black9 member/black9 前两天用户反馈移动端网页访问不正常,一查看发现,特定的路由会触发跳转到黄色广告地址。 如: 网址:https://www.网址.com/edu 在 pc 端正常访问,但在移动端就会被跳转到其他网站上。 如修改路由为/edu123也会跳转,修改为/ed则不会。初步判断关键字为edu

进行排查:

  1. dns污染
    网站都是使用了https,排除
  2. nginx
    通过查看日志以及转发记录,发现/edu 被转发到了 php-cgi ,排除
    Imgur
  3. php
    在排查时在看见 v2 中有人遇到类似的问题,于是照着大佬的思路排查,结果发现每个站都被添加了一个 pass.php 文件。 pass.php 根据代码内容搜索,是用Godzilla生成的木马脚本。对网络安全这个块不太懂,有没有大佬知道怎么切底清除这个脚本带来的影响?

初步处理尝试:

清除每个站点下的 pass.php 文件,以及相同时间被创建的一些文件。检查 php.ini 文件以及 so 文件是否被修改。没有发现可疑配置后,重启 php 服务再次访问。 结果还是一样会跳转到其他网站。。。

继续排查:

查看 php 慢日志时,发现请求网页时有执行file_get_contents函数,于是循着文件路径查看,找到了罪魁祸首,项目composer下的autoload_real.php被植入了一行代码,删除掉后网址恢复正常。 慢日志 植入代码

疑问请求:

请问这是利用了 composer 的漏洞吗? composer 的版本是 2.3.7? 然后被植入 pass.php 的文件是不是宝塔的漏洞导致被上传的?因为我看到这些文件都是 www 宝塔用户上传的。

]]>
请教下关于 PHP composer 源 部分包过于老旧,如何解决 tag:www.v2ex.com,2024-07-27:/t/1060564 2024-07-27T11:59:16Z 2024-07-27T19:44:51Z jenson47 member/jenson47 原由 我打算升级下 laravel 版本,结果 g 了

报错内容为:

Your requirements could not be resolved to an installable set of packages. Problem 1 - laravel/framework[v11.9.0, v11.9.1, v11.9.2, 11.x-dev] require fruitcake/php-cors ^1.3 -> found fruitcake/php-cors[dev-feat-setOptions, dev-master, dev-main, dev-test-8.2, v0.1.0, v0.1.1, v0.1.2, v1.0-alpha1, v1.0-alpha2, v1.0-beta1, v1.0.0, v1.0.1, v1.1.0, 1.1.x-dev (alias of dev-main), v1.2.0, 1.2.x-dev (alias of dev-master)] but it does not match the constraint. - Root composer.json requires laravel/framework ^11.9 -> satisfiable by laravel/framework[v11.9.0, v11.9.1, v11.9.2, 11.x-dev]. 

经过对比 某云源 fruitcake/php-cors.jsonpackagist fruitcake/php-cors.json,实际上某云的镜像源并未更新

尝试解决

我通过两种方式尝试解决

  1. 通过清理缓存以及切换源,我使用Composer Registry Manager 去一个个试,结果都不如意。
  2. 使用代理, 我的代理有问题,速度太慢了,部分拉取失败。

不知各位是如何拉取 composer 包的,现在这些源都™的给限制了,开发都没脾气了。。。

]]>
有做过 typecho 插件开发的没? tag:www.v2ex.com,2024-07-24:/t/1059837 2024-07-24T15:37:54Z 2024-07-04T15:36:54Z tangknox1 member/tangknox1 付费做个插件(数据同步)
能做的 PHP 优秀的哥哥
发邮件联系俺:nikesr#yeah.net
把#改为 @ ]]>
借云招 OurATS 的升级变迁来分享下 PHP 语言"动态一时爽,写好测试一直爽"的体验 tag:www.v2ex.com,2024-07-17:/t/1057983 2024-07-17T04:41:13Z 2024-07-17T04:41:13Z heguangyu5 member/heguangyu5 对云招 OurATS 完全不了解的网友可以看下上一篇文章/帖子:

谈谈云招 OurATS 为什么不把开发语言从 PHP 转成 go/java/.net,而是搞了个 PHP 编译器 BPC 来实现本地部署

简言之,云招 OurATS 是一个使用 PHP 语言开发的招聘管理系统.

1. 动态一时爽,重构火葬场?

经常看到有网友接手 PHP/Python 等动态语言开发的项目后,不管是加功能还是调 bug,都痛苦不不堪,直呼"动态一时爽,重构火葬场".

确实,我本人在参与开发云招 OurATS 的前两年里也有类似的经历.

当时面临最大的问题是,不敢轻易加需求.

为什么?

因为经过 2 年的开发,云招 OurATS 的代码库已经稍有规模.每添加一个需求,是否会影响到其它功能模块,都需要好好测试才放心.

可是招聘系统的功能模块多,相互关联错综复杂,要测试的地方太多了,就一个字,累!

当时敏捷开发、TDD 是非常流行的,尤其是 TDD 很明显可以解决我们面临的测试问题,所以当时狠花了一些时间研究怎么搞 TDD.

也去聆听过某知名公司组织的敏捷开发分享交流会,发现方法论比较多,落地实战几乎没有.

于是静下心来,自己搞.

2. 写好测试用例

核心关键点:

  1. 测试用例不好写

    那是被测试代码不容易测,需要重组代码结构,让测试用例好写

  2. 要高覆盖率吗?

    覆盖率只是一个指标/手段,不是目的.

    目的是建立对被测试代码的信心,写个1+1=2 根本不需要测试,因为我们有信心不会出错.

    核心逻辑覆盖到,在担心出错的地方多覆盖,在有信心不出错的地方少覆盖.

  3. 摆正心态,写测试用例肯定会多花点时间,但这是为了不去体验火葬场

3. 云招 OurATS 的升级之路

云招 OurATS 至今已经历了三次大的升级:

  1. Ubuntu 12.04 + MySQL 5.5 + PHP 5.3
  2. Ubuntu 18.04 + MySQL 5.7 + PHP 7.2 / BPC 编译发布
  3. Ubuntu 24.04 + MySQL 8.0 + PHP 8.3 / BPC 编译发布

每次升级,都是先跑通测试用例,这时会有一些 SQL 和 PHP 的兼容性调整,但都很轻松地搞定了.

升级后系统运行地也很平稳,利益于 PHP 近些年来的性能提升,每次升级都还能节省几台服务器.

在 2023 年初,我们使用 PHP 编译器 BPC 成功编译了云招 OurATS.

BPC 完全脱离了 PHP 解释器,每一个 PHP 的语法、扩展函数都重新实现了一遍,这当中如果没有测试做保障,那根本就是不可能完成的任务.

好在 PHP 自身有完善的 phpt 测试用例,BPC 首先通过了这一层测试.

OurATS 相关的每个项目/模块也都有写好的 PHPUnit 测试用例,BPC 也通过了这一关测试.

测试都过了,我们很有信心 BPC 编译后的 OurATS 等价于 PHP 解释执行的 OurATS.

如此大规模的升级都轻松搞定了,日常的小升级更不在话下.

这十多年来,每年我们的代码库都会有相当规模的变化,但这十多年里,我们没再体验过火葬场,我们也非常自信云招 OurATS 和"屎山"不沾边.

4. 更多关于云招 OurATS TDD 经验的分享

  1. 我本人写过一本小电子书《 PHPUnit in Action --- The Easy Way 》,介绍了云招 OurATS 的 PHPUnit 实战经验.
  2. 云招 CTO(不是我)的公众号"小马过河的思考",也分享了不少云招 OurATS 的东西.并且小马过河的文风别具一格,读来轻松幽默,配图也非常有趣,非常推荐大家关注看看.
]]>
谈谈云招 OurATS 为什么不把开发语言从 PHP 转成 go/ Java /.net,而是搞了个 PHP 编译器 BPC 来实现本地部署 tag:www.v2ex.com,2024-07-16:/t/1057636 2024-07-16T02:47:03Z 2024-07-16T08:08:36Z heguangyu5 member/heguangyu5 每次发 PHP 编译器 BPC 新版本 的文章/帖子,都有会网友评论说为什么不用 go/java/.net 或者其它别的语言.

今天就来说说为什么?

1. 缘起

最初决定要开发 BPC 是为了想要本地部署云招 OurATS 的一个核心组件 简历解析器 bob-parser.

bob-parser 是用 PHP 开发的,而 PHP 的源码加密方案没有找到一个 100%可靠的,并且还想解决软件授权问题.

有网友一提到源码保护什么的,老是会说你的代码是有多好,多有价值,给我我也不看,屎山一堆.

这个问题我们后边再讨论.

但云招的做事风格大致就是这样,想要解决一个问题时,就会尽可能地想把这个问题解决好.

开发了 BPC 一段时间后,发现实际上不只能解决 php cli 程序的编译,php web 项目通过编译成动态链接库当作 module 嵌入 apache 就好了,再进一步,引入了 althttpd, apache 也不需要了.

2. 背景

云招 OurATS 是一个招聘管理系统, ATS 是 Applicant Tracking System 的缩写.

非这个领域的人一开始往往会把 ATS 和招聘渠道(Jobboard)弄混.

招聘渠道是指 Boss 直聘/智联招聘/51job 等面向求职者的网站.

企业从招聘渠道获取到简历后,或者说候选人把简历投递给企业后,下一步进行 简历筛选/征求用人部门意见/安排面试/Offer 审批/Offer 发放... 等工作时需要的 申请追踪系统 就是 ATS.

当然现在的招聘渠道企业后台可能也有一部分 ATS 的功能.

云招 OurATS 没怎么搞市场推广,所以虽然我们从 2010 年就开始做了,很多网友可能没听说过.

3. 友商

这里列几个大家可能听过/用过的招聘管理系统.

  1. 飞书招聘
  2. 北森招聘管理系统
  3. Moka

4. 为什么不换开发语言

有些网友认为开发一套招聘管理系统没什么难的,找几个人搞个半年还能搞不出来?

我们来看看实际案例.

北森在 2019 重构了它的招聘管理系统,在其官网发布的文章中这样说:

2019 年,北森基于 Nature Design3.0“高效、愉悦、温暖”的设计理念,历时 3 年,斥资 2 亿人民币,重塑新一代体验优先的招聘管理系统。

文章链接: https://www.beisen.com/res/848.html

显然,北森的这次重构应该没有更换技术栈,从其 招聘的岗位 来看,开发语言应该是 java/.net.

在不更换开发语言的情况下,重做一个招聘管理系统的成本是 3 年 + 2 亿人民币.

如果换语言的,成本恐怕不只这么多了.

那么这个历时 3 年,斥资 2 亿人民币,重塑新一代的招聘系统有惊艳了市场吗?看看北森在港股的表现就知道了.

在脉脉上经常看到 Moka 比北森好的评价,可是在脉脉上 Moka 比北森裁员裁和还狠.

如果还有网友不信邪,可以下水试一试,反正国内做 ATS 的也没几家,机会还有.

云招 OurATS 从 2010 年开始,到今年已经持续开发了 15 年,代码库现存代码上千万行,换语言重构的成本不好估量.

而 PHP 编译器 BPC 从开始开发到成功编译云招 OurATS,用了 3 年,资金投入约 500 万人民币.

说到底,PHP 真是世界上最好的语言呀!

5. 再说说 BPC 编译带来的好处

首先,完美解决了源码保护,软件授权这两大基本需求.

如果换 java/.net 的话,这两个语言的反编译比 PHP 成熟多了.

GraalVM 和 .NET 8 的 Native AOT 是否好用还不好说.

如果换 go 的话,源码保护是没问题,但需要解决软件授权的问题,当然 java/.net 也需要解决这个问题.

BPC 编译还带来了额外好处:

  1. 软件交付变得简单了.

    整个云招 OurATS 招聘系统被编译成了一个二进制可执行文件,日常升级维护就是替换这一个文件(当然整个系统的运行还需要其它几个辅助程序).

  2. 运行环境更安全了.

    生产环境不需要 PHP 解释器,因为 PHP 源码已经被 BPC 最终转译成 C,然后编译成可执行文件了.

    也就是说,服务器上不能执行 PHP 代码,很多针对 PHP 的攻击手段失效了.

  3. 合作方式更灵活

    PHP 项目源码保护的一个做法是使用编译型语言编写部分核心逻辑,然后其它代码开源.

    有了 BPC 之后,完全可以把核心 PHP 代码编译成动态链接库,其它部分开源.

6. 最后说说 BPC 的美中不足

BPC 的目标是源码保护和软件授权,现阶段没有在生成代码和运行性能上做特别的优化.

因此虽然是编译成 C,但性能在大多数场景下还不如解释执行的 PHP 快.

所以如果是性能敏感的项目慎用.

]]>
关于 PHP 启动的进程怎么退出的问题 tag:www.v2ex.com,2024-07-15:/t/1057466 2024-07-15T09:06:42Z 2024-07-15T09:53:58Z dzdh member/dzdh 经典套娃。

场景这样的使用 symfony process 启动了一个 php 进程。

symfony 启动使用的命令是:

$commandline = '{ ('.$commandline.') <&3 3<&- 3>/dev/null & } 3<&0;'; $commandline .= 'pid=$!; echo $pid >&3; wait $pid 2>/dev/null; code=$?; echo $code >&3; exit $code' 

启动成功后进程树类似:(假设执行的是 php -r "sleep(10);")

|---master |-----|---(PID:100) sh -c php -r "sleep(10);" |-----------|---(PID:101) php -r "sleep(10);"

当在 master 中执行 posix_kill(101, 15) sigterm后。sh 立刻退出了。剩余一个 php -r "sleep(10);" 还在跑。

通过这种形式启动的进程应该怎么“优雅”的退出呢?

]]>
ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86