在下面的示例中使用
self
和 static
有什么区别?
class Foo
{
protected static $bar = 1234;
public static function instance()
{
echo self::$bar;
echo "\n";
echo static::$bar;
}
}
Foo::instance();
1234
1234
当您使用
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
类内的重新声明。
// self
var_dump(Foo::$bar);
// (int) 1234
// static
var_dump(Bar::$bar);
// (int) 4321
您通常对方法甚至类本身使用后期静态绑定,而不是属性,因为您不经常在子类中重新声明属性;使用
static
关键字调用后期绑定构造函数的示例可以在此相关问题中找到:New self vs. new static
但是,这并不排除将
static
与属性一起使用。
我有一个小例子显示
self
和 static
之间的区别。使用 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
与
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();
您可以在上面看到,即使我们已经用
$number
类覆盖了 Fax
,getNumber()
仍然返回 123
。
这是因为我们要求 PHP 给我们定义它的变量 -- 这将返回
Phone
s 变量。
如果我们将
self
调用与 static
交换,我们将得到 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();
如前所述,主要区别之一是
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
在基类中使用
return static::$name
将返回扩展时静态附加的内容。如果您要使用 return self::$name
,那么 B::getName()
将返回一个空字符串,因为这是在基类中声明的。
也许这个不言自明的代码可以帮助您:
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)
self::
和static::
都引用父类中的静态属性/函数。但是,当 self::
从定义函数的类中获取某些内容时,static::
从执行函数的类中获取某些内容。
class Parent
{
function __construct($name){}
static $best_afternoon = ' take a nap.';
static $opinion = ' enjoy';
function plan()
{
echo $this->name . ' will' . self::$activity;
}
function nap()
{
echo $this->name . ' will' . static::$opinion . ' it.';
}
}
class Child extends Parent
{
static $best_afternoon = ' run around.';
static $opinion = ' dislike';
}
每个
Child
都从 plan()
类继承 nap()
和 Parent
函数,并覆盖 $best_afternoon
和 $opinion
。但是 plan()
即使在继承时也保留其原始上下文,因为它使用 self::
而不是 static::
。
$mother = new Parent('Alice');
$son = new Child('Ben');
$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."
当然,如果孩子们有自己的
plan()
,调用 $son->plan()
可能会返回完全不同的东西。当然,直接询问 Ben Child::$best_afternoon
是什么仍然会返回被覆盖的 Child
值(“到处乱跑”)。