分类 PHP 下的文章

最近接到反馈,说网站无法在电脑微信上打开,提示 页面错误!请稍后再试~,开启调试模式后发现报 Undefined offset: 1 in Lang.php line 204

问题定位

经过排查,应该是微信浏览器近期进行了更新,给服务器传递了 Accept-Language: *
然后框架的多语言自动侦测逻辑不严谨,导致了此次错误。

出错文件:library/think/Lang.php
出错代码:

        if (isset($_GET[self::$langDetectVar])) {
            // url 中设置了语言变量
            $langSet = strtolower($_GET[self::$langDetectVar]);
        } elseif (isset($_COOKIE[self::$langCookieVar])) {
            // Cookie 中设置了语言变量
            $langSet = strtolower($_COOKIE[self::$langCookieVar]);
        } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
            // 自动侦测浏览器语言
            preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
            $langSet     = strtolower($matches[1]);
            $acceptLangs = Config::get('header_accept_lang');

            if (isset($acceptLangs[$langSet])) {
                $langSet = $acceptLangs[$langSet];
            } elseif (isset(self::$acceptLanguage[$langSet])) {
                $langSet = self::$acceptLanguage[$langSet];
            }
        }

关键定位:

// 自动侦测浏览器语言
preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches);
// 这里没有判断 preg_match 是否匹配到内容,直接就使用 $matches[1] ,然后就报错了
$langSet     = strtolower($matches[1]);

解决办法

解决办法很简单,先判断 preg_match,再执行后续代码。

调整后代码:

        if (isset($_GET[self::$langDetectVar])) {
            // url 中设置了语言变量
            $langSet = strtolower($_GET[self::$langDetectVar]);
        } elseif (isset($_COOKIE[self::$langCookieVar])) {
            // Cookie 中设置了语言变量
            $langSet = strtolower($_COOKIE[self::$langCookieVar]);
        } elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && preg_match('/^([a-z\d\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches)) {
            // 自动侦测浏览器语言
            $langSet     = strtolower($matches[1]);
            $acceptLangs = Config::get('header_accept_lang');

            if (isset($acceptLangs[$langSet])) {
                $langSet = $acceptLangs[$langSet];
            } elseif (isset(self::$acceptLanguage[$langSet])) {
                $langSet = self::$acceptLanguage[$langSet];
            }
        }

影响版本

目前主要发现影响 ThinkPHP 5.0 和 5.1 版本,已向官方提交修复,但这两个版本已停止更新,不确定是否会合并。

其它

如果不会修复的,本人提供代修复处理,仅需 20 元。>>淘宝链接

命令说明
queue:failed列出所有失败的队列作业
queue:failed-table创建记录失败的队列作业表
queue:flush删除所有失败的队列作业
queue:forget删除一个失败的队列作业
queue:listen监听执行指定的队列
queue:restart在执行当前作业后重新启动队列工作守护进程
queue:retry重试失败的队列作业
queue:table为队列作业数据库表创建迁移
queue:work处理队列中的下一个作业

queue:failed

列出所有失败的队列作业

php think queue:failed
+----+------------+---------+--------------------------------------------------+---------------------+
| ID | Connection | Queue   | Class                                            | Fail Time           |
+----+------------+---------+--------------------------------------------------+---------------------+
| 37 | redis      | default | app\common\job\TaskAttach@updateOtherSubsidy     | 2023-07-07 17:19:43 |
| 36 | redis      | default | app\common\job\TaskAttach@updateOtherSubsidy     | 2023-07-07 16:15:26 |

queue:failed-table

创建记录失败的队列作业表,表名为 queue.php 配置文件中 failed.table 设置的名称。
依赖 think-migration 库,如果没安装,则会提示 Install think-migration first please,可使用 composer require topthink/think-migration 命令安装。

php think queue:failed-table
Migration created successfully!

queue:table

创建队列作业表,表名为 queue.php 配置文件中 connections.database.table 设置的名称。

php think queue:table

queue:flush

删除所有失败的队列作业记录

php think queue:flush
All failed jobs deleted successfully!

queue:forget

删除一个失败的队列作业记录

参数

名称必须说明
id队列作业记录的ID

使用示例

php think queue:forget 123
All failed jobs deleted successfully!

queue:retry

重新发布失败的作业

参数

名称必须说明
id队列作业记录的ID

使用示例

重新发布指定作业

php think queue:retry 123 124 125

重新发布所有作业

php think queue:retry all

queue:listen

监听执行指定的队列

参数

名称必须说明
connection队列的连接名称,默认为queue.php 中的 default 配置

选项

名称必须默认说明
--queuenull要监听的队列名称,默认为连接配置中的queue
--delay0延迟失败作业的时间(单位:秒)
--memory128作业内存限制(单位:M)
--timeout60作业执行时间限制(单位:秒)
--sleep3获取作业前的等待时间(单位:秒)
--tries0失败重试次数,超过该次数则变为失败作业,不再执行(单位:次)

使用示例

php think queue:listen database --queue=default --delay=10 --memory=128 --timeout=60 --sleep=3 --tries=0

queue:work

处理队列中的下一个作业

参数

名称必须说明
connection队列的连接名称,默认为queue.php 中的 default 配置

选项

名称必须默认说明
--queuenull要监听的队列名称,默认为连接配置中的queue
--once 只处理队列中的下一个作业
--delay0延迟失败作业的时间(单位:秒)
--force 即使在维护模式下也强制工作人员运行
--memory128作业内存限制(单位:M)
--timeout60作业执行时间限制(单位:秒)
--sleep3获取作业前的等待时间(单位:秒)
--tries0失败重试次数,超过该次数则变为失败作业,不再执行(单位:次)

使用示例

php think queue:work database --queue=default --once --delay=10 --force --memory=128 --timeout=60 --sleep=3 --tries=0