You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP 7.0-7.3中eval函数的异常行为及解决方案求助

PHP 7.0-7.3中eval动态创建匿名类出现输出异常的问题

问题描述

以下代码在PHP 7.4及以上版本运行时输出符合预期,但在PHP 7.0至7.3版本中出现异常:多次调用相同参数的e()函数,返回的匿名类实例的test()方法返回值出现混淆,不符合传入的$n参数预期。

<?php

function e($m, $n) {
    return eval(
        'return new class() {

            // '. str_repeat('a', $m). '
            public function test() {
                return "'. $n. '";
            }

        };'
    );
}

for ($i = 131; $i < 140; $i++) {
    printf("%d: %d, %d, %d, %d, %d, %d\n",
        $i,
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i + 1, 1)->test(),
        e($i + 1, 2)->test()
    );
}

不同版本输出对比

PHP 7.4/8.* 预期输出

131: 1, 2, 1, 2, 1, 2
132: 1, 2, 1, 2, 1, 2
133: 1, 2, 1, 2, 1, 2
134: 1, 2, 1, 2, 1, 2
135: 1, 2, 1, 2, 1, 2
136: 1, 2, 1, 2, 1, 2
137: 1, 2, 1, 2, 1, 2
138: 1, 2, 1, 2, 1, 2
139: 1, 2, 1, 2, 1, 2

PHP 7.0-7.3 异常输出

131: 1, 2, 2, 2, 1, 1
132: 1, 1, 1, 1, 1, 2
133: 1, 2, 1, 2, 1, 2
134: 1, 2, 1, 2, 1, 2
135: 1, 2, 1, 2, 1, 1
136: 1, 1, 1, 1, 1, 1
137: 1, 1, 1, 1, 1, 1
138: 1, 1, 1, 1, 1, 1
139: 1, 1, 1, 1, 1, 1

问题分析

这是PHP 7.0-7.3版本的匿名类缓存机制缺陷导致的:当通过eval()动态生成匿名类时,PHP会根据类代码内容生成哈希值作为缓存键,但早期版本中,哈希计算可能忽略注释部分的差异或出现哈希碰撞,导致不同参数生成的匿名类被错误复用之前缓存的类定义,最终出现返回值混淆。

最初推测异常与eval代码开头的相同字符数有关,但复杂场景下该推测不成立,核心问题还是早期版本对动态匿名类的缓存逻辑不完善。

解决方案

方案1:用动态命名类替代匿名类

通过生成唯一类名避免缓存冲突,确保每个参数组合对应不同的类:

<?php

function e($m, $n) {
    // 结合参数生成唯一类名,避免重复
    $className = 'DynamicClass_' . md5($m . '_' . $n);
    if (!class_exists($className)) {
        eval(
            'class ' . $className . ' {
                public function test() {
                    return "' . $n . '";
                }
            }'
        );
    }
    return new $className();
}

for ($i = 131; $i < 140; $i++) {
    printf("%d: %d, %d, %d, %d, %d, %d\n",
        $i,
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i + 1, 1)->test(),
        e($i + 1, 2)->test()
    );
}

方案2:给匿名类添加唯一标识的属性逻辑

通过构造函数传递参数,同时让类结构包含依赖参数的唯一逻辑,确保生成的类代码内容唯一:

<?php

function e($m, $n) {
    return eval(
        'return new class(' . $n . ') {
            private $value;
            public function __construct($value) {
                $this->value = $value;
            }
            // '. str_repeat('a', $m). '
            public function test() {
                return $this->value;
            }
        };'
    );
}

for ($i = 131; $i < 140; $i++) {
    printf("%d: %d, %d, %d, %d, %d, %d\n",
        $i,
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i, 1)->test(),
        e($i, 2)->test(),
        e($i + 1, 1)->test(),
        e($i + 1, 2)->test()
    );
}

这种方式避免了将参数硬编码在方法返回值中,同时每个类的初始化逻辑唯一,不会触发错误的缓存复用。

方案3:升级PHP版本到7.4+

PHP官方在7.4版本中修复了匿名类的缓存逻辑问题,升级到7.4或更高版本可以彻底解决该问题。


内容的提问来源于stack exchange,提问作者Alexandar Popov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:13:27