【问题标题】:New self vs. new static新自我与新静态
【发布时间】:2026-01-13 03:50:01
【问题描述】:

我正在转换 PHP 5.3 库以在 PHP 5.2 上运行。阻碍我的主要事情是使用像 return new static($options); 这样的后期静态绑定,如果我将其转换为 return new self($options) 会得到相同的结果吗?

new selfnew static 有什么区别?

【问题讨论】:

    标签: php static late-static-binding


    【解决方案1】:

    我会得到相同的结果吗?

    不是真的。不过,我不知道 PHP 5.2 的解决方法。

    new selfnew static 有什么区别?

    self 指的是实际写入new 关键字的同一类。

    static,在 PHP 5.3 的后期静态绑定中,指的是层次结构中您调用该方法的任何类。

    在以下示例中,B 继承了 A 的两个方法。 self 调用绑定到 A,因为它是在 A 的第一个方法的实现中定义的,而 static 绑定到被调用的类(另见 get_called_class())。

    class A {
        public static function get_self() {
            return new self();
        }
    
        public static function get_static() {
            return new static();
        }
    }
    
    class B extends A {}
    
    echo get_class(B::get_self());  // A
    echo get_class(B::get_static()); // B
    echo get_class(A::get_self()); // A
    echo get_class(A::get_static()); // A
    

    【讨论】:

    • 有道理。我认为最好的办法是将类名传递给使用后期静态绑定的函数,然后返回 new $className($options);
    • 您不必“传递”类名,您始终可以使用get_called_class(),这实际上与__CLASS__ 相同,但与LSB 兼容。
    • get_call_class 在
    • 调用 self::theFunction() 的函数的行为类似于“我将在我物理所属的类的上下文中执行”。并且名为 static::theFunction() 的函数的行为类似于“我将在已被外部世界实际调用的类的上下文中执行”。 (假设继承场景)。谢谢
    • 在我的脑海里,我只是把直觉的东西拿来,然后让它相反。你会认为基于命名,self 会返回自己,static 会返回一些不能被覆盖的东西......但是你瞧,情况恰恰相反。 PHP 的命名、约定和整体风格给我留下了深刻的印象。 -_-
    【解决方案2】:

    如果此代码的方法不是静态的,您可以在 5.2 中使用get_class($this) 获得解决方法。

    class A {
        public function create1() {
            $class = get_class($this);
            return new $class();
        }
        public function create2() {
            return new static();
        }
    }
    
    class B extends A {
    
    }
    
    $b = new B();
    var_dump(get_class($b->create1()), get_class($b->create2()));
    

    结果:

    string(1) "B"
    string(1) "B"
    

    【讨论】:

    • 如果方法不是静态的,那么后期静态绑定就变得完全无关紧要了。
    • 例如,您可以在“复制”方法中使用它,在该方法中复制对象而不使用clone,而只需重新创建和设置属性即可。 $copy = new static(); $copy->set($this->get()); return $copy;
    • @BoltClock 确定不是吗?如果您从子类的实例方法中调用被覆盖的静态方法,那么您对self::static:: 的选择将影响是使用该静态方法的基类版本还是子类版本。在没有理由认为这种情况的发生本质上表明不良做法的情况下(我认为没有任何理由应该如此),self::static:: 之间的选择在非静态方法,就像在静态方法中一样。我是否误解了您的评论,或者我们只是错了?
    • @Mark Amery:嗯,我没想到。你是绝对正确的。我曾假设在所讨论的实例方法中不会调用任何静态方法,但根据您的示例,我可以看出这是一个非常幼稚的假设。
    【解决方案3】:

    除了别人的回答:

    static:: 将使用运行时信息进行计算。

    这意味着您不能在类属性中使用static::,因为属性值:

    必须能够在编译时进行评估,并且不得依赖于运行时信息。

    class Foo {
        public $name = static::class;
    
    }
    
    $Foo = new Foo;
    echo $Foo->name; // Fatal error
    

    使用self::

    class Foo {
        public $name = self::class;
    
    }
    $Foo = new Foo;
    echo $Foo->name; // Foo
    

    请注意,我所做的代码中的致命错误注释并未指出错误发生的位置,该错误在对象被实例化为 cmets 中提到的@Grapestain 之前发生过

    【讨论】:

    • 请注意,错误是在第 2 行 public $name = static::class; 上引发的,而不是在第 7 行上,如示例所示。错误说:“static::class cannot be used for compile-time class name resolution”,这表明问题不在于您尝试访问 $name 字段的位置,而是在 PHP 类的编译之前。在第一个示例中将无法到达第 7 行(或第 6 行)。
    • 对,我没有批评的意思,只是先澄清一下我的困惑,希望它可以帮助别人。无论如何都是有用的例子!