PHP中self::$bar和static::$bar有什么区别?

203

在下面的示例中,使用selfstatic有什么区别?

class Foo
{
    protected static $bar = 1234;

    public static function instance()
    {
        echo self::$bar;
        echo "\n";
        echo static::$bar;
    }

}

Foo::instance();

产生

1234
1234

2
@deceze:这是一个类似的问题,但它不是重复的。这个问题询问如何在属性中使用关键字,而那个问题询问如何在构造函数中使用它们。 - BoltClock
6个回答

258
当您使用self来引用类成员时,您正在引用使用该关键字的类。在本例中,您的Foo类定义了一个受保护的静态属性$bar。当您在Foo类中使用self来引用该属性时,您正在引用相同的类。
因此,如果您尝试在Foo类的其他位置使用self::$bar,但是您有一个Bar类具有不同的属性值,则会使用Foo::$bar而不是Bar::$bar,这可能不是您想要的结果:
class Foo
{
    protected static $bar = 1234;
}

class Bar extends Foo
{
    protected static $bar = 4321;
}

当你通过static调用一个方法时,你正在调用一个名为“late static bindings”的功能(在PHP 5.3中引入)。

在上述场景中,使用self将导致Foo::$bar(1234)。 而使用static将导致Bar::$bar(4321),因为使用static时,解释器会在运行时考虑在Bar类中的重新声明。

// self
var_dump(Foo::$bar);
// (int) 1234

// static
var_dump(Bar::$bar);
// (int) 4321

通常情况下,我们使用后期静态绑定来调用方法或者类本身,而不是属性,因为在子类中很少会重新声明属性。如果你想查看使用 static 关键字调用后期绑定构造函数的示例,可以参考这个相关问题:New self vs. new static

然而,这并不排除对属性使用 static 的可能性。


您可以在子类中非常容易地重新声明父类,父类可能是子类使用的默认值,除非它们重新声明。如果您在父类中,则可以安全地使用self::,如果在子类中,则可以提出使用任一种方法的论据,但是如果您不希望重新声明,self::也可以起作用。 - Andrew
7
请前往phpfiddle.org并运行以下代码:class Foo { public static $bar = 1234; public static function a() { echo 'static'.static::$bar; echo 'self'.self::$bar; } } class Bar extends Foo { public static $bar = 4321; } (new Bar())->a(); - Yevgeniy Afanasyev
3
第一段和第二段措辞令人困惑,存在一个有歧义的代词“it”,并且也是多余的,因为后面的段落更清楚地解释了相同的信息。我建议用以“在上述情况下开始”的后面段落替换前两段。这样底线、直截了当的答案就在顶部。它很清晰易懂。 - ahnbizcad
另一种思考方式是:在class Foo中使用self::$abc与使用Foo::$abc是相同的。它不会受到子类中$abc重新声明的影响。据我所知,使用self的唯一原因是为了缩写,避免使用可能更长的类名Foo。[这也意味着您可以更改类名而无需更改所有这些位置 - 但在我看来这并不是一个很好的理由。](PHP选择的名称不幸,并且似乎是反向的;“static”是可以更改的 - 这与自然语言词“static”的口语含义相反。) - ToolmakerSteve

54

我有一个小例子,展示了selfstatic之间的区别。使用static::会执行后期静态绑定,并从子类中绑定变量值。

class A { // Base Class
    protected static $name = 'ClassA';
    public static function getSelfName() {
        return self::$name;
    }
    public static function getStaticName() {
        return static::$name;
    }
}

class B extends A {
    protected static $name = 'ClassB';
}

echo A::getSelfName(); // ClassA
echo A::getStaticName(); // ClassA

echo B::getSelfName(); // ClassA
echo B::getStaticName(); // ClassB

32

使用 self 调用:

class Phone
{
    protected static $number = 123;
    
    public function getNumber()
    {
        return self::$number;
    }
}
class Fax extends Phone
{
    protected static $number = 234;
}

// Displays: "123"
echo (new Fax)->getNumber();
您可以看到,尽管我们已经使用我们的Fax类覆盖了$numbergetNumber()仍然返回123。这是因为我们要求PHP给我们定义它的变量 - 这将返回Phone的变量。如果我们用static替换self调用,我们将获得Fax覆盖的值: 使用static调用:
class Phone
{
    protected static $number = 123;
    
    public function getNumber()
    {
        // return self::$number;

        return static::$number;
    }
}
class Fax extends Phone
{
    protected static $number = 234;
}

// Displays: "234"
echo (new Fax)->getNumber();

1
非常直接且不令人困惑的示例。 - Blues Clues

12

如上所述,其中一个主要区别在于static允许延迟静态绑定。我发现最有用的场景之一是为单例类创建基类:

class A { // Base Class
    protected static $name = '';
    protected static function getName() {
        return static::$name;
    }
}
class B extends A {
    protected static $name = 'MyCustomNameB';
}
class C extends A {
    protected static $name = 'MyCustomNameC';
}

echo B::getName(); // MyCustomNameB
echo C::getName(); // MyCustomNameC

在 Base 类中使用 return static::$name 会返回静态扩展时附加的内容。如果您使用 return self::$name,那么 B::getName() 将返回一个空字符串,因为在 Base 类中声明了一个空字符串。


12
也许这段自解释的代码能帮到你:
class Foo 
{
    protected static $bar = 'parent value';
     
    public static function test() 
    {
         var_dump('I am your father');
         var_dump('self:: here means '.self::$bar);
         var_dump('static:: here means '.static::$bar);
    }
}
     
class Bar extends Foo 
{
     protected static $bar = 'child value';
     
     public static function test() 
     {
         parent::Test();
     
         var_dump('I am the child');
         var_dump('self:: here means '.self::$bar);
         var_dump('static:: here means '.static::$bar);
     }
}

Bar::test();
Foo::test();

这将产生以下输出(为了清晰起见,我添加了换行符):
'I am your father' (length=16)
'self:: here means parent value' (length=30)
'static:: here means child value' (length=31)

'I am the child' (length=14)
'self:: here means child value' (length=29)
'static:: here means child value' (length=31)

'I am your father' (length=16)
'self:: here means parent value' (length=30)
'static:: here means parent value' (length=32)

1

简短版本

self::static::都指向父类中的静态属性/函数。但是,self::从定义函数的类中获取内容,而static::从执行函数的类中获取内容。

示例

class Parent
{
    static $best_afternoon = ' take a nap.';
    static $opinion_of_plan = ' enjoy';

    $name;

    function __construct($name){$this->name = $name;}

    function plan()
    {
        echo $this->name . ' will' .  self::$best_afternoon;
    }
    function nap()
    {
        echo $this->name . ' will' . static::$opinion_of_plan . ' it.';
    }
}

class Child extends Parent
{
    static $best_afternoon = ' run around.';
    static $opinion_of_plan = ' dislike';
}

$mother = new Parent('Alice');
$son = new Child('Ben');

Child类具有自己的best-afternoon和opinion值,但每个Child类都从Parent类继承了plan和nap函数。但是,虽然nap函数使用static::获取被覆盖(晚绑定)的值,但plan函数因为使用self::而引用其原始上下文
$mother->plan(); //> "Alice will take a nap."
$son->plan(); //> "Ben will take a nap."

$mother->nap(); //> "Alice will enjoy it."
$son->nap(); //> "Ben will dislike it."

当然,如果孩子们有自己的计划函数,调用$son->plan()可能会返回完全不同的结果。当然,直接问本仍然会返回Child类中覆盖的值的最佳下午活动。
echo $mother::$best_afternoon; //> " take a nap."
echo $son::$best_afternoon; //> " run around."

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接