Jaka jest różnica między self :: $ bar i static :: $ bar w PHP?

125

Jaka jest różnica między użyciem selfa staticw poniższym przykładzie?

class Foo
{
    protected static $bar = 1234;

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

}

Foo::instance();

produkuje

1234
1234
cwd
źródło
2
@deceze: To podobne pytanie, ale nie jest to duplikat. Ten pyta o użycie słów kluczowych z właściwościami, podczas gdy to pyta o użycie ich z konstruktorami.
BoltClock

Odpowiedzi:

191

Kiedy używasz selfodniesienia do członka klasy, odnosisz się do klasy, w której używasz słowa kluczowego. W takim przypadku Twoja Fooklasa definiuje chronioną właściwość statyczną o nazwie $bar. Kiedy używasz selfw Fooklasie do odwoływania się do właściwości, odwołujesz się do tej samej klasy.

Dlatego jeśli spróbujesz użyć self::$barinnego miejsca w swojej Fooklasie, ale masz Barklasę z inną wartością właściwości, użyje ona Foo::$barzamiast Bar::$bar, co może nie być tym, czego zamierzałeś:

class Foo
{
    protected static $bar = 1234;
}

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

Kiedy wywołujesz metodę via static, wywołujesz funkcję zwaną późnymi wiązaniami statycznymi (wprowadzoną w PHP 5.3).

W powyższym scenariuszu użycie selfspowoduje Foo::$bar(1234). Użycie staticda w wyniku Bar::$bar(4321), ponieważ w staticprzypadku interpreter bierze pod uwagę ponowną deklarację w Barklasie podczas działania.

Zwykle używasz późnych powiązań statycznych dla metod lub nawet samej klasy, a nie właściwości, ponieważ często nie deklarujesz ponownie właściwości w podklasach; przykład użycia staticsłowa kluczowego do wywołania konstruktora z późnym wiązaniem można znaleźć w tym pokrewnym pytaniu: Nowa jaźń vs. nowa statyczna

Jednak nie wyklucza to również używania staticz właściwościami.

BoltClock
źródło
Możesz bardzo łatwo ponownie zadeklarować w klasie podrzędnej, klasa nadrzędna może być wartością domyślną, której używa klasa potomna, chyba że ponownie zadeklarują. Jeśli jesteś w klasie nadrzędnej, myślę, że bezpiecznie jest używać self ::, a jeśli jesteś w klasie podrzędnej, możesz wymyślić argument, aby użyć jednego z nich, ale self :: będzie również działać, jeśli nie spodziewasz się tego ponownie zadeklaruj kiedykolwiek.
Andrew,
3
przejdź do phpfiddle.org i uruchom to<?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(); ?>
Yevgeniy Afanasyev
2
Sformułowanie dwóch pierwszych akapitów jest mylące, zawiera niejednoznaczny zaimek „it” i jest również zbędne, ponieważ późniejsze akapity wyjaśniają te same informacje jaśniej. Proponuję zastąpić dwa pierwsze akapity akapitem późniejszym, zaczynającym się od „W powyższym scenariuszu” na początku. W ten sposób ostateczna odpowiedź znajduje się na górze. Jest jasne i łatwe do naśladowania.
ahnbizcad
Można o tym inaczej pomyśleć: self::$abcużycie w środku class Footo to samo, co powiedzenie Foo::$abc. Nie wpłynie na to żadna ponowna deklaracja $abcw podklasie. AFAIK, jedynym powodem użycia selfjest skrót, aby uniknąć używania nazwy klasy Foo, która może być dłuższa. [Oznacza to również, że możesz zmienić nazwę klasy bez zmiany tych wszystkich miejsc - ale to nie jest powód, dla którego IMHO.] (Wybór nazw PHP jest niefortunny i wydaje się być odwrotny; „statyczny” to ten, który może się zmienić - co jest przeciwieństwem potocznego znaczenia słowa „statyczny” w języku naturalnym).
ToolmakerSteve,
4

Jak wspomniano, jedną z głównych różnic jest to, że staticpozwala na późne wiązanie statyczne. Jednym z najbardziej przydatnych scenariuszy, które znalazłem, było tworzenie klas podstawowych dla klas 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

Użycie return static::$namew klasie Base zwróci to, co zostało statycznie dołączone, gdy zostało rozszerzone. Jeśli było użyć return self::$namewtedy B::getName()zwróciłby pusty ciąg jako to, co jest zadeklarowana w klasie bazowej.

ggedde
źródło
0

Z selfwezwaniem:

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();

Możesz zobaczyć powyżej, mimo że nadpisaliśmy the $varz naszą Barklasą, nadal zwraca 123, ponieważ jawnie poprosiliśmy PHP o selfzmienną, która z kolei prosi o Foozmienną s.

Teraz, jeśli zamienimy wywołanie na static, zamiast tego otrzymamy Barnadpisaną wartość:

Z staticwezwaniem:

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();
Steve Bauman
źródło