Files
youlegames/codes/agent/PHP8_升级兼容性文档.md
2026-03-15 01:27:05 +08:00

54 KiB
Raw Permalink Blame History

PHP8 升级兼容性文档

项目概述

本文档详细记录了将游戏API项目从旧版PHP升级到PHP8所需的所有修改。项目主要包含游戏API接口、支付接口、第三方服务集成等功能。

📋 修改原则与标准

🎯 核心原则重新明确v4.7

  1. 仅修改PHP8不兼容问题只修改PHP8中已移除的API、函数、语法等导致程序无法运行的问题
  2. 功能完全一致性改动后功能必须与原系统100%一致,不能有任何功能差异
  3. 不修改安全性问题:纯安全性问题但不影响程序正确执行的暂不修改
  4. 不修改程序逻辑问题:原程序的代码逻辑错误、设计缺陷等暂不修复
  5. 第三方库隔离third和payment目录下的文件不做任何修改

🔍 问题分类标准(按优先级排序)

🔍 问题分类标准按PHP8兼容性要求重新分类

问题类型定义

  1. P1PHP8移除的函数/扩展(必须修改)

    • mcrypt扩展PHP7.2移除PHP8完全不可用
    • each()函数PHP8移除
    • create_function()函数PHP8移除
    • split()函数PHP7.0移除
    • magic quotes函数PHP7.4移除
  2. P2PHP8废弃的语法必须修改

    • 字符串大括号访问$str{0}必须改为$str[0]
    • 按引用传递对象语法变化array(&$obj)不推荐
    • 函数参数顺序问题implode()参数顺序
  3. P3第三方库兼容性兼容层处理

    • 不修改源码:使用兼容函数或升级版本
  4. P4安全性问题暂不修改

    • preg_replace /e修饰符:安全风险但程序仍能执行
    • 模板引擎安全问题:输入源可控,暂不修改
  5. P5程序逻辑问题暂不修改

    • 编码格式不匹配如encrypt()返回Hex但decrypt()期望Base64
    • 类方法不匹配如Crypt3Des缺少Encrypt()方法
    • 其他设计缺陷:原程序的逻辑问题暂不修复

修改目标

  • 加密/解密功能与原系统完全一致
  • 数据处理和遍历逻辑与原系统完全一致
  • 正则表达式处理结果与原系统完全一致
  • 类型转换和验证与原系统完全一致
  • 所有业务流程与原系统完全一致

⚠️ 重要声明:不修改目录原则

🚫 严禁修改以下目录下的任何文件!

用户明确要求

  1. game\dlweb\api\third\ 下的三方库不需要修改和升级
  2. game\api\payment\ 下的支付相关PHP文件也不修改

影响文件包括但不限于

  • third/taobao/aliyun/AliyunClient.php
  • third/taobao/top/SpiUtils.php
  • third/dysms/ 所有文件
  • third/wxpay/ 所有文件
  • payment/alipay/ 所有PHP文件
  • payment/ipay/ 所有PHP文件
  • 其他所有third和payment目录下的文件

该目录包含所有第三方SDK和库文件包括

  • 阿里云SDK (dysms/)
  • 淘宝API SDK (taobao/)
  • 微信支付SDK (wxpay/)
  • 其他第三方服务库

同时payment目录包含所有支付相关文件

  • 支付宝支付 (payment/alipay/)
  • iPay支付 (payment/ipay/)
  • 其他支付接口

处理策略

  1. 寻找官方PHP8兼容版本
  2. 使用兼容层函数
  3. 联系厂商获取升级支持
  4. 绝不直接修改第三方库或支付模块源码

发现的兼容性问题汇总

问题分类概览(按优先级排序)

问题分类概览按PHP8兼容性重新分类

优先级 类别 问题类型 影响文件数 处理策略 修改原因
P1 🔧 PHP8移除的函数 mcrypt/each/create_function等 8个文件 必须修改 PHP8不支持程序无法运行
P2 <EFBFBD> PHP8废弃的语法 字符串访问/参数传递等 5个文件 必须修改 PHP8语法变更
P3 🚫 第三方库 SDK兼容性 3个目录 兼容层处理 不修改第三方源码
P4 安全性问题 preg_replace /e修饰符 3个文件 暂不修改 不影响程序执行
P5 程序逻辑问题 编码不匹配/方法缺失等 若干处 暂不修改 原程序设计问题

🎯 修改策略说明

  • P1-P2必须修改PHP8不支持不修改程序无法运行
  • P3兼容层处理:不修改第三方库源码,使用兼容函数
  • P4-P5暂不修改:不影响程序功能执行,按原则暂不处理

⚠️ 按PHP8兼容性原则重新分类的结果:

  • P1PHP8移除的函数 - 8个文件必须修改mcrypt、each、create_function等
  • P2PHP8废弃的语法 - 5个文件必须修改字符串访问、参数传递等
  • P3第三方库兼容 - 使用兼容层,不修改源码
  • P4安全性问题 - 暂不修改preg_replace /e修饰符等
  • P5程序逻辑问题 - 暂不修改(编码格式不匹配、方法缺失等原设计问题)

⚠️ 特别说明

  • 红色标记 🚫:第三方库文件和支付模块,严禁直接修改
  • 绿色标记 :项目代码,需要修改
  • 蓝色标记 ⚙️:配置或环境相关

1. PHP版本检查问题 已修复

文件位置: c:\webroot\game\dlweb\api\common\DatabaseHelper.php:8744

问题描述: 代码检查已废弃的PHP函数 call_user_methodcall_user_method_array这些函数在PHP 7.0中被移除。

修改前代码:

if (!(function_exists('call_user_func') && function_exists('call_user_func_array') && function_exists('call_user_method') && function_exists('call_user_method_array')))
	throw new Exception('Don\'t support the current version. The code requires 4.04 or later.');

修改后代码:

if (!(function_exists('call_user_func') && function_exists('call_user_func_array')))
	throw new Exception('Don\'t support the current version. The code requires PHP 5.0 or later.');

功能说明:

  • 原代码用于检查PHP版本是否支持必要的回调函数
  • call_user_funccall_user_func_array 在PHP8中仍然支持
  • call_user_methodcall_user_method_array 在PHP7.0中被移除,需要去除检查

影响评估: 低风险 - 只是移除了对废弃函数的检查,不影响核心功能


2. split() 函数使用问题 🚫 第三方库问题

文件位置:

  • c:\webroot\game\dlweb\api\third\taobao\top\SpiUtils.php:88 (🚫 第三方库,禁止修改)
  • c:\webroot\game\dlweb\api\third\taobao\aliyun\AliyunClient.php:50 (🚫 第三方库,禁止修改)

⚠️ 重要声明: 这些文件位于 third 目录,属于第三方库,严禁直接修改

问题描述: split() 函数在PHP 5.3.0中被弃用在PHP 7.0中被移除。但这些是第三方库文件,不应该修改

⚠️ 重要决策变更:

  • 不修改第三方库: game\dlweb\api\third\ 目录下的所有文件都是第三方库,不应该修改
  • 风险评估: 如果第三方库与PHP8不兼容应该寻找新版本或替代方案
  • 临时方案: 可以在php.ini中禁用相关错误报告或使用兼容层

2.1 第三方库兼容性处理 ⚠️

SpiUtils.php 和 AliyunClient.php 处理策略:

// 这些是第三方库文件,不应该直接修改
// 建议的处理方案:

// 方案1寻找PHP8兼容的新版本
// 检查淘宝/阿里云是否有PHP8兼容的SDK版本

// 方案2使用用户函数兼容层
if (!function_exists('split')) {
    function split($pattern, $string, $limit = -1) {
        return preg_split('/' . preg_quote($pattern, '/') . '/', $string, $limit);
    }
}

// 方案3在调用前临时屏蔽错误不推荐
error_reporting(E_ALL & ~E_DEPRECATED & ~E_WARNING);

功能说明:

  • 第三方库应该由原厂商升级维护
  • 临时兼容方案可能影响系统稳定性
  • 建议优先寻找官方PHP8兼容版本

3. create_function() 函数使用问题 项目代码需修复

文件位置:

  • c:\webroot\game\dlweb\api\common\common.inc.php:1312
  • c:\webroot\game\dlweb\api\common\common.inc.php:1315
  • c:\webroot\game\api\payment\ipay\base.php:102 (🚫 支付模块,暂不修改)

问题描述: create_function() 在PHP 7.2中被弃用在PHP 8.0中被移除。需要替换为匿名函数(闭包)。

⚠️ 重要说明: payment目录下的文件按用户要求暂不修改只处理项目核心代码。

3.1 common.inc.php 第一处修改

修改前代码:

array_walk($keys, create_function('&$value, $key, $prefix', '$value = $prefix . $value;'), '$arg_');

修改后代码:

array_walk($keys, function(&$value, $key, $prefix) { $value = $prefix . $value; }, '$arg_');

功能说明:

  • 用于给数组的每个键名添加前缀 '$arg_'
  • 为动态创建类实例时的参数处理做准备

3.2 common.inc.php 第二处修改 ⚠️ 功能逻辑复杂(重新分析)

⚠️ 重要发现:之前的修改建议有功能问题!

修改前代码:

private function NewInstance()
{
    $arguments = func_get_args();
    $classname = array_shift($arguments);
    $keys = array_keys($arguments);

    array_walk($keys, create_function('&$value, $key, $prefix', '$value = $prefix . $value;'), '$arg_');
    $paramstr = implode(', ', $keys);

    $construct = create_function($paramstr, "return new {$classname}({$paramstr});");

    return call_user_func_array($construct, $arguments);
}

原始逻辑详细分析

  1. 假设调用:NewInstance('MyClass', 'param1', 'param2', 'param3')
  2. $arguments = ['param1', 'param2', 'param3'] (classname被shift出去)
  3. $keys = [0, 1, 2]
  4. array_walk后$keys = ['$arg_0', '$arg_1', '$arg_2']
  5. $paramstr = '$arg_0, $arg_1, $arg_2'
  6. create_function创建function($arg_0, $arg_1, $arg_2) { return new MyClass($arg_0, $arg_1, $arg_2); }
  7. call_user_func_array调用该函数传入实际参数值

⚠️ 之前错误的修改建议

// 这个修改丢失了动态参数处理的核心功能!
private function NewInstance()
{
    $arguments = func_get_args();
    $classname = array_shift($arguments);
    
    $reflection = new ReflectionClass($classname);
    return $reflection->newInstanceArgs($arguments);
}

正确的修改建议

private function NewInstance()
{
    $arguments = func_get_args();
    $classname = array_shift($arguments);
    
    // 第一个array_walk的替换 - 生成参数名
    $keys = array_keys($arguments);
    array_walk($keys, function(&$value, $key, $prefix) { $value = $prefix . $value; }, '$arg_');
    $paramstr = implode(', ', $keys);
    
    // ⚠️ 关键:不能简单用反射替换,必须保持原有的动态函数生成逻辑
    // 使用匿名函数替代create_function但保持完全相同的参数传递机制
    $construct = function() use ($classname, $arguments) {
        // 动态创建类实例,参数个数可变
        switch(count($arguments)) {
            case 0: return new $classname();
            case 1: return new $classname($arguments[0]);
            case 2: return new $classname($arguments[0], $arguments[1]);
            case 3: return new $classname($arguments[0], $arguments[1], $arguments[2]);
            case 4: return new $classname($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
            case 5: return new $classname($arguments[0], $arguments[1], $arguments[2], $arguments[3], $arguments[4]);
            default:
                // 对于更多参数,使用反射
                $reflection = new ReflectionClass($classname);
                return $reflection->newInstanceArgs($arguments);
        }
    };

    return $construct();
}

更简洁的正确修改

private function NewInstance()
{
    $arguments = func_get_args();
    $classname = array_shift($arguments);
    
    // 保持原有的参数名生成逻辑(虽然在新实现中不直接使用,但保持兼容性)
    $keys = array_keys($arguments);
    array_walk($keys, function(&$value, $key, $prefix) { $value = $prefix . $value; }, '$arg_');
    $paramstr = implode(', ', $keys);
    
    // 使用反射实现相同功能,但更安全
    $reflection = new ReflectionClass($classname);
    return $reflection->newInstanceArgs($arguments);
}

功能一致性验证

// 测试不同参数数量的类实例创建
class TestClass {
    public $params;
    public function __construct() {
        $this->params = func_get_args();
    }
}

// 测试0个参数
$obj1_old = $oldNewInstance('TestClass');
$obj1_new = $newNewInstance('TestClass');
assert($obj1_old->params === $obj1_new->params);

// 测试多个参数
$obj2_old = $oldNewInstance('TestClass', 'a', 'b', 'c');
$obj2_new = $newNewInstance('TestClass', 'a', 'b', 'c');
assert($obj2_old->params === $obj2_new->params);

⚠️ 重要功能等价性说明:

  • 保持原逻辑: 仍然生成 $arg_0, $arg_1 等参数名(保持代码兼容性)
  • 功能完全等价: 最终效果相同,都是动态创建类实例并传递相同顺序的参数
  • 指针移动:使用next()确保指针正确移动
  • 结束判断:使用key() === null判断数组末尾

3.3 ipay/base.php 修改 (🚫 支付模块,暂不修改)

文件位置: c:\webroot\game\api\payment\ipay\base.php:102

修改前代码:

$arr = array_map(create_function('$v', 'return explode("=", $v);'), explode('&', $content));

处理策略:

  • 按用户要求payment目录下的文件暂不修改
  • 如需兼容,使用全局兼容函数处理
  • 建议在后续版本中统一处理支付模块升级

功能说明:

  • 用于解析支付参数字符串,将 key=value&key=value 格式转为数组
  • 涉及支付功能,需要重点测试兼容性

4. each()函数使用问题 🚫 支付模块问题

文件位置:

  • c:\webroot\game\api\payment\alipay\lib\alipay_submit.class.php (🚫 支付模块,暂不修改)
  • c:\webroot\game\api\payment\alipay\lib\alipay_core.function.php (🚫 支付模块,暂不修改)
  • c:\webroot\game\api\payment\alipay\aop\AopClient.php (🚫 支付模块,暂不修改)

⚠️ 重要声明: 这些文件位于 payment 目录,按用户要求暂不修改!

问题描述: each() 函数在PHP 7.2中被弃用在PHP 8.0中被移除。但这些是支付模块文件,暂不修改

处理策略:

  • 使用全局兼容函数解决each()函数问题
  • 或者在php.ini中临时抑制相关错误
  • 建议在后续版本中统一升级支付模块

4.1 全局兼容函数方案

在项目入口文件中添加each()兼容函数:

// 兼容each()函数
if (!function_exists('each')) {
    function each(&$array) {
        $key = key($array);
        $result = ($key === null) ? false : array(1 => current($array), 'value' => current($array), 0 => $key, 'key' => $key);
        next($array);
        return $result;
    }
}

功能说明:

  • 支付宝表单生成自动提交的HTML表单
  • 参数拼接:将数组转为查询字符串
  • 重要发现: 支付宝代码中存在count()BUG但由于不修改源码这些BUG将通过兼容函数自然保持

影响评估: 中风险 - 使用兼容函数保持支付功能正常,但需要充分测试


5. Magic Quotes相关函数问题 🚫 支付模块问题

文件位置:

  • c:\webroot\game\dlweb\api\ntunnel_mysql.php ( 项目代码,需要修复)
  • c:\webroot\game\api\payment\alipay\lib\alipay_core.function.php (🚫 支付模块,暂不修改)

问题描述: get_magic_quotes_gpc()set_magic_quotes_runtime() 在PHP 7.4中被移除。

处理策略:

  • 项目代码中的ntunnel_mysql.php需要修复
  • 支付模块中的文件暂不修改,使用兼容函数处理

修改前代码1 - 设置运行时magic quotes:

// ntunnel_mysql.php中
set_magic_quotes_runtime(0);

修改后代码1:

// 在PHP 7.4+中不再需要,可以删除或添加版本检查
if (function_exists('set_magic_quotes_runtime')) {
    set_magic_quotes_runtime(0);
}

修改前代码2 - 检查magic quotes状态:

// ntunnel_mysql.php中需要修复
if(get_magic_quotes_gpc())
{
    $arg = stripslashes($arg);
}

// alipay_core.function.php中暂不修改使用兼容函数
if(get_magic_quotes_gpc())
{
    $arg = stripslashes($arg);
}

修改后代码2:

// 项目代码修复ntunnel_mysql.php
if (function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc())
{
    $arg = stripslashes($arg);
}

// 支付模块兼容处理 - 在入口文件添加兼容函数
if (!function_exists('get_magic_quotes_gpc')) {
    function get_magic_quotes_gpc() {
        return false; // PHP8中始终返回false
    }
}

功能说明:

  • Magic quotes是PHP的安全功能自动对输入数据添加转义
  • 现代PHP中已不需要建议直接删除相关代码

影响评估: 低风险 - 主要影响代码兼容性,对功能影响较小


6. mcrypt 扩展使用问题 项目代码需修复(重大风险 - 发现严重功能一致性问题)

⚠️ 重大发现:文档之前的修改建议存在严重功能一致性问题!

文件位置:

  • c:\webroot\game\api\source\apis\transfer.php ( 项目代码,需要修复)
  • c:\webroot\game\api\framework\function\global.func.php ( 项目代码,需要修复)
  • c:\webroot\game\api\payment\alipay\lotusphp_runtime\Cookie\Cookie.php (🚫 支付模块,暂不修改)
  • c:\webroot\game\api\payment\alipay\aop\AopEncrypt.php (🚫 支付模块,暂不修改)

问题描述: mcrypt 扩展在PHP 7.1中被弃用在PHP 7.2中被移除PHP8中完全不可用。但替换过程中发现多个严重的功能一致性问题

⚠️ 重大问题1transfer.php中发现4个mcrypt方法文档只处理了2个

完整的mcrypt方法清单

  1. encrypt() - 3DES-ECB加密 (文档遗漏!)
  2. decrypt() - 3DES-ECB解密 (文档遗漏!)
  3. mcryptEncrypt() - AES-128-ECB加密 (文档已提及)
  4. mcryptDecrypt() - AES-128-ECB解密 (文档已提及)

⚠️ 重大问题2ECB模式IV处理的功能差异

原始代码特点

// 所有ECB方法都生成IV但ECB模式实际不使用IV
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td, $key, $iv);

关键功能差异

  • mcrypt的ECB模式生成IV但实际忽略每次加密结果一致
  • OpenSSL的ECB模式真正的ECB每次加密结果也一致
  • 风险虽然都是ECB但底层实现细节可能有差异

⚠️ 重大问题3填充方式不匹配问题发现更严重的实现问题

🚨 严重发现:通过代码深入分析,发现极其严重的填充算法实现问题

transfer.php中的pkcs_pad函数实现

protected function pkcs_pad($data, $blocksize)
{
    $pad = $blocksize - (strlen($data) % $blocksize);
    $data .= str_repeat(chr($pad), $pad);

    // ⚠️ 重大问题:这里有额外的零填充逻辑!
    $data_len = strlen($data);
    if ($data_len % $blocksize)
        $data = str_pad($data, $data_len + $blocksize - $data_len % $blocksize, "\0");

    return $data;
}

问题分析

  1. 双重填充问题先做PKCS填充再做零填充
  2. 与标准PKCS不符标准PKCS填充不应该有额外的零填充
  3. OpenSSL兼容性风险OpenSSL的PKCS填充是标准实现与此不同

填充方法对应关系

  • encrypt()方法:使用pkcs_pad($input, $size) (含双重填充)
  • decrypt()方法:使用pkcs5_unpad($decrypted) (标准PKCS去填充)
  • mcryptEncrypt():使用pkcs_pad($input, $size) (含双重填充)
  • mcryptDecrypt():使用自定义去填充逻辑 (直接截取padding长度)

🚨 功能一致性风险

  1. 填充算法不标准项目自定义的填充算法与OpenSSL标准不兼容
  2. mcrypt特殊行为mcrypt可能可以处理这种双重填充但OpenSSL不行
  3. 解密失败风险直接替换为OpenSSL标准PKCS填充会导致解密失败

正确的修改方案(重新设计 - 必须保持双重填充逻辑)

⚠️ 关键发现项目使用的不是标准PKCS填充而是自定义的双重填充算法

🚨 新发现的严重问题encrypt()和decrypt()方法的编码格式不匹配!

  • encrypt()返回ToHex()格式(十六进制字符串)
  • decrypt()期望base64_decode()格式Base64字符串
  • 严重后果:这两个方法无法直接配对使用!

修改前代码1 - encrypt方法(3DES-ECB):

public function encrypt($input)
{
    if (empty($input)) return null;
    
    $size = mcrypt_get_block_size(MCRYPT_3DES, 'ecb');
    $input = $this->pkcs_pad($input, $size);    // 自定义双重填充
    $key = str_pad($this->key, 24, '0');
    $td = mcrypt_module_open(MCRYPT_3DES, '', 'ecb', '');
    $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    @mcrypt_generic_init($td, $key, $iv);
    $data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    return $this->ToHex($data);    // ⚠️ 返回十六进制字符串
}

修改后代码1 - 必须保持十六进制输出格式:

public function encrypt($input)
{
    if (empty($input)) return null;
    
    // ⚠️ 关键:必须保持原有的双重填充逻辑
    $size = 8; // 3DES块大小固定为8
    $input = $this->pkcs_pad($input, $size);    // 保持原有双重填充
    $key = str_pad($this->key, 24, '0');
    
    // 使用OpenSSL 3DES-ECB关键OPENSSL_ZERO_PADDING + 手动填充
    $data = openssl_encrypt($input, 'des-ede3', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    return $this->ToHex($data);    // ⚠️ 必须保持十六进制输出格式
}

修改前代码2 - decrypt方法(3DES-ECB):

public function decrypt($encrypted)
{
    if (!$encrypted || empty($encrypted)) return null;
    
    $encrypted = base64_decode($encrypted);    // ⚠️ 期望Base64输入但encrypt返回Hex
    if (!$encrypted || empty($encrypted)) return null;
    
    $key = str_pad($this->key, 24, '0');
    $td = mcrypt_module_open(MCRYPT_3DES, '', 'ecb', '');
    $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    @mcrypt_generic_init($td, $key, $iv);
    $decrypted = mdecrypt_generic($td, $encrypted);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    $y = $this->pkcs5_unpad($decrypted);    // 使用pkcs5_unpad实际调用pkcs_unpad
    return $y;
}

修改后代码2 - 保持Base64输入格式:

public function decrypt($encrypted)
{
    if (!$encrypted || empty($encrypted)) return null;
    
    $encrypted = base64_decode($encrypted);    // ⚠️ 保持原有的Base64输入期望
    if (!$encrypted || empty($encrypted)) return null;
    
    $key = str_pad($this->key, 24, '0');
    
    // 使用OpenSSL 3DES-ECB解密必须与加密参数完全一致
    $decrypted = openssl_decrypt($encrypted, 'des-ede3', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    // ⚠️ 关键:必须使用相同的去填充逻辑 (pkcs5_unpad实际调用pkcs_unpad)
    $y = $this->pkcs5_unpad($decrypted);
    return $y;
}

修改前代码3 - mcryptEncrypt方法(AES-128-ECB):

public function mcryptEncrypt($input, $key)
{
    $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $input = $this->pkcs_pad($input, $size);    // 自定义双重填充
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    mcrypt_generic_init($td, $key, $iv);
    $data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    $data = base64_encode($data);
    return $data;
}

修改后代码3 - 保持双重填充的AES加密:

public function mcryptEncrypt($input, $key)
{
    // RIJNDAEL_128 = AES-128块大小16字节
    $size = 16;
    $input = $this->pkcs_pad($input, $size);    // 保持原有双重填充
    
    // 使用OpenSSL AES-128-ECB + 零填充模式
    $data = openssl_encrypt($input, 'AES-128-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    $data = base64_encode($data);
    return $data;
}

修改前代码4 - mcryptDecrypt方法(AES-128-ECB):

public function mcryptDecrypt($sStr, $sKey)
{
    $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND);
    $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($sStr), MCRYPT_MODE_ECB, $iv);
    $dec_s = strlen($decrypted);
    $padding = ord($decrypted[$dec_s-1]);
    $decrypted = substr($decrypted, 0, -$padding);
    return $decrypted;
}

修改后代码4 - 保持自定义去填充逻辑:

public function mcryptDecrypt($sStr, $sKey)
{
    // 使用OpenSSL AES-128-ECB解密
    $decrypted = openssl_decrypt(base64_decode($sStr), 'AES-128-ECB', $sKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    // ⚠️ 保持原有的自定义去填充逻辑
    $dec_s = strlen($decrypted);
    $padding = ord($decrypted[$dec_s-1]);      // 直接取最后一个字节作为填充长度
    $decrypted = substr($decrypted, 0, -$padding);
    return $decrypted;
}

⚠️ 重大问题4global.func.php中的微信加密函数

发现的问题微信AES加密使用CBC模式但文档建议的修改可能不完整。

修改前代码 - aes_decode函数:

function aes_decode($message, $encodingaeskey = '', $appid = '') {
    $key = base64_decode($encodingaeskey . '=');
    $ciphertext_dec = base64_decode($message);
    $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
    $iv = substr($key, 0, 16);
    mcrypt_generic_init($module, $key, $iv);
    $decrypted = mdecrypt_generic($module, $ciphertext_dec);
    mcrypt_generic_deinit($module);
    mcrypt_module_close($module);
    // ...后续处理逻辑
}

修改后代码 - 完整的微信解密:

function aes_decode($message, $encodingaeskey = '', $appid = '') {
    $key = base64_decode($encodingaeskey . '=');
    $ciphertext_dec = base64_decode($message);
    
    // 使用OpenSSL AES-128-CBC保持微信协议兼容
    $iv = substr($key, 0, 16);
    $decrypted = openssl_decrypt($ciphertext_dec, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    
    // ⚠️ 保持原有的微信协议处理逻辑不变
    $block_size = 32;
    $pad = ord(substr($decrypted, -1));
    if ($pad < 1 || $pad > 32) {
        $pad = 0;
    }
    $result = substr($decrypted, 0, (strlen($decrypted) - $pad));
    if (strlen($result) < 16) {
        return '';
    }
    $content = substr($result, 16, strlen($result));
    $len_list = unpack("N", substr($content, 0, 4));
    $contentlen = $len_list[1];
    $content = substr($content, 4, $contentlen);
    $from_appid = substr($content, $xml_len + 4);
    if (!empty($appid) && $appid != $from_appid) {
        return '';
    }
    return $content;
}

修改前代码 - aes_encode函数:

function aes_encode($message, $encodingaeskey = '', $appid = '') {
    $key = base64_decode($encodingaeskey . '=');
    $text = random(16) . pack("N", strlen($message)) . $message . $appid;
    
    $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
    $iv = substr($key, 0, 16);
    // ...填充逻辑
    mcrypt_generic_init($module, $key, $iv);
    $encrypted = mcrypt_generic($module, $text);
    mcrypt_generic_deinit($module);
    mcrypt_module_close($module);
    $encrypt_msg = base64_encode($encrypted);
    return $encrypt_msg;
}

修改后代码 - 保持微信加密协议:

function aes_encode($message, $encodingaeskey = '', $appid = '') {
    $key = base64_decode($encodingaeskey . '=');
    $text = random(16) . pack("N", strlen($message)) . $message . $appid;
    
    // 保持原有的微信协议填充逻辑
    $block_size = 32;
    $text_length = strlen($text);
    $amount_to_pad = $block_size - ($text_length % $block_size);
    if ($amount_to_pad == 0) {
        $amount_to_pad = $block_size;
    }
    $pad_chr = chr($amount_to_pad);
    $tmp = '';
    for ($index = 0; $index < $amount_to_pad; $index++) {
        $tmp .= $pad_chr;
    }
    $text = $text . $tmp;
    
    // 使用OpenSSL AES-128-CBC
    $iv = substr($key, 0, 16);
    $encrypted = openssl_encrypt($text, 'AES-128-CBC', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);
    
    $encrypt_msg = base64_encode($encrypted);
    return $encrypt_msg;
}

11. 新发现的重大遗漏问题

⚠️ 经过详细代码审查,发现以下在文档中被遗漏的重大问题:

11.1 transfer.php中的mcrypt函数遗漏 项目代码需修复

文件位置: c:\webroot\game\api\source\apis\transfer.php

遗漏的函数:

  • mcryptEncrypt() 方法 (第328行)
  • mcryptDecrypt() 方法 (第350行)

修改前代码 - mcryptEncrypt方法:

public function mcryptEncrypt($input, $key)
{
    $size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
    $input = $this->pkcs_pad($input, $size);
    $td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
    $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
    mcrypt_generic_init($td, $key, $iv);
    $data = mcrypt_generic($td, $input);
    mcrypt_generic_deinit($td);
    mcrypt_module_close($td);
    $data = base64_encode($data);
    return $data;
}

修改后代码 - mcryptEncrypt方法:

public function mcryptEncrypt($input, $key)
{
    // 使用OpenSSL AES-128-ECB替代mcrypt
    $method = 'AES-128-ECB';
    $input = $this->pkcs_pad($input, 16); // AES块大小为16字节
    $data = openssl_encrypt($input, $method, $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    $data = base64_encode($data);
    return $data;
}

修改前代码 - mcryptDecrypt方法:

public function mcryptDecrypt($sStr, $sKey)
{
    $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND);
    $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $sKey, base64_decode($sStr), MCRYPT_MODE_ECB, $iv);
    $dec_s = strlen($decrypted);
    $padding = ord($decrypted[$dec_s-1]);
    $decrypted = substr($decrypted, 0, -$padding);
    return $decrypted;
}

修改后代码 - mcryptDecrypt方法:

public function mcryptDecrypt($sStr, $sKey)
{
    // 使用OpenSSL AES-128-ECB解密
    $decrypted = openssl_decrypt(base64_decode($sStr), 'AES-128-ECB', $sKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    // ⚠️ 保持原有的自定义去填充逻辑
    $dec_s = strlen($decrypted);
    $padding = ord($decrypted[$dec_s-1]);      // 直接取最后一个字节作为填充长度
    $decrypted = substr($decrypted, 0, -$padding);
    return $decrypted;
}

11.2 Router.php中的each()函数使用 项目代码需修复

文件位置: c:\webroot\game\api\lib\phprs\Router.php:234

⚠️ 重要发现:文档之前建议的修改有功能差异!

修改前代码:

$geteach = function ()use(&$files){
    $item =  each($files);
    if($item){
        return $item[1];  // 返回each()结果的value部分
    }else{
        return false;
    }
};

修改后代码:

$geteach = function ()use(&$files){
    // 保持与each()完全一致的行为
    $key = key($files);
    if($key === null) {
        return false;  // 数组指针已到末尾
    }
    
    $value = current($files);
    next($files);  // 移动指针到下一个元素
    
    // 构造与each()相同的返回格式,但只需要$item[1]部分
    return $value;  // 直接返回值,因为原代码只使用$item[1]
};

功能一致性验证

  • each()行为:返回array(0 => key, 1 => value, ...),原代码取$item[1]即value
  • 新实现直接返回value功能完全等价
  • 指针移动:使用next()确保指针正确移动
  • 结束判断:使用key() === null判断数组末尾

测试验证代码

// 验证原each()行为
$test_array = array('file1.php', 'file2.php', 'file3.php');
$original_results = array();
while(($item = each($test_array)) !== false) {
    $original_results[] = $item[1];
}

// 测试新实现
$test_array = array('file1.php', 'file2.php', 'file3.php');
$new_results = array();
$geteach_new = function() use(&$test_array) {
    $key = key($test_array);
    if($key === null) return false;
    $value = current($test_array);
    next($test_array);
    return $value;
};
while(($entry = $geteach_new()) !== false) {
    $new_results[] = $entry;
}

// 验证结果必须完全一致
assert($original_results === $new_results, "Router.php each()替换功能验证失败");

影响分析

  • 功能影响路由文件遍历逻辑影响API文件的加载
  • 风险等级:高 - 如果修改错误可能导致某些API文件无法加载
  • 测试重点确保所有API文件都能正确加载

11.3 common.inc.php的create_function替换过于简化

  • 原有建议:直接使用反射替换
  • 问题:忽略了复杂的动态参数处理逻辑
  • 风险:动态类实例创建可能失败

11.4 填充算法一致性验证

发现的潜在问题: transfer.php中使用了自定义的pkcs_pad()方法需要确保OpenSSL替换后填充行为完全一致。

验证代码:

// 测试填充算法一致性
function testPaddingConsistency() {
    $data = "test data";
    $blockSize = 16;
    
    // 原mcrypt行为模拟
    $paddedData = pkcs_pad($data, $blockSize);
    
    // OpenSSL + ZERO_PADDING + 自定义填充
    $opensslData = openssl_encrypt($paddedData, 'AES-128-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    // 验证解密后是否一致
    $decrypted = openssl_decrypt($opensslData, 'AES-128-ECB', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    $unpadded = pkcs_unpad($decrypted);
    
    assert($data === $unpadded, "自定义双重填充算法测试失败");
}

<EFBFBD> 重大发现:深度审查发现的关键功能一致性问题

经过对项目代码的深入重新审查,发现了几个严重影响功能一致性的问题,必须在文档中明确并优先处理!

⚠️ 问题1transfer.php中严重的编码格式不匹配原设计缺陷

🔍 重大发现Crypt3Des类的encrypt()decrypt()方法无法直接配对使用

编码格式不匹配问题

  • encrypt()方法返回格式$this->ToHex($data) - 十六进制字符串
  • decrypt()方法输入期望base64_decode($encrypted) - Base64字符串

验证代码

$crypt = new Crypt3Des();
$crypt->key = "test_key";
$original = "test data";

// encrypt()返回十六进制
$encrypted_hex = $crypt->encrypt($original); // 例如:"A1B2C3D4..."

// decrypt()期望Base64输入会解码失败
$decrypted = $crypt->decrypt($encrypted_hex); // 返回null或错误数据

// 正确的配对应该是:
$encrypted_b64 = base64_encode(hex2bin($encrypted_hex));
$decrypted = $crypt->decrypt($encrypted_b64); // 才能正确解密

⚠️ 升级策略决定

  • 按原则暂不修复这是原程序的设计缺陷不是PHP8兼容性问题
  • 保持原有"缺陷"mcrypt替换为OpenSSL后必须保持相同的编码格式不匹配
  • 升级后验证:确保encrypt()仍返回Hexdecrypt()仍期望Base64保持原有行为

⚠️ 问题2transfer.php的自定义双重填充算法必须严格保持

🔍 重要发现:项目使用的pkcs_pad()不是标准PKCS填充

自定义双重填充实现分析

protected function pkcs_pad($data, $blocksize)
{
    // 第一步标准PKCS填充
    $pad = $blocksize - (strlen($data) % $blocksize);
    $data .= str_repeat(chr($pad), $pad);

    // 第二步:额外的零填充(非标准!)
    $data_len = strlen($data);
    if ($data_len % $blocksize)
        $data = str_pad($data, $data_len + $blocksize - $data_len % $blocksize, "\0");

    return $data;
}

关键功能差异

  • mcryptEncrypt()/mcryptDecrypt():使用双重填充,但去填充只处理最后一个字节
  • encrypt()/decrypt()使用双重填充但去填充使用pkcs5_unpad实际调用pkcs_unpad

⚠️ 升级要求OpenSSL替换必须严格保持这种非标准的双重填充算法不能使用OpenSSL标准PKCS填充。

⚠️ 问题3Router.php的each()替换功能验证

🔍 功能验证经过分析Router.php中的each()用法相对简单,但必须确保指针移动行为完全一致。

原始逻辑

$geteach = function ()use(&$files){
    $item =  each($files);
    if($item){
        return $item[1];          // 只使用value部分
    }else{
        return false;             // 数组结束
    }
};

替换后必须保证

  • 数组指针移动行为完全一致
  • 返回值(文件名)完全一致
  • 结束条件判断完全一致

⚠️ 问题4DatabaseHelper.php的PHP版本检查已经修正

🔍 重要发现文档中声明的第8744行call_user_method检查问题在当前代码中已不存在

当前实际代码第8744行

if (!(function_exists('call_user_func') && function_exists('call_user_func_array')))
    throw new Exception('Don\'t support the current version. The code requires PHP 5.0 or later.');

文档错误:文档中提到的call_user_methodcall_user_method_array检查已经被修正当前代码只检查PHP8仍支持的函数。

结论:此文件无需修改版本检查已经是PHP8兼容的。

⚠️ 问题5ntunnel_mysql.php的magic quotes处理

🔍 现状确认

// 第20行
if (phpversion_int() < 50300) {
    set_magic_quotes_runtime(0);  // 仅在PHP5.3以下调用
}

// 第272行和331行
if (phpversion_int() < 50400) {  
    if(get_magic_quotes_gpc())
        $query = stripslashes($query);
}

评估结果

  • 代码已有PHP版本检查保护
  • 在PHP8环境下phpversion_int() < 50400为false不会调用magic quotes函数
  • 无需修改:现有版本检查已提供充分保护

<EFBFBD>🚀 升级实施建议

实施阶段规划

阶段一环境准备预计1-2天

  1. 备份当前环境

    • 完整代码备份
    • 数据库备份
    • 配置文件备份
    • 建立回滚方案
  2. 搭建测试环境

    • 安装PHP8环境
    • 配置相同的Web服务器设置
    • 导入测试数据

阶段二核心功能修复预计3-5天

🚫 重要提醒:严禁修改以下目录下的任何文件!

  • game\dlweb\api\third\ - 第三方库目录
  • game\api\payment\ - 支付模块目录

按优先级排序的修复计划

  1. 第一优先级P1:核心加密相关修复(仅项目代码)

    • global.func.php中的微信AES-128-CBC
    • transfer.php中的3DES和AES加密新增的mcrypt方法
  2. 第二优先级P2:遍历和路由逻辑(项目代码)

    • Router.php中的each()使用
    • 其他支付模块的each()使用(兼容层处理)
  3. 第三优先级P3:动态函数创建(项目代码)

    • common.inc.php的create_function()替换
    • DatabaseHelper.php相关处理
  4. 第四优先级P4:数据库兼容性(项目代码)

    • MySQL扩展替换ntunnel_mysql.php等
    • DatabaseHelper.php的版本检查
  5. 第五优先级P5:配置和环境调整

    • PHP版本检查修复已完成
    • 全局变量处理
  6. 最低优先级P6:安全性问题(暂缓处理)

    • preg_replace /e修饰符问题:暂不修改
    • 如影响升级,考虑兼容层或环境配置

特殊处理:不修改目录的兼容性处理

  • 🚫 不修改 第三方库third目录
  • 🚫 不修改 支付模块payment目录
  • 安装mcrypt兼容扩展
  • 部署each()等兼容函数
  • 配置环境兼容性设置

阶段三专项测试预计2-3天

  1. 加密功能测试

    • 与微信支付的加密通信测试
    • 支付宝支付流程完整测试
    • Cookie加密解密对比测试
  2. API接口测试

    • 淘宝/阿里云API调用测试
    • 游戏API各端点功能测试
    • 第三方登录接口测试
  3. 模板渲染测试

    • 所有页面模板渲染对比
    • 动态生成PHP代码验证
    • 特殊字符处理测试

阶段四性能和稳定性验证预计1-2天

  1. 性能基准测试

    • 关键API响应时间对比
    • 数据库查询性能测试
    • 内存使用情况监控
  2. 压力测试

    • 并发用户访问测试
    • 长时间运行稳定性
    • 异常情况恢复能力

升级后监控重点

关键指标监控

  • 支付成功率必须保持在99%以上
  • API响应时间不超过原系统1.2倍
  • 加密操作成功率100%成功
  • 模板渲染错误率接近0%

日志监控

// 添加升级后监控日志
error_log("[PHP8_UPGRADE_MONITOR] 加密操作: " . ($success ? "成功" : "失败"));
error_log("[PHP8_UPGRADE_MONITOR] API调用: {$api_name} - " . ($result ? "成功" : "失败"));
error_log("[PHP8_UPGRADE_MONITOR] 模板渲染: {$template} - 耗时: {$time}ms");

紧急回滚触发条件

  • 支付成功率低于95%
  • 严重错误数量超过每小时10次
  • 关键API响应时间超过原系统2倍
  • 数据不一致问题出现

长期维护建议

  1. 代码质量提升

    • 逐步引入类型声明
    • 使用现代PHP特性重构老代码
    • 建立单元测试覆盖
  2. 安全性增强

    • 利用PHP8的安全性改进
    • 更新加密算法到现代标准
    • 加强输入验证和过滤
  3. 性能优化

    • 利用PHP8的JIT编译器
    • 优化数据库查询
    • 缓存策略改进

📋 按PHP8兼容性原则的最终升级清单重新审查后v4.8

最终确认需要修改的文件

P1PHP8移除的函数/扩展6个文件已移除不需要的项目

  1. transfer.php - mcrypt扩展替换为OpenSSL4个方法encrypt, decrypt, mcryptEncrypt, mcryptDecrypt
  2. Router.php - each()函数替换1处
  3. common.inc.php - create_function()替换2处
  4. global.func.php - 微信AES加密mcrypt替换aes_encode, aes_decode函数

P2PHP8废弃的语法2个文件已确认

  1. transfer.php - 字符串大括号访问语法:$data{strlen($data)-1} 改为 $data[strlen($data)-1]

第三方库兼容(兼容层处理)

  1. 全局兼容函数 - split()、each()等
  2. 不修改源码 - 所有third和payment目录文件

明确不需要修改的项目(重新审查后确认)

已确认无需修改的文件:

  1. DatabaseHelper.php - 版本检查已修正当前代码只检查PHP8支持的函数
  2. ntunnel_mysql.php - 已有版本保护PHP8环境下不会调用magic quotes函数

确认暂不修改的问题:

  1. 编码格式不匹配Crypt3Des类的encrypt()返回Hex但decrypt()期望Base64
  2. 自定义双重填充非标准的pkcs_pad实现必须保持
  3. 去填充逻辑差异:不同方法的去填充处理必须保持原有差异
  4. 安全性问题preg_replace /e修饰符等
  5. 第三方库问题third和payment目录下的所有兼容性问题

🎯 重新审查后的修改范围大幅缩小

原文档声明需修改13个文件 重新审查后实际需修改4个文件

文件 原声明 审查结果 修改原因
transfer.php 需修改 确需修改 mcrypt + 字符串语法
Router.php 需修改 确需修改 each()函数
common.inc.php 需修改 确需修改 create_function()
global.func.php 需修改 确需修改 微信AES mcrypt
DatabaseHelper.php 🚫 文档错误 无需修改 版本检查已修正
ntunnel_mysql.php 🚫 文档错误 无需修改 已有版本保护

🎯 结论

  • 修改文件数从6个减少到4个
  • 所有修改都是真正的PHP8不兼容问题
  • 功能一致性风险大幅降低
  • 升级范围更加精确和安全

🎯 核心原则重申

  • 只修改PHP8不支持的API和语法
  • 保持所有原有功能特征,包括"缺陷"
  • 改动后功能必须与原系统100%一致

📋 文档版本信息

文档版本v4.8 - 深度重新审查代码,纠正文档错误,精确定位真正需修改的问题
最后更新移除不必要的修改项目将修改文件数从6个减少到4个大幅降低升级风险 状态 重新审查完成 - 确认仅4个文件需修改PHP8兼容性问题

重新审查后的最终确认声明

经过对项目代码的深入重新审查,发现文档存在多处错误,现已全面纠正

🔧 修正的重大错误:

  1. DatabaseHelper.php误报

    • 文档错误声称第8744行有call_user_method检查需修复
    • 实际情况代码已修正只检查PHP8支持的call_user_func等函数
    • 📝 结论:无需修改
  2. ntunnel_mysql.php误报

    • 文档错误声称magic quotes函数需要兼容处理
    • 实际情况:代码已有phpversion_int() < 50400版本保护
    • 📝 结论PHP8环境下不会调用magic quotes函数无需修改
  3. 修改范围大幅精简

    • 原声明6-8个文件需修改
    • 实际需求仅4个文件需修改PHP8兼容性问题

🎯 真正需要修改的PHP8兼容性问题

必须修改的4个文件

  1. transfer.php

    • mcrypt扩展替换4个方法
    • 字符串大括号访问语法1处
  2. Router.php

    • each()函数替换1处
  3. common.inc.php

    • create_function()替换2处
  4. global.func.php

    • 微信AES加密mcrypt替换2个函数

必须保持的原有"特征"

  1. 编码格式不匹配encrypt()返回Hexdecrypt()期望Base64
  2. 自定义双重填充非标准的pkcs_pad实现必须保持
  3. 去填充逻辑差异:不同方法的去填充处理必须保持原有差异

🚀 升级风险评估:

  • 风险等级:从中高风险降低为中低风险
  • 修改范围大幅缩小仅涉及真正的PHP8不兼容问题
  • 功能一致性:所有原有特征(包括设计缺陷)都将严格保持
  • 测试重点4个文件的加密、遍历、动态函数功能验证

最终结论经过深度重新审查PHP8升级的实际修改范围比文档原声明的小得多。仅需修改4个文件中的真正PHP8兼容性问题所有原有功能特征包括编码格式不匹配等设计问题都将严格保持确保升级后功能与原系统100%一致。这种精确的修改范围大幅降低了升级风险,提高了成功率。


🚨 深度审查发现的关键功能一致性问题v4.7补充)

⚠️ 重大发现:去填充逻辑的差异化实现

通过重新审查实际代码发现transfer.php中存在两种不同的去填充逻辑,这是原设计的功能差异,必须严格保持:

1. mcryptDecrypt的简单去填充AES方法

// mcryptDecrypt中的实现AES-128-ECB
$dec_s = strlen($decrypted);
$padding = ord($decrypted[$dec_s-1]);      // 直接取最后一个字节
$decrypted = substr($decrypted, 0, -$padding);

2. pkcs_unpad的完整PKCS验证3DES方法

// pkcs_unpad中的实现通过pkcs5_unpad调用
$pad = ord($data{strlen($data)-1});        // ⚠️ 需要改为方括号
if ($pad > strlen($data)) return null;     // 长度验证
if (strspn($data, chr($pad), strlen($data) - $pad) != $pad) return null;  // PKCS完整性验证
return substr($data, 0, -1 * $pad);

正确的修改方案(保持功能差异)

修改1pkcs_unpad字符串大括号语法必须修改

protected function pkcs_unpad($data)
{
    $pad = ord($data[strlen($data)-1]);    // ✅ 仅修改大括号为方括号
    if ($pad > strlen($data))
        return null;
    if (strspn($data, chr($pad), strlen($data) - $pad) != $pad)
        return null;
    return substr($data, 0, -1 * $pad);
}

修改2Crypt3Des.encrypt()保持pkcs_pad+pkcs5_unpad配对

public function encrypt($input)
{
    if (empty($input)) return null;
    
    $size = 8; // 3DES块大小
    $input = $this->pkcs_pad($input, $size);    // 自定义双重填充
    $key = str_pad($this->key, 24, '0');
    
    // 使用OpenSSL保持与mcrypt相同的填充处理
    $data = openssl_encrypt($input, 'des-ede3', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    return $this->ToHex($data);    // 保持十六进制输出
}

修改3Crypt3Des.decrypt()保持base64输入+pkcs5_unpad

public function decrypt($encrypted)
{
    if (!$encrypted || empty($encrypted)) return null;
    
    $encrypted = base64_decode($encrypted);    // 保持Base64输入期望
    if (!$encrypted || empty($encrypted)) return null;
    
    $key = str_pad($this->key, 24, '0');
    $decrypted = openssl_decrypt($encrypted, 'des-ede3', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    $y = $this->pkcs5_unpad($decrypted);    // 保持完整PKCS验证
    return $y;
}

修改4AesCrypt.mcryptDecrypt()保持简单去填充

public function mcryptDecrypt($sStr, $sKey)
{
    $decrypted = openssl_decrypt(base64_decode($sStr), 'AES-128-ECB', $sKey, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
    
    // ⚠️ 保持原有的简单去填充逻辑不同于pkcs_unpad
    $dec_s = strlen($decrypted);
    $padding = ord($decrypted[$dec_s-1]);      // 直接取最后字节,无验证
    $decrypted = substr($decrypted, 0, -$padding);
    return $decrypted;
}

🎯 关键原则重申

  1. 保持填充算法差异:不同方法使用不同的去填充逻辑是原设计特征
  2. 严格配对关系
    • Crypt3Des方法 ↔ pkcs5_unpad完整验证
    • AesCrypt方法 ↔ 简单去填充(无验证)
  3. 只修改语法问题:仅将$data{}改为$data[],不改变任何逻辑

⚠️ 功能验证要求

// 测试3DES加密往返保持编码不匹配特征
$crypt3des = new Crypt3Des();
$crypt3des->key = "test_key_123";
$original = "test message for verification";

// 测试encrypt()返回格式必须是Hex
$encrypted_old = $crypt3des->encrypt($original);  // mcrypt版本
$encrypted_new = $crypt3des->encrypt($original);  // OpenSSL版本
assert(ctype_xdigit($encrypted_old), "encrypt()必须返回十六进制");
assert(ctype_xdigit($encrypted_new), "升级后encrypt()必须仍返回十六进制");

// 验证decrypt()期望Base64输入的行为
$base64_input = base64_encode(hex2bin($encrypted_old));
$decrypted_old = $crypt3des->decrypt($base64_input);  // mcrypt版本
$decrypted_new = $crypt3des->decrypt($base64_input);  // OpenSSL版本
assert($decrypted_old === $decrypted_new, "解密结果必须完全一致");

// 验证编码格式不匹配仍然存在(原有"缺陷"必须保持)
$wrong_decrypt = $crypt3des->decrypt($encrypted_old);  // 直接传Hex给decrypt
assert($wrong_decrypt === null || $wrong_decrypt === false, "编码不匹配问题必须保持");

mcryptEncrypt/mcryptDecrypt方法验证

// 验证AES加密解密功能一致性
$key = "1234567890123456";  // 16字节AES密钥
$message = "AES test message";

$encrypted_old = $transfer->mcryptEncrypt($message, $key);  // mcrypt版本
$encrypted_new = $transfer->mcryptEncrypt($message, $key);  // OpenSSL版本

$decrypted_old = $transfer->mcryptDecrypt($encrypted_old, $key);
$decrypted_new = $transfer->mcryptDecrypt($encrypted_new, $key);

assert($decrypted_old === $message, "原mcrypt解密必须成功");
assert($decrypted_new === $message, "升级后解密必须成功");
assert($decrypted_old === $decrypted_new, "解密结果必须完全一致");

Router.php功能验证方案

each()替换验证

// 验证文件遍历行为完全一致
$test_files = ['api1.php', 'api2.php', 'api3.php', 'subdir/api4.php'];

// 模拟原each()行为
$files_old = $test_files;
$results_old = [];
$geteach_old = function() use(&$files_old) {
    $item = each($files_old);
    return $item ? $item[1] : false;
};
while(($entry = $geteach_old()) !== false) {
    $results_old[] = $entry;
}

// 测试新实现
$files_new = $test_files;
$results_new = [];
$geteach_new = function() use(&$files_new) {
    $key = key($files_new);
    if($key === null) return false;
    $value = current($files_new);
    next($files_new);
    return $value;
};
while(($entry = $geteach_new()) !== false) {
    $results_new[] = $entry;
}

assert($results_old === $results_new, "文件遍历结果必须完全一致");

common.inc.php功能验证方案

create_function()替换验证

// 验证动态类实例创建功能一致性
class TestClass {
    public $params;
    public function __construct() {
        $this->params = func_get_args();
    }
}

// 测试不同参数数量
$test_cases = [
    ['TestClass'],
    ['TestClass', 'param1'],
    ['TestClass', 'param1', 'param2'],
    ['TestClass', 'param1', 'param2', 'param3']
];

foreach($test_cases as $case) {
    $obj_old = call_user_func_array([$helper, 'NewInstance'], $case);  // 原版本
    $obj_new = call_user_func_array([$helper, 'NewInstance'], $case);  // 升级版本
    assert($obj_old->params === $obj_new->params, "动态实例创建参数必须一致");
}

global.func.php功能验证方案

微信AES加密验证

// 验证微信协议兼容性
$message = "微信消息测试内容";
$encodingaeskey = "test_encoding_aes_key_for_wechat_protocol";
$appid = "test_app_id";

$encrypted_old = aes_encode($message, $encodingaeskey, $appid);  // mcrypt版本
$encrypted_new = aes_encode($message, $encodingaeskey, $appid);  // OpenSSL版本

$decrypted_old = aes_decode($encrypted_old, $encodingaeskey, $appid);
$decrypted_new = aes_decode($encrypted_new, $encodingaeskey, $appid);

assert($decrypted_old === $message, "原微信解密必须成功");
assert($decrypted_new === $message, "升级后微信解密必须成功");
assert($decrypted_old === $decrypted_new, "微信协议处理必须完全一致");

⚠️ 验证失败处理

  • 任何功能验证失败都必须停止升级
  • 必须分析差异原因并调整OpenSSL实现
  • 确保所有原有行为(包括"缺陷"100%保持