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

PHP如何让工厂函数每次调用返回全新独立的匿名类

问题

尝试通过工厂函数实例化匿名类时遇到问题:每次调用函数返回的都是同一个类,使用静态变量时会出现数据互相覆盖的异常。

PHP官方文档明确说明:

同一匿名类声明创建的所有对象,都属于这个完全相同的类。

这意味着如下代码运行后,永远会判定两次调用anonymous_class()返回的对象属于同一个类:

function anonymous_class() {
    return new class {};
}

if (get_class(anonymous_class()) === get_class(anonymous_class()))
    echo 'same class';

需求是找到可行方案,让同一个工厂函数每次调用都能生成全新的独立类,从而实现预期业务逻辑,避免静态变量被覆盖。
当前编写的factory函数代码如下,两次传入不同参数调用后,两次调用getStaticVar()都输出最后一次赋值的"Hello Nurse",不符合预期:

function factory(string $param) {
    return new class($param) {
        public string $param;
        public static Closure $getter;

        public function __construct(string $param) {
            $this->param = $param;

            static::$getter = function () {
                return $this->param;
            };
        }

        public static function getStaticVar(): string
        {
            $getter = static::$getter;
            return $getter();
        }
    };
}

$world = factory("Hello World");
$nurse = factory("Hello Nurse");

echo $world::getStaticVar(); //Output: Hello Nurse
echo $nurse::getStaticVar(); //Output: Hello Nurse

如果不通过工厂函数,手动复制两份相同的匿名类代码分别实例化,就能得到符合预期的输出,两次调用分别返回对应传入的参数值,但手动重复复制类代码的方式维护成本太高,手动实现的参考代码如下:

$world = new class("Hello World") {
    public string $param;
    public static Closure $getter;

    public function __construct(string $param) {
        $this->param = $param;
        static::$getter = function () { return $this->param; };
    }

    public static function getStaticVar(): string
    {
        $getter = static::$getter;
        return $getter();
    }
};

$nurse = new class("Hello Nurse") {
    public string $param;
    public static Closure $getter;

    public function __construct(string $param) {
        $this->param = $param;
        static::$getter = function () { return $this->param; };
    }

    public static function getStaticVar(): string
    {
        $getter = static::$getter;
        return $getter();
    }
};

echo $world::getStaticVar(); //Output: Hello World
echo $nurse::getStaticVar(); //Output: Hello Nurse
解决方案

方案一:动态生成独立类声明(非可信输入场景禁用)

PHP中匿名类的唯一性和代码中的声明位置绑定,同一位置编写的匿名类无论调用多少次,都只会被解析为一个类。要实现每次调用生成全新独立类,只需要让类声明每次都在全新的解析上下文中执行即可,eval()可以实现这个效果:

function factory(string $param) {
    $safeParam = var_export($param, true);
    return eval(<<<CODE
return new class($safeParam) {
    public string \$param;
    public static Closure \$getter;

    public function __construct(string \$param) {
        \$this->param = \$param;
        static::\$getter = function () {
            return \$this->param;
        };
    }

    public static function getStaticVar(): string
    {
        \$getter = static::\$getter;
        return \$getter();
    }
};
CODE
    );
}

$world = factory("Hello World");
$nurse = factory("Hello Nurse");

echo $world::getStaticVar(); // 输出 Hello World
echo $nurse::getStaticVar(); // 输出 Hello Nurse

注意:该方案存在代码注入风险,仅当传入factory的参数完全可控、无任何用户输入内容时才可使用。

方案二:调整代码设计,规避类级别全局状态冲突(推荐)

静态属性本身是类级别的全局状态,既然需要每个实例拥有独立的访问入口,完全不需要强行依赖动态生成类,通过实例唯一标识做状态隔离即可,性能更好也无安全风险:

function factory(string $param) {
    return new class($param) {
        private string $param;
        private static array $instanceStorage = [];

        public function __construct(string $param) {
            $this->param = $param;
            self::$instanceStorage[spl_object_hash($this)] = $this->param;
        }

        // 实例调用方式
        public function getVar(): string
        {
            return self::$instanceStorage[spl_object_hash($this)];
        }

        // 兼容静态调用方式
        public static function getStaticVar(object $instance): string
        {
            $key = spl_object_hash($instance);
            if (!isset(self::$instanceStorage[$key])) {
                throw new InvalidArgumentException("传入实例不属于当前类");
            }
            return self::$instanceStorage[$key];
        }

        // 实例销毁时自动清理存储,避免内存泄漏
        public function __destruct()
        {
            unset(self::$instanceStorage[spl_object_hash($this)]);
        }
    };
}

$world = factory("Hello World");
$nurse = factory("Hello Nurse");

echo $world->getVar(); // 输出 Hello World
echo $nurse->getVar(); // 输出 Hello Nurse
echo $world::getStaticVar($world); // 输出 Hello World
echo $nurse::getStaticVar($nurse); // 输出 Hello Nurse

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:45:33