
还在为 Nginx + PHP + 数据库 的复杂安装而头疼吗? WNMP 一键包,让这一切变成——一行命令搞定。
apt install -y curl && curl -fL https://wnmp.org/zh/wnmp.sh -o wnmp.sh && chmod +x wnmp.sh && bash wnmp.sh
一分钟安装完整 Web 环境:
系统自动优化:
安全为先 · 默认即最优:
面向开发者与站长的真正“零阻力”方案: 无论你是独立开发者、云服务商、还是边缘节点运维者,WNMP 让服务器环境部署变得和安装浏览器一样简单。 轻量、稳定、可复制 —— 一次配置,永久受益。
官方网站: https://wnmp.org 社区支持:QQ 群 1075305476 | Telegram @wnmps Github:[url]https://github.com/lowphpcom/wnmp[/url] 开源协议:GPLv3
WNMP 不仅仅是一个脚本,它是下一代 PHP 运行环境生态的起点 —— 基于 LOWPHP 的常驻内存架构,未来将带来原生级的高性能 PHP 体验。
]]>python:
@workflow.defn class SleepForDaysWorkflow: # Send an email every 30 days, for the year @workflow.run async def run(self) -> None: for i in range(12): # Activities have built-in support for timeouts and retries! await workflow.execute_activity( send_email, start_to_close_timeout=timedelta(secOnds=10), ) # Sleep for 30 days (yes, really)! await workflow.sleep(timedelta(days=30)) ruby:
# Send an email every 30 days, for the year class SleepForDaysWorkflow < Temporalio::Workflow::Definition def execute 12.times do # Activities have built-in support for timeouts and retries! Temporalio::Workflow.execute_activity( SendEmailActivity, start_to_close_timeout: 10 ) # Sleep for 30 days (yes, really)! Temporalio::Workflow.sleep(30 * 24 * 60 * 60) end end end C#:
[Workflow] public class SleepForDaysWorkflow { // Send an email every 30 days, for the year [WorkflowRun] public async Task RunAsync() { for (int i = 0; i < 12; i++) { // Activities have built-in support for timeouts and retries! await Workflow.ExecuteActivityAsync( (Activities act) => act.SendEmail(), new() { StartToCloseTimeout = TimeSpan.FromSeconds(10) }); // Sleep for 30 days (yes, really)! await Workflow.DelayAsync(TimeSpan.FromDays(30)); } } } PHP:
class SleepForDaysWorkflow implements SleepForDaysWorkflowInterface { // Send an email every 30 days. public function sleepForDays(): void { for ($i = 0; $i < 12; $i++) { // Activities have timeouts, and will be retried by default! $this->sendEmailActivity->sendEmail(); // Sleep for 30 days (yes, really)! Workflow::sleep(30 * 24 * 60 * 60) } } } 感觉对于 java 程序员 php 的心智负担好小啊
]]>多项目使用微信公众号access_token,每个项目的access_token不共享,就会:
{"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest, could get access_token by getStableAccessToken, more details at https://mmbizurl.cn/s/JtxxFh33r rid: "} "重复获取将导致上次获取的 access_token 失效。建议开发者使用中控服务器统一获取和刷新 access_token"
文档:https://developers.weixin.qq.com/doc/oplatform/developers/dev/AccessToken.html
工作这么多年,还没仔细看过微信文档。
搞中控服务器麻烦,也懒着换成 reids 。
扩展包用的: w7corp/easywechat
默认用文件的缓存,保证用同个文件就行。
$app->getAccessToken()->getKey() $app->getAccessToken()->getToken() // 不同版本,方法不一样 $app->base->getAccessToken()->getTokenKey() $app->base->getAccessToken()->getToken() 一开始我以为扩展包的坑,一个个排查,还去对比缓存文件的 token。
最后一看微信文档,我擦 结案了。
]]>在访问 hello work 场景下,和 go http 标准库接近,2 万左右 qps 。 
同时新增了更加便捷的查询
$db = open("mysql", "root:root@/temp"); $db->ping(); Database\registerDefaultConnection($db); @Table("users") class User { public int $id; @Column("name") public string $userName; public int $age; public float $coin; @Column("create_at") public string $createAt; } $data = DB<User>(); echo "=== 基础查询 ===\n"; $user = $data->where("id = ?", 100)->first(); dump($user); 除了 array 数组外,还有增强数组
$list = new List<int>(); $list->add(1); $list->add(2); $list->add(3); // 手动迭代 $list->rewind(); while ($list->valid()) { echo "索引: " . $list->key() . ", 值: " . $list->current() . "\n"; $list->next(); } KV 方式的泛型类
$map = new HashMap<string, int>(); $map->put("apple", 10); $map->put("banana", 20); $map->put("orange", 30); ]]>$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 求助高手帮我改写以上代码。
$result = "Hello World" |> strtoupper(...) |> str_shuffle(...) |> trim(...); 同样语法 JS 那边卡了很久,PHP 直接上了。
]]>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' ] ]); 会话允许在多个请求之间持久化 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'); ]]>本人是独立开发者,所以更趋向于了解整个开发流程的每个细节,毕竟除了自己,没有任何合作团队。 我认为说再多也没有拿作品说话比较实在。 本人以前就职于香港找换店和餐饮项目开发的公司。带领团队开发。但就是由于本人的观点是非主流观点,导致手下有部分人是不服从的。
由于 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 的开发环境做一个贡献。希望大家参与讨论。前提是站在平等的位置上。不过你讽刺挖苦也无所谓,对于我来说,早已经锻炼出了死猪不怕开水烫的本事。
]]>PHP 源码保护方案有多种,本文说的是对 opcode 进行加密混淆的方案.一般认为,这种方案的加密强度较强,保护程度也较高.
本文调研了两款 PHP 源码加密产品.调研过程中关注两个重点:
为了不对产品本身造成不好的影响, 我们称这两款产品分别为 AAA 和 BBB.
AAA 是国内产品,号称 "最佳 PHP 源代码加密编译器".
BBB 是国外产品,号称 "the most widely trusted PHP protection tool".
先来看 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 的 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_file 和 zend_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. 简直太强了!!!
/** * 项目 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 不存在数据。
]]>package 都是用私有包分发的,用户使用私有包的授权码获取更新包。当然这个项目的主要功能是分发包,而不是保护包的源代码。 感兴趣的可以关注下 😂 Packvault 开源地址
做一些 PHP 项目 然后给用户做分发还是挺有用的
回头重新看了 php 协程 发现 php 协程实在太垃圾了 好像有个官方文档 提供了大量示例 协程也可以取消 但它是把所有 job 放入一个队列 取消就是从队列删除 实在太垃圾了 跟 python asyncio 比都不能比 python asyncio 可以直接把函数创建为 task 然后超时的话 在主控制函数里直接 task.cancel()
php 的协程代码也是看起来太啰嗦 一团糟
这几年 php 的份额不断被 js go 蚕食 php 要推出 jit 主要作者是鸟哥 其实鸟哥是国内 php 卷王 必须要维持自己的生存 不断给 php 进化 不然 php 被抛弃了 被扫进历史垃圾堆了 他的日子也要不好过了
]]>用户审核通过,然后给他的邀请人加优惠价。
伪代码 foreach(users as user){ //通过审核(事务保证) // 给邀请人加优惠卷(事务保证) } 现在出现的问题是用户加优惠价这个过程,对用户信息更新优惠卷总数失败。
给用户加优惠卷有 2 个插入操作,一个更新操作。
刚才进行批量操作,执行了 20 个用户,其中几个 update 操作失败。所以对这个问题没想明白,mysql rr 级别。
]]>如果想继续待下去,是学前端还是运维呢 或者卷其它,目前基本的运维会,k8s 懂一些
]]>本以为这个版本之后,下个版本会直升 PHP 9.0 ,带来一些新的东西,没想到还是小版本 PHP 8.5 ,虽然 PHP 正在没落,但还是希望她能够越来越完善。
]]>
突然回收,我只能做成手动不发,不分享了。但是这样的失去了获得视频 item_id 的途径。所以上面视频数据-“获取视频基础数据” 这个接口废了。。(还有其他途径,1 月份回收了,1000 粉丝以下的数据不返回,接抖音开放平台,心真的很累,说回收就回收~。) 然后我再去通过第三方,tikhub,调用了接口能获得,但是不知道为什么,在本地调用可以,到服务器就不行。服务器调用 20 多分钟就成功 1 2 次。问了那边客服,说也有腾讯云、阿里云服务器的客户出现这样的问题,让买台美国的服务器。。。。我不可能买美国的,我没这个预算,。。 所以又断掉了。。各位大神有没啥渠道能获得指定视频播放等数据,付费的都 ok ,别太贵的。。 ]]>在 Xserver 中优雅地实现了这个功能。只需要鼠标点击一下即可快速切换镜像,即时生效。
Xserver 内置了阿里云、华为云、腾讯云和默认镜像,你可以在这些镜像之间随意快速切换。
]]>if($request->input('lang')){ app()->setLocale($request->input('lang')); }
return $next($request);
首次设置生效,切换页面不带'lang'参的时候在控制器里用 app()->getLocale()就还是只能拿到默认语言。使用 session 也是一样。检查了中间件的顺序,应该是没什么问题,也没有其他的地方有设置语言操作。求 debug 思路
]]>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的Laravel框架(或者ThinkPHP)是真的好用啊,很多功能开箱即用,为什么现在的Web框架反而不这么做了呢? 比如Go的Gin,Node的express,Python的Flask,都是定义路由、返回JSON、模板渲染、静态文件服务这几个基础功能,想要别的需求还得自己装,自己装数据库驱动、搞注册登录、搞邮件发送什么的,更别说结合前端的Vue、React、Tailwind CSS这些了,不会前端的搞配置都要搞半天。
反观Laravel系列,上面的功能要么自带,要么装个库运行下命令就自动全配置好了,简直不要太方便!
狠狠地被惊艳到了!
]]>vhost 够用了。而且要容器化 vhost 也很容易这么好的 serverless 是哪家?请看本贴所属节点 😆😆😆😆😆
同步发表于我的 blog
]]>
从一个新装好的 Ubuntu 24.04 Desktop 的虚拟机开始:
composer + PHP 运行项目没问题
去掉 composer 运行项目没问题
PHP 跑通 PHPUnit 测试
BPC 编译跑通 PHPUnit 测试
PHP 环境下项目各项功能测试没问题
BPC 编译运行起来项目各项功能测试没问题
[ PHP 编译器 BPC 编译 ThinkPHP8 + PHPUnit 测试] https://www.bilibili.com/video/BV1Laece6Epr/?share_source=copy_web&vd_source=fb7701afa4e3c4faaffabbe49ab11ac2
]]>BPC 还内置软件授权机制,最终可实现源码保护、软件授权、二进制打包三合一!
彻底解决 PHP 项目的交付问题.
写在前面:
之前就有网友提过能不能编译 ThinkPHP,我也翻看过几次 ThinkPHP 的代码,虽然 ThinkPHP 带有测试用例,但是一眼看去很少,不清楚能覆盖到多少功能点.
再加上我自己从没写过 ThinkPHP 的项目,对 ThinkPHP 很陌生,所以一直没有尝试编译.
由于 php 的动态性和 bpc 与 php 的高兼容特点,bpc 编译通过不代表就没问题了,如果有测试用例保障的话,通过运行测试用例可以验证编译后的二进制可执行文件是否与原来的 php 等同.
之前写的一本关于 PHPUnit 的电子书《 PHPUnit in Action --- The Easy Way 》里有一个博客项目 OurBlog,虽然功能很简单,但基本的 CURD 都涉及到了,测试也非常完整.
于是就想着把 OurBlog 迁移到 ThinkPHP 试一下,由于有测试保障,这个迁移应该比较好做.
一番折腾之后,迁移成功了! 源码见: bpc-thinkphp8-ourblog
在使用 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 个依赖就可以了:
最后,ourblog 的前端界面能正常运行,不需要搞定所有依赖,只需要搞定下边 4 个就行了:
由于 topthink/framework 和 think-orm 里都包含了 think\Facade 和 think\Exception , 需要把 think-orm/stubs 独立出来, 再加上 ourblog 项目本身,一共 9 个 repo, 见这里.
要想一行代码不动就能编译成功,几乎是不可能的.
代码调整主要集中在 3 个方面:
is_file/is_dir/file_exists/glob 来判断,要换用 bpc 自己的专有函数.代码调整的细节可以查看每个 repo 的 commit 历史.
tp8-ourblog-althttpd-ubuntu-24.04-amd64详见: bpc-thinkphp8-ourblog release v0.1
后边可以出个视频来演示一下整个编译运行的过程.
]]>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
]]>pgsql,tap用的是shivammathur/php/php@7.4 php -m 包含了基本的扩展,pdo_pgsql和pgsql扩展直接编译进了内核 ➜ 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 有人遇到过吗
]]>行为说明 当第一个参数( 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 回答错了?
]]>