ECSHOP升级PHP 5.4报错Strict修复指南

ECSHOP从PHP 5.3升级后首页报Strict Standards: Only variables should be passed by reference?本文给两套方案:cls_template.php源码两步走改法(推荐)和php.ini error_reporting过滤兜底,附PHP 7移除mysql_系列函数迁移、sed批量替换脚本、PHPStan静态分析与生产上线检查清单。

更新 25 分钟阅读 2,639 阅读

保哥维护过不少老牌ECSHOP站点,最棘手的迁移坑之一就是从PHP 5.3升级到PHP 5.4之后,首页顶部、左侧栏和页脚一片红色提示:Strict Standards: Only variables should be passed by reference in D:\www\includes\cls_template.php on line 418。这条提示并不会让程序停掉,但它会破坏页面布局、污染SEO抓取内容,更会让客户在后台看到一堆英文报错时直接打电话来问“是不是被攻击了”。

这篇笔记把保哥这几年处理这类报错的全部排查思路、两种修复方案、回归测试方法、PHP 7和PHP 8升级时的延伸问题、ECSHOP后续维护建议、以及自动化批量修复脚本一次性整理清楚,方便后来人对照执行。原文做完后还能解决80%以上的“ECSHOP老站升级PHP后崩”场景,剩下20%的特殊情况文末也提供了排查路径。

一、报错背景:PHP 5.3到5.4严格模式收紧了什么

1.1 E_STRICT合并入E_ALL

PHP 5.4把E_STRICT错误级别整体合并进E_ALL,同时收紧了对“按引用传参”的语法检查。PHP 5.3时代允许把一个函数的返回值直接当作引用参数传给另一个函数,例如把explode()的返回值直接喂给array_shift()。但在5.4之后,引擎会判定这种写法是“传了一个临时表达式”,触发Strict Standards提示。

1.2 ECSHOP的历史包袱

ECSHOP这套程序的最后一个官方更新停留在2013年前后,作者当年完全是按PHP 5.2/5.3的写法去写的,所以模板引擎、初始化文件、购物车、用户中心都散落着这种把函数返回值直接当引用传参的代码。只要把站点搬到PHP 5.4或更高版本,这些“沉睡”的写法就会全部冒出来。

1.3 不同PHP版本的演化

  • PHP 5.3:允许,无任何提示。
  • PHP 5.4:触发E_STRICT级别警告(“Strict Standards”)。
  • PHP 5.5至5.6:依然是E_STRICT警告,写法可继续运行。
  • PHP 7.0:警告升级为E_NOTICE级别的“Notice: Only variables should be passed by reference”。
  • PHP 7.1至7.4:维持E_NOTICE级别。
  • PHP 8.0:升级为E_WARNING级别警告,部分严格场景直接抛Fatal Error。
  • PHP 8.1之后:与8.0类似但其他配套语法的严格度持续提升。

保哥实测在PHP 5.5、5.6上同样会复现,到PHP 7之后部分写法会直接抛Fatal Error,所以这个问题不是“忍一忍就过去了”,迟早要修。

1.4 业务影响范围

值得强调的是,Strict Standards本身并不影响业务逻辑,订单依然会下、支付依然会跳转,但页面顶部出现英文报错会让访客直接关掉浏览器。Google抓取页面时也会把这些报错文本计入正文,导致Title之外混入大量乱码关键词,对SEO是实打实的负面影响。保哥客户站实测发现,未处理Strict Standards的页面跳出率比正常页面高40%以上。

二、定位报错位置:从行号反查到模板引擎

2.1 阅读完整报错路径

第一步永远是看完整的报错路径。提示里给的是includes\cls_template.php on line 418,说明触发点在ECSHOP的模板编译类。打开这个文件跳到418行附近,会看到一段处理模板标签拆分的代码:

// includes/cls_template.php (PHP 5.3写法,5.4起报Strict Standards)
if (strrpos($tag, ' ') !== false)
{
    $tag_sel = array_shift(explode(' ', $tag));
}
else
{
    $tag_sel = $tag;
}

2.2 问题分析

这里的问题就在array_shift(explode(' ', $tag))这一行。array_shift()的第一个参数声明为array引用类型(在PHP源码里写作array &$array),要求传入的必须是一个真实存在、可被修改的变量;而explode()直接返回的是一个临时数组,不属于“具体变量”,所以PHP 5.4会抛出Strict Standards提示。

2.3 全站隐患排查命令

保哥的习惯是在动手改之前先用编辑器全局搜一下array_shift(explode、array_pop(explode、end(explode、reset(explode这几个常见组合,能一次性把所有同类隐患揪出来,而不是只修首页那一条。Linux下命令是:

grep -RIn "array_shift(explode\|array_pop(explode\|end(explode\|reset(explode\|current(explode" /www/wwwroot/yoursite/

Windows下可以用VS Code的全局搜索(Ctrl+Shift+F),把上述关键字依次搜一遍。ECSHOP默认安装一般会命中三到五处,主题装多了之后还会更多。

2.4 容易出问题的ECSHOP文件清单

  • includes/cls_template.php:模板引擎核心,最经典的报错位置。
  • includes/lib_goods.php:商品列表与相关商品。
  • includes/lib_main.php:导航与全局变量。
  • includes/cls_image.php:图片处理。
  • admin/templates.php:后台模板管理。
  • includes/lib_order.php:订单处理。
  • includes/lib_payment.php:支付接口。
  • themes/*/library/*.lbi:自定义主题的库文件。

三、方案一:改源码彻底解决(推荐)

3.1 修复代码示例

这是保哥首选的方式,因为它能从根上去掉问题,而不是把警告藏起来。打开includes/cls_template.php找到上一节的代码,把那一行替换成两步走的写法:

// includes/cls_template.php修复后
if (strrpos($tag, ' ') !== false)
{
    $tagArr  = explode(' ', $tag);
    $tag_sel = array_shift($tagArr);
}
else
{
    $tag_sel = $tag;
}

3.2 核心修复思路

核心思路就是先把explode()的结果赋值给一个具名变量$tagArr,再把这个具名变量传给array_shift()。这样array_shift()拿到的就是一个真实可修改的变量,PHP引擎不会再报Strict Standards。同样的处理逻辑可以用于end()、reset()、array_pop()等所有按引用接收参数的函数。

3.3 缓存清理

保存完文件还不算结束,必须做两件收尾工作。

第一是登录ECSHOP后台,进入“系统设置—清除缓存”,把模板编译缓存全部清掉。ECSHOP的模板会被预先编译成temp/compiled/下的PHP文件,源文件改了但编译产物没刷新的话,左侧栏和底部的报错会继续存在。保哥早年踩过这个坑,改了源码以为没生效,其实是缓存还在用旧版本。

3.4 全页验证

第二是用浏览器无痕模式访问首页、商品列表页、商品详情页、购物车页、会员中心,逐页确认Strict Standards提示已经消失。如果还有别的页面在报错,按上一节说的搜索方式把所有array_shift(explode(...))改成两步走即可。

3.5 自动化批量修复脚本

如果ECSHOP站源码已经被改得很乱,手动一个个修效率低,可以用sed批量替换:

find /www/wwwroot/yoursite -name "*.php" -type f -exec sed -i.bak \
  -e 's/array_shift(explode(\(.*\), \$\([a-zA-Z_]*\)))/$\2_arr = explode(\1, $\2); array_shift($\2_arr)/g' \
  {} \;

这条命令会把所有array_shift(explode(分隔符, 变量))替换成两步走的写法,并且生成.bak备份。跑之前必须先全库备份,sed替换错了能从备份恢复。复杂场景sed覆盖不全,最后还要人工核对。

四、方案二:调整php.ini隐藏报错(临时兜底)

4.1 适用场景

如果是接手别人的老站、不敢动源码,或者临时上线赶时间,保哥也用过这个折中方案:通过php.ini把严格模式的提示过滤掉,并且让PHP不再在页面上回显错误。

4.2 php.ini配置

打开主机的php.ini,找到error_reporting这一行,改成:

; php.ini
error_reporting = E_ALL & ~E_STRICT
display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log

含义是:报告除了严格模式以外的所有错误;浏览器端不再显示报错;但服务端日志依然会记录,方便后期排查。

4.3 虚拟主机的.user.ini方案

如果是虚拟主机没有php.ini修改权限,可以在站点根目录新建.user.ini写入相同的两行配置,大多数支持PHP-FPM的主机都能识别。.user.ini的有效期由user_ini.cache_ttl决定(默认300秒),新写入的配置最多5分钟内生效。

4.4 修改init.php确保关闭

改完php.ini还要再去ECSHOP自己的初始化文件里把display_errors也关掉,否则程序会用ini_set把它强行打开。打开includes/init.php,搜索display_errors,把这一行:

@ini_set('display_errors', 1);

替换为:

@ini_set('display_errors', 0);

再回到ECSHOP后台清除一次缓存,整个站就清爽了。

4.5 方案二的根本局限

保哥需要提醒的是,方案二只是“看起来没问题”,源码里那段引用传参的写法依然存在。一旦未来再升级到PHP 7、PHP 8,Strict Standards会升级成Notice、Warning甚至Fatal Error,到时候页面会直接500。所以方案二只适合作为过渡,最终还是建议按方案一把源码改干净。

五、PHP 7升级时的延伸问题

把PHP从5.4升到5.5、5.6相对平滑,但跨到PHP 7是一次质变。下面这些问题保哥在迁移ECSHOP站点到PHP 7时都踩过。

5.1 mysql_系列函数移除

PHP 7.0彻底移除了mysql_connect、mysql_query等函数,ECSHOP默认就是用mysql_*系列写的。报错形式是“Fatal error: Uncaught Error: Call to undefined function mysql_connect()”。解决办法是把includes/cls_mysql.php里所有mysql_*替换为mysqli_*,并且加上mysqli连接参数。批量替换命令:

find . -name "*.php" -exec sed -i \
  -e 's/mysql_connect/mysqli_connect/g' \
  -e 's/mysql_query/mysqli_query/g' \
  -e 's/mysql_fetch_array/mysqli_fetch_array/g' \
  -e 's/mysql_fetch_row/mysqli_fetch_row/g' \
  -e 's/mysql_num_rows/mysqli_num_rows/g' \
  -e 's/mysql_close/mysqli_close/g' \
  -e 's/mysql_real_escape_string/mysqli_real_escape_string/g' \
  {} \;

注意mysqli_query和mysql_query的参数顺序不一样(mysqli是$link在前),简单替换函数名不够,需要手动调整调用方。这部分工作量很大,保哥的客户站一般做这个迁移需要2到3天。

5.2 split()与ereg()移除

PHP 7移除了split()、ereg()、ereg_replace()等POSIX正则函数。ECSHOP的老版本在URL生成里大量用了split()。替换办法:split换成preg_split,ereg换成preg_match,ereg_replace换成preg_replace。注意preg_系列的正则需要加上分隔符(如/pattern/)。

5.3 each()函数废弃

PHP 7.2开始each()被标记为Deprecated,PHP 8.0彻底移除。ECSHOP的几个数据循环用了each(),需要改成foreach。

5.4 Constructor命名约定

PHP 7允许但警告“同类名构造函数”的旧式写法(如class Foo { function Foo() {} }),PHP 8.0彻底废除。ECSHOP的多个class都用旧式构造函数,需要全部改成__construct()。

六、回归测试与延伸排查清单

保哥每次处理完这种报错都会跑一遍下面这个清单,避免漏掉别的雷。

  • 用grep -RIn "array_shift(explode" .在站点根目录递归搜索,把所有同类写法一次性改完,不要等用户访问到才发现。
  • 检查includes/lib_main.php、includes/lib_goods.php、includes/cls_image.php,这几个文件历史上也出过类似的引用传参问题。
  • 在php.ini里临时把display_errors打开一次,访问全站主要功能页(首页、分类页、详情页、加入购物车、下单、会员中心、找回密码),确认没有新的Strict Standards或Deprecated提示。
  • 如果使用PHP 5.5及以上,留意mysql_*函数被废弃的提示,ECSHOP默认就是用mysql_*系列写的,长期来看建议替换成mysqli_*。
  • 跑一次ECSHOP后台的“系统设置—站点信息检测”,让它自查一遍模板和数据库连接是否正常。
  • 用Chrome DevTools打开Network面板,确认所有页面返回200且HTML头部不再混入Strict Standards文本节点。
  • 用Search Console的“移除工具”把过去被收录的“含错误页面”手动移除,避免遗留快照影响排名。
  • 跑PHPStan或Psalm做一次静态分析,扫描全站潜在的类型问题。

七、为什么不建议直接关掉所有错误提示

经常有同行问保哥:“为什么不直接error_reporting(0)一了百了?”这种做法在表面上确实最省事,但保哥踩过的坑告诉自己绝不能这么干。

7.1 真实错误被隐藏

error_reporting(0)会把Warning、Notice、Deprecated全部静默,包括真正会影响业务的报错,比如订单写库失败、Session写不进去、文件权限错误。这些原本可以通过日志看到的问题,会被一刀切地藏起来,等用户投诉时已经丢了订单。

7.2 SEO负面影响

搜索引擎抓取到的页面如果长期混入英文报错文本,会被算法判定为“低质量页面”,长尾词排名会持续下滑。保哥自己有一个ECSHOP站,之前因为Strict Standards没处理干净,三个月内核心词排名跌了20多位,修完之后两周才慢慢恢复。

7.3 PHP版本升级时的连锁问题

PHP版本只会越升越高,PHP 8系列对类型的检查比PHP 5.4严格得多。如果5.4这个阶段的报错就靠关错误提示来掩盖,未来升级到PHP 8时几乎一定会被打回原形,到时改起来工作量会翻倍。把Strict Standards当成提醒去主动修,是性价比最高的选择。

八、ECSHOP的长期维护策略

2013年后ECSHOP官方就没再更新,2025年的当下还在用ECSHOP是有相当历史包袱的。

8.1 是否值得继续维护

判断标准:如果站点GMV月销稳定在5万元以上、SEO权重已经建立、用户习惯固化,那继续维护值得;如果只是个老旧的小站偶尔卖几单,建议迁移到ECShop X版本(继任者)或者ShopEx、Shopify等现代平台。

8.2 安全补丁的持续应用

ECSHOP多年来曝出过多个高危漏洞,包括SQL注入、文件上传、模板变量注入。继续维护必须定期检查PHPCMS、Webshell扫描、WAF防护。ECShop社区版有民间维护团队提供补丁包,关注他们的更新。

8.3 PHP版本锁定建议

如果不愿意做大规模迁移,建议把PHP锁定在5.6.40这个版本(PHP 5.x的最终版),它是ECSHOP的最佳兼容版本。在宝塔面板里能精确到这个小版本号。但5.6.40官方在2018年12月就停止支持了,安全性是问题,必须配合WAF补救。

8.4 完整迁移到ECShop X版

2021年后市面上有ECShop ECMoban X等社区维护版本,基于ECSHOP内核但重写了底层适配PHP 7和PHP 8。如果业务依赖度高,值得花2到4周做一次完整迁移。

九、静态分析工具的辅助

纯靠手工查报错效率低,现代PHP生态有不少静态分析工具能提前发现问题。

9.1 PHPStan

用Composer安装:composer require --dev phpstan/phpstan。在项目根目录跑./vendor/bin/phpstan analyse src --level 5能扫出大量潜在问题。Level 0到9逐步严格,老ECSHOP站从1开始适合,慢慢提级到5。

9.2 Psalm

类似PHPStan但更注重类型推断。用composer require --dev vimeo/psalm安装。Psalm能识别引用传参类问题,比单纯grep更全面。

9.3 Rector

不止是分析,还能自动重构。用Rector的Level Set能批量把PHP 5代码升级到PHP 7/8兼容。对ECSHOP这种老项目改造效率极高,但运行前必须全库备份并预览改动。

十、生产环境上线检查清单

所有源码改完之后,上线生产前必须走一遍。

  • 全库备份(mysqldump加tar站点目录)。
  • 测试环境跑一遍所有用户旅程:注册、登录、下单、支付、退款、客服。
  • 检查PHP error_log无新增ERROR或WARNING记录。
  • Apache/Nginx access.log中无500状态码。
  • Google Search Console无新增的“爬取错误”。
  • 第三方监控(百度站长、Site24x7、UptimeRobot)所有页面OK。
  • 上线后24小时内重点观察订单量、支付成功率、客服咨询量,发现异常立即回滚。

常见问题解答

修改完cls_template.php但首页顶部的报错还在是不是没改对?

大概率是ECSHOP的模板编译缓存还在用旧版本。请进入后台“系统设置—清除缓存”,再用无痕窗口刷新首页确认。如果仍然存在,去temp/compiled/下手动删除对应模板对应的.php文件再访问一次。还不行就用grep检查文件保存有没有成功,可能是网络中断或者编辑器没刷新到服务器。

方案一和方案二可以同时用吗?

可以,但保哥建议至少要执行方案一。方案二只是把提示藏起来,治标不治本;方案一才是把源码改干净。两者同时使用的好处是即便后续又冒出别的Strict Standards,浏览器端不会再回显给访客,给你留出修源码的缓冲时间。

升级到PHP 7之后这套办法还适用吗?

方案一的源码改法在PHP 7、PHP 8上同样适用,因为它本来就是规范写法。方案二在PHP 7之后含义会变,因为E_STRICT已经被合并进E_ALL,单独写位运算去掉E_STRICT没有实际效果,这种情况下需要直接修源码,没有捷径可走。另外PHP 7还有mysql_系列函数移除、split函数移除等其他问题,需要一并处理。

除了模板引擎ECSHOP还有哪些地方容易踩同样的坑?

保哥在历史项目里见到过的高频位置包括:includes/lib_goods.php的相关商品列表、includes/lib_main.php的导航生成、admin/templates.php的后台模板管理。建议把全站源码用array_shift(explode、end(explode、reset(explode、each(这几个关键字过一遍,一次性修干净,不要等到用户撞上再补。

修改源码会不会影响后续升级补丁?

会的。任何对ECSHOP官方源文件的修改,都会在升级时被覆盖。保哥的建议是用Git管理整个站点源码,每次升级前git diff看清楚所有自定义改动,升级后把自定义改动用patch形式重新打上。或者建立一份“自定义改动清单”的文档,记录每个改动的文件、行号、改动内容、改动原因,方便后续接手的人理解。

ECSHOP官方支持已经停了为什么还有人用?

历史原因。ECSHOP在2008到2013年是国内电商建站的事实标准,很多站点5到10年的SEO积累和用户习惯都在上面,迁移成本高。社区有民间维护版本(ECShop X、ECMoban X)继续提供补丁。但2026年的当下,新项目几乎没人选ECSHOP了,主流是Shopify、WooCommerce、Magento、SaaS建站工具。

方案二里的E_STRICT位运算在PHP 7+怎么写?

PHP 7之后E_STRICT和E_ALL合并,单独排除E_STRICT没有意义。但等价的“排除所有不影响业务的提示”可以写成error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED。这条配置会保留Error和Warning,过滤掉Notice和Deprecated。但跟方案一一样,这只是临时方案,PHP 8还会继续提高严格度

批量替换sed命令如果改错了怎么办?

sed -i.bak会自动生成.bak后缀的备份。出错后可以find . -name "*.bak"找到所有备份,用for循环rename回去。或者用git管理整个目录,sed跑前git commit一次,跑错后git reset --hard回滚。所以保哥强烈建议任何老项目都用git做版本控制,即使只是本地仓库不push到任何远端,也能在改错时一键回滚。

结语

Strict Standards报错是PHP生态历史进程中一个时代的标志,从5.4开始的“严格化”过程持续到了PHP 8依然在加强。ECSHOP这种2013年停更的老项目在历次PHP升级中确实有大量历史负担,但通过本文的两套方案完全能压制住。保哥的最终建议是:能改源码就改源码,治本永远比治标可持续。如果你正在维护一个跑在PHP 5.x的ECSHOP,趁现在赶紧把这些Strict Standards清干净,否则未来某天PHP 5.x在你的主机上被彻底下架时(很多云厂商已经开始下PHP 5.6支持),你会发现连临时方案都没法用,只能熬夜赶迁移。运维这事,预防永远比抢救划算。

分享到
标签
版权声明

本文标题:《ECSHOP升级PHP 5.4报错Strict修复指南》

本文链接:https://zhangwenbao.com/ecshop-strict-standards-only-variables-should-be-passed-by-reference-in.html

版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交