В чем разница между self :: $ bar и static :: $ bar в PHP?

125

В чем разница между использованием selfи staticв приведенном ниже примере?

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

Ответы:

191

Когда вы используете selfдля ссылки на член класса, вы имеете в виду класс, в котором вы используете ключевое слово. В этом случае ваш Fooкласс определяет защищенное статическое свойство с именем $bar. Когда вы используете selfв Fooклассе для ссылки на свойство, вы ссылаетесь на тот же класс.

Поэтому, если вы пытались использовать в self::$barдругом месте своего Fooкласса, но у вас был Barкласс с другим значением свойства, он будет использовать Foo::$barвместо Bar::$bar, что может быть не тем, что вы намереваетесь:

class Foo
{
    protected static $bar = 1234;
}

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

Когда вы вызываете метод через static, вы вызываете функцию, называемую поздними статическими привязками (введена в PHP 5.3).

В приведенном выше сценарии использование selfприведет к Foo::$bar(1234). И использование staticприведет к Bar::$bar(4321), потому staticчто интерпретатор принимает во внимание повторное объявление внутри Barкласса во время выполнения.

Обычно вы используете поздние статические привязки для методов или даже самого класса, а не свойств, поскольку вы не часто повторно объявляете свойства в подклассах; пример использования staticключевого слова для вызова конструктора с поздним связыванием можно найти в этом связанном вопросе: New self vs. new static

Однако это также не исключает использования staticсо свойствами.

BoltClock
источник
Вы можете очень легко повторно объявить в дочернем классе, родительский класс может быть значением по умолчанию, которое использует дочерний класс, если они не объявят повторно. Если вы находитесь в родительском классе, я думаю, безопасно использовать self ::, а если в дочернем классе, вы можете придумать аргумент для использования любого из них, но self :: также будет работать, если вы не ожидаете повторно объявить когда-либо.
Эндрю
3
зайдите на phpfiddle.org и запустите это<?php 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(); ?>
Евгений Афанасьев
2
Формулировка первых двух абзацев сбивает с толку, имеет двусмысленное местоимение «оно» и также является избыточным, поскольку в последующих абзацах та же информация объясняется более четко. Я предлагаю заменить первые два абзаца вторым абзацем, который начинается с «В приведенном выше сценарии» вверх. Таким образом, нижняя строка, краткий ответ будет наверху. Это понятно и легко понять.
ahnbizcad
Другой способ подумать об этом:, self::$abcкогда используется внутри class Foo, то же самое, что и сказать Foo::$abc. Никакое повторное объявление $abcв подклассе не повлияет на него. AFAIK, единственная причина для использования self- это сокращение, чтобы избежать использования имени класса Foo, которое может быть длиннее. [Это также означает, что вы можете изменить имя класса, не меняя все эти места - но это не основная причина, IMHO.] (Выбор имен PHP неудачен и кажется обратным; «статический» - это тот, который может измениться - который противоположно разговорному значению слова "статический" на естественном языке.)
ToolmakerSteve
4

Как уже упоминалось, одно из основных отличий заключается в том, что staticдопускаются поздние статические привязки. Один из наиболее полезных сценариев, который я нашел, был для создания базовых классов для классов Singleton:

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

Использование return static::$nameв базовом классе вернет то, что было статически прикреплено при расширении. Если бы вы использовали return self::$namethen B::getName(), вернули бы пустую строку, поскольку она объявлена ​​в базовом классе.

ggedde
источник
0

По selfзвонку:

class Foo
{
    protected static $var = 123;
    
    public function getVar()
    {
        return self::$var;
    }
}

class Bar extends Foo
{
    protected static $var = 234;
}

// Displays: "123"
echo (new Bar)->getVar();

Вы можете видеть выше, хотя мы переопределили $varнаш Barкласс, он все равно возвращается 123, потому что мы явно запросили selfпеременную PHP , которая, в свою очередь, Fooвместо этого запрашивает переменную s.

Теперь, если мы поменяем вызов на static, вместо этого мы получим Barпереопределенное значение s:

По staticзвонку:

class Foo
{
    protected static $var = 123;
    
    public function getVar()
    {
        return static::$var;
    }
}

class Bar extends Foo
{
    protected static $var = 234;
}

// Displays: "234"
echo (new Bar)->getVar();
Стив Бауман
источник