2024 PHP 日本大会

后期静态绑定

PHP 实现了一个称为后期静态绑定的特性,可用于在静态继承的上下文中引用调用的类。

更准确地说,后期静态绑定通过存储在最后一个“非转发调用”中命名的类来工作。对于静态方法调用,这是显式命名的类(通常是在 :: 运算符左侧的类);对于非静态方法调用,它是对象的类。“转发调用”是指由 self::parent::static:: 或(如果向上遍历类层次结构)forward_static_call() 引入的静态调用。get_called_class() 函数可以用来检索包含调用类名称的字符串,而 static:: 引入其作用域。

这个特性被命名为“后期静态绑定”,其名称是从内部视角考虑的。“后期绑定”源于 static:: 的解析不会使用定义方法的类,而是使用运行时信息进行计算。它也被称为“静态绑定”,因为它可以用于(但不限于)静态方法调用。

self:: 的局限性

self::__CLASS__ 这样的对当前类的静态引用使用函数所属的类(即定义它的类)进行解析。

示例 #1 self:: 用法

<?php
class A {
public static function
who() {
echo
__CLASS__;
}
public static function
test() {
self::who();
}
}

class
B extends A {
public static function
who() {
echo
__CLASS__;
}
}

B::test();
?>

以上示例将输出

A

后期静态绑定的用法

后期静态绑定试图通过引入一个关键字来解决这个限制,该关键字引用在运行时最初调用的类。基本上,这是一个允许在前面的示例中从 test() 引用 B 的关键字。决定不引入新的关键字,而是使用已保留的 static

示例 #2 static:: 简单用法

<?php
class A {
public static function
who() {
echo
__CLASS__;
}
public static function
test() {
static::
who(); // 后期静态绑定在此处
}
}

class
B extends A {
public static function
who() {
echo
__CLASS__;
}
}

B::test();
?>

以上示例将输出

B

注意:

在非静态上下文中,调用的类将是对象实例的类。由于 $this-> 将尝试从相同作用域调用私有方法,因此使用 static:: 可能产生不同的结果。另一个区别是 static:: 只能引用静态属性。

示例 #3 static:: 在非静态上下文中的用法

<?php
class A {
private function
foo() {
echo
"success!\n";
}
public function
test() {
$this->foo();
static::
foo();
}
}

class
B extends A {
/* foo() 将被复制到 B,因此其作用域仍然是 A,并且
* 调用将成功 */
}

class
C extends A {
private function
foo() {
/* 原始方法被替换;新方法的作用域是 C */
}
}

$b = new B();
$b->test();
$c = new C();
$c->test(); // 失败
?>

以上示例将输出

success!
success!
success!


Fatal error:  Call to private method C::foo() from context 'A' in /tmp/test.php on line 9

注意:

后期静态绑定的解析将在完全解析的静态调用处停止,没有回退。另一方面,使用 parent::self:: 等关键字的静态调用将转发调用信息。

示例 #4 转发和非转发调用

<?php
class A {
public static function
foo() {
static::
who();
}

public static function
who() {
echo
__CLASS__."\n";
}
}

class
B extends A {
public static function
test() {
A::foo();
parent::foo();
self::foo();
}

public static function
who() {
echo
__CLASS__."\n";
}
}
class
C extends B {
public static function
who() {
echo
__CLASS__."\n";
}
}

C::test();
?>

以上示例将输出

A
C
C

添加注释

用户贡献的注释 29 条注释

166
sergei at 2440media dot com
16 年前
最终我们可以实现一些 ActiveRecord 方法了

<?php

Model
{
public static function
find()
{
echo static::
$name;
}
}

Product 扩展 Model
{
protected static
$name = 'Product';
}

Product::find();

?>

输出:'Product'
61
mhh1422 at hotmail dot com
10年前
对于带有静态工厂方法的抽象类,您可以使用`static`关键字代替`self`,如下所示
<?php

抽象类 A{

static function
create(){

//return new self(); //致命错误:无法实例化抽象类 A

return new static(); //这是正确的方法

}

}

B 扩展 A{
}

$obj=B::create();
var_dump($obj);

?>
25
MelkiySoft
6年前
<?php

A
{

}

B 扩展 A
{
public static function
foo () {
echo
'new self: ';
var_dump(new self());
echo
'<br>new parent: ';
var_dump(new parent());
echo
'<br>new static: ';
var_dump(new static());
}
}

C 扩展 B
{

}

c::foo();
===========================
输出:
//new self: object(B)#1 (0) { }
//new parent: object(A)#1 (0) { }
//new static: object(C)#1 (0) { }
2
MikeT
2年前
警告:`static::class`并不总是像你预期的那样工作
<?php
命名空间 NameSpace;

类 Class
{
static function
getClass()
{
return static::class;
}
}

Class::
getClass()
?>
可能会返回 `\NameSpace\Class` 或 `Class`,取决于上下文
7
backnot
3年前
在上面的例子(#3)中,为了使其工作,您可以将子类的方法从`private`更改为`protected`(或`public`),它将通过`static`调用。

<?php
A {
private function
foo() {
echo
"success!\n";
}
public function
test() {
$this->foo();
static::
foo();
}
}

B 扩展 A {
/* foo() 将被复制到 B,因此其作用域仍然是 A,并且调用将成功 */
}

C 扩展 A {
protected function
foo() { //注意这里的更改
echo 'hello world!';
}
}

$b = new B();
$b->test();
$c = new C();
$c->test(); // 'success' 'hello world'
?>
12
sskaje at gmail dot com
9年前
`static::class` 和 `self::class` 可用于获取当前类名,
在 5.5 和 5.6 版本下工作
在 5.3 版本下失败。

<?php
a{
function
d() {
echo
"=== self::class ===\n";
var_dump(self::class);
echo
"=== static::class ===\n";
var_dump(static::class);
}
}
b 扩展 a{}
c 扩展 b{}

a::d();
b::d();
c::d();

/*
输出:

=== self::class ===
string(1) "a"
=== static::class ===
string(1) "a"
=== self::class ===
string(1) "a"
=== static::class ===
string(1) "b"
=== self::class ===
string(1) "a"
=== static::class ===
string(1) "c"

*/
1
aabweber at gmail dot com
2年前
理解最简单的方法是运行这段脚本

<?php
ParentClass
{
static
$A = 'ParentVariable';

static function
parentCall()
{
echo
get_called_class() . ', self: ' . self::$A . "\n";
echo
get_called_class() . ', static: ' . static::$A . "\n";
echo
"---\n";
}
}

ChildClass 扩展 ParentClass
{
static
$A = 'ChildVariable';

static function
childCall()
{
echo
get_called_class() . ', self: ' . self::$A . "\n";
echo
get_called_class() . ', static: ' . static::$A . "\n";
echo
get_called_class() . ', parent: ' . parent::$A . "\n";
echo
"---\n";
}
}

echo
"后期静态绑定:\n";
ParentClass::parentCall();
ChildClass::parentCall();
ChildClass::childCall();
?>

----
输出

后期静态绑定
ParentClass, self: ParentVariable
ParentClass, static: ParentVariable
---
ChildClass, self: ParentVariable
ChildClass, static: ChildVariable
---
ChildClass, self: ChildVariable
ChildClass, static: ChildVariable
ChildClass, parent: ParentVariable
1
Anonymous
2年前
类 P_Class {
public static $val = "Parent";
public static function setVal($val){
static::$val = $val;
}
public static function getVal(){
return static::$val;
}
}

类 C_Class 扩展 P_Class{}

C_Class::setVal("Child");
var_dump(C_Class::getVal());
var_dump(P_Class::getVal());

输出
string(5) "Child"
string(5) "Child"
6
tyler AT canfone [dot] COM
16 年前
@ php at mikebird

您可以通过`getInstance`方法向构造函数传递参数,假设您正在运行php5。

public static function getInstance($params = null) {
if (self::$objInstance == null) {
$strClass = static::getClass();
self::$objInstance = new $strClass($params);
}
return self::$objInstance;
}

这将把参数传递给您的构造函数。热爱php。
7
steven dot karas+nospam at gmail dot com
14年前
此函数可用作PHP >= 5.1.0中后期静态绑定的变通方法。此函数的另一个类似版本在其他地方,但使用了eval。

<?php

function & static_var($class, $name)
{
if (
is_object($class))
{
$class = get_class($class);
}
elseif ( !
is_string($class))
{
throw new
Exception('必须提供对象或类名', NULL);
}

$class = new ReflectionClass($class);
return
$class->getStaticPropertyValue($name);
}

?>
11
tamilps2 at gmail dot com
10年前
我已经使用延迟静态绑定实现了枚举。

<?php
interface IEnum {
/**
* 只有具体的类应该实现此函数,此函数应表现为
* 枚举。
*
* 此方法应返回该类的 __CLASS__ 常量属性
*
* @return string __CLASS__
*/
public static function who();
}

abstract class
Enum {

/**
* 枚举实现的选择值
*
* @var mixed
*/
public $value;

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

/**
* 创建相应枚举类的工厂方法。
*
* @param integer $type
* @return false|\class
*/
public static function Factory($type) {
if (empty(
$type)) {
return
false;
}

// 使用延迟静态绑定获取类。
$class = static::who();

if (
array_key_exists($type, static::$_enums)) {
return new
$class($type);
}

return
false;
}

public function
getValue() {
return
$this->value;
}

public static function
getValues() {
return
array_keys(static::$_enums);
}

public function
getString() {
return static::
$_enums[$this->value];
}

public function
__toString() {
return static::
$_enums[$this->value];
}

}

class
Fruits extends Enum implements IEnum {

public static
$_enums = array(
1 => 'Apple'
2 => 'Orange'
3 => 'Banana'
)

public static function
who() {
return
__CLASS__;
}
}

// 用法

// 来自水果列表下拉菜单的用户输入
$input = 3;

$fruit = Fruits::Factory($input);

$fruit->getValue(); // 3
$fruit->getString(); // Banana
?>
9
jakub dot lopuszanski at nasza-klasa dot pl
14年前
令人惊讶的是,即使你使用 self 而不是 static,常量也是延迟绑定的。
<?php
class A{
const
X=1;
const
Y=self::X;
}
class
B extends A{
const
X=1.0;
}
var_dump(B::Y); // float(1.0)
?>
7
kx
16 年前
至少从 PHP 5.3.0a2 开始,有一个函数 get_called_class(),它返回调用静态方法的类。

<?php

class a {
static public function
test() {
print
get_called_class();
}
}

class
b extends a {
}

a::test(); // "a"
b::test(); // "b"

?>
7
Andrea Giammarchi
16 年前
关于静态参数,这些按预期工作。
<?php
class A {
protected static
$__CLASS__ = __CLASS__;
public static function
constructor(){
return static::
$__CLASS__;
}
}

class
B extends A {
protected static
$__CLASS__ = __CLASS__;
}

echo
B::constructor(); // B
?>
4
adam dot prall at thinkingman dot com
14年前
只是快速提醒一下,始终检查你的语法。虽然我喜欢 LSB,但我认为它不起作用

static::$sKey 未设置

……直到我意识到我完全忘记把它做成一个变量变量。

$sKey = 'testStaticClassVarNameThatExistsInThisClassesScope';

static::$$sKey 已设置

……当然,这适用于 PHP 中的任何地方,但是由于(当前)延迟静态绑定的新颖性,我从其他人那里看到了很多包含此特定错误的代码。
2
joost dot t dot hart at planet dot nl
15年前
PHP5.3不可用,但需要“static”,我做了以下操作。

有什么异议吗?我个人讨厌使用 eval() 语句……

<?php

class mother
{
function
setStatic( $prop, $val ) {
// 此后,self:: 指的是 mother,但下一个 $class 指的是……
//
$class = get_class( $this );
eval(
"$class::\$$prop = \$$val;" );
}
}

class
child extends mother
{
protected static
$sProp;

function
writer( $value ) {
parent::setStatic( 'sProp', $value );
}
function
reader()
{
return
self::$sProp;
}
}

$c = new child();
$c->writer( 3 );
echo
$c->reader(); // 3

?>
3
Taai
12年前
我发现了一个有趣的事情。类名字符串必须直接从“扁平”变量访问。从类实例传递的数组中获取其变量的延迟静态绑定代码会引发语法错误。错误?

<?php
A {

public
$metadata = array('class' => 'A');

public static function
numbers()
{
return
123;
}

}

$instance = new A();

// 这会抛出错误
// Parse error: syntax error, unexpected '::' (T_PAAMAYIM_NEKUDOTAYIM)
var_dump( $instance->metadata['class']::numbers() );

// 获取类名并将其存储在“flat”变量中,现在可以了
$class_name = $instance->metadata['class'];
var_dump( $class_name::numbers() );

// 其他测试 -------------------------------------------

$arr = array('class' => 'A');

// 这也可以工作。
var_dump( $arr['class']::numbers() );
?>
0
github.com上的5imun
2年前
示例:仅当父类中未定义时,才在子类中设置静态属性。许多人期望输出为“Foo Bar”,但实际上我们得到的是“Foo Foo”

<?php
Foo
{
public static
string $A;

public static function
init() {
return
"Foo";
}
public static function
get() {
if (!isset(static::
$A)) {
static::
$A = static::init();
}
return static::
$A;
}
}

Bar extends Foo {
public static function
init() {
return
"Bar";
}
}

$foo = new Foo();
$bar = new Bar();

echo
$foo->get();
echo
$bar->get();
?>

输出
Foo
Foo
0
info-conseil.fr的sebastien
16 年前
这是一个针对静态继承问题的简单解决方法。它并不完美,但有效。

<?php

// BaseClass 类将被任何需要静态继承解决方法的类扩展
BaseClass {
// 临时存储类名,用于 Entry::getStatic() 和 Entry::setNextStatic()
protected static $nextStatic = false;

// 返回调用该方法的类的真实名称,而不是声明该方法的类的名称。
protected static function getStatic() {
// 如果已存储
if (self::$nextStatic) {
// 清理并返回
$class = self::$nextStatic;
self::$nextStatic = false;
return
$class;
}

// 初始化
$backTrace = debug_backtrace();
$class = false;

// 遍历
for ($i=0; $i<count($backTrace); $i++) {
// 如果定义了类
if (isset($backTrace[$i]['class'])) {
// 检查它是否不是基本类
if (!in_array($backTrace[$i]['class'], array('BaseClass', 'GenericClass'))) {
return
$backTrace[$i]['class'];
} else {
$class = $backTrace[$i]['class'];
}
} else {
// 返回最后已知的类
return $class;
}
}

// 默认
return $class;
}

// 如果在全局环境中调用静态方法,则前一种方法不起作用,因此我们需要告诉 BaseClass 哪一个
public static function setNextStatic($class) {
// 保存值
self::$nextStatic = $class;
}
}

// 声明各种静态方法的通用类
GenericClass extends BaseClass {
public static
$name = 'Generic';

public function
getName() {
$static = get_class_vars(get_class($this));
return
$static['name'];
}

public static function
basicClassName() {
return
self::$name;
}

public static function
staticClassName() {
// 获取真实名称
$staticName = self::getStatic();

// 返回最终类名
$static = get_class_vars($staticName);
return
$static['name'];
}
}

// 最终类
SomeClass extends GenericClass {
public static
$name = 'Some';

public static function
returnClassNameWith($string) {
return
$string.' : '.self::staticClassName();
}
}

// 实例调用

// 将打印 'Some'
$a = new SomeClass();
echo
'$a 的名称:'.$a->getName().'<br />';

// 静态调用

// 将打印 'Generic'
echo '对 SomeClass::$name 的基本调用:'.SomeClass::basicClassName().'<br />';

// 将打印 'Generic'
echo '对 SomeClass::$name 的全局调用:'.SomeClass::staticClassName().'<br />';

// 将打印 'Some'
BaseClass::setNextStatic('SomeClass');
echo
'预设的 SomeClass::$name 的全局调用:'.SomeClass::staticClassName().'<br />';

// 将打印 'Some'
echo '对 SomeClass::$name 的内部调用:'.SomeClass::returnClassNameWith('这是一个 ').'<br />';

?>

此解决方法存在两个问题
- 如果从全局环境调用静态方法,则需要在调用方法之前声明类的名称,否则解决方法无效(参见第 3 和第 4 个示例)。但我假设良好的编程习惯很少从全局作用域调用静态方法,因此如果您使用了这种方法,修复起来应该不会花费太长时间。
- 此解决方法无法访问私有或受保护的静态变量,因为它使用了 `get_class_vars()` 函数。如果您找到更好的解决方案,请告知我们。

- 使用 PHP 5.3.0,升级将非常容易:只需从基类中删除这些方法,并将所有对 `getStatic()` 和 `setNextStatic()` 的调用替换为 `static::`——或者可以使用 `PHP_VERSION` 值选择器来包含使用解决方法的 `BaseClass` 文件或使用 `static` 的 `BaseClass` 文件:
0
deadimp at gmail dot com
16 年前
- 我认为这也会非常有用。
- 我的问题是,仅仅 `static` 本身能否解析为延迟静态类?
- 我之所以这样问,是因为这有助于从基类创建派生类的新的实例,方法是调用派生类的静态方法,而不是必须创建一个派生类的新的实例——或者为每个派生类显式定义一个 `getClass` 方法。
- 示例
<?php
// 我发布的这个示例实际上没有任何目的
//只是一个随意的实现
class Base {
static function
useful() {
// 创建派生类实例列表
$list=array();
for (
$i=0;$i<10;$i++) $list[]=new static(); // 这就是问题的关键
return $list;
}
}
class
Derived extends Base {
static function
somethingElse() {
//...
$list=static::useful();
}
}
?>
我不确定这会带来什么样的词法/任何你所谓的解析问题。我认为它不太可能与其他使用 `static` 的上下文(变量/方法声明)发生冲突。

- 更进一步,有没有办法获取关键字“self”、“parent”或“static”所引用的类的名称?
- 示例
<?php
class Base {
static function
stuff() {
echo
"Self: ".get_class(self);
echo
"Parent: ".get_class(parent);
echo
"Derived: ".get_class(static);
}
}
class
Derived extends Base {
static function
stuff() {
static::
stuff();
}
}
?>

- 我认为不需要在 PHP 核心添加大量代码来支持所有这些功能,但利用 PHP 的动态特性将会是很好的。

- 另一个旁注
- 如果您在基类的某个方法的实例级别作用域中,并且想要获取顶级静态变量,这里有一个不太优雅的解决方法(来自 Thacmus /lib/core.php - 请参阅 SVN 仓库)
<?php
// 获取对类中静态变量的引用 [?]
//$class - 类名或对象 (使用 get_class())
//$var - 不说了
function& get_static($class,$var) { //'static_get'?
if (!is_string($class)) $class=get_class($class);
if (!@
property_exists($class,$var)) {
trigger_error("静态属性不存在: $class::\$$var");
//debug_callstack(); // 这只是 debug_backtrace() 的包装器,用于 HTML
return null;
}
// 存储一个引用,以便可以引用基本数据
// 代码 [[ return eval('return &'.$class.'::$'.$var.';') ]] 不起作用 - 无法返回引用...
// 要建立引用,请使用 [[ $ref=&get_static(...) ]]
eval('$temp=&'.$class.'::$'.$var.';'); // 使用
return $temp;
}
?>
0
max at mastershrimp dot com
16 年前
- 如果您使用的是 PHP < 5.3.0,您可能对以下延迟静态绑定解决方法感兴趣:http://de2.php.net/manual/de/function.get-class.php#77698
-1
gern_ at hotmail dot com
16 年前
- 用于 PHP < 5.3 的 `get_called_class`

<?php
/**
* 返回调用的类名
*
* @author Michael Grenier
* @param int $i_level 可选
* @return string
*/
function get_called_class ($i_level = 1)
{
$a_debug = debug_backtrace();
$a_called = array();
$a_called_function = $a_debug[$i_level]['function'];
for (
$i = 1, $n = sizeof($a_debug); $i < $n; $i++)
{
if (
in_array($a_debug[$i]['function'], array('eval')) ||
strpos($a_debug[$i]['function'], 'eval()') !== false)
continue;
if (
in_array($a_debug[$i]['function'], array('__call', '__callStatic')))
$a_called_function = $a_debug[$i]['args'][0];
if (
$a_debug[$i]['function'] == $a_called_function)
$a_called = $a_debug[$i];
}
if (isset(
$a_called['object']) && isset($a_called['class']))
return (string)
$a_called['class'];
$i_line = (int)$a_called['line'] - 1;
$a_lines = explode("\n", file_get_contents($a_called['file']));
preg_match("#([a-zA-Z0-9_]+){$a_called['type']}
{$a_called['function']}( )*\(#", $a_lines[$i_line], $a_match);
unset(
$a_debug, $a_called, $a_called_function, $i_line, $a_lines);
if (
sizeof($a_match) > 0)
$s_class = (string)trim($a_match[1]);
else
$s_class = (string)$a_called['class'];
if (
$s_class == 'self')
return
get_called_class($i_level + 2);
return
$s_class;
}
?>
-2
php at mikebird dot co dot uk
16 年前
- 如果您的项目有很多单例类,例如,这将使生活更轻松,更整洁。

<?php

class Singleton {

public static
$objInstance;

public static function &
getInstance() {
if (
self::$objInstance == null) {
$strClass = static::getClass();
self::$objInstance = new $strClass;
}
return
self::$objInstance;
}

public static function
getClass() {
return
__CLASS__;
}

}

class
Foo extends Singleton {

public
$intBar;

public function
__construct() {
$this->intBar = 1;
}

public static function
getClass() {
return
__CLASS__;
}

}


$objFooTwo = Foo::getInstance();
$objFooTwo->intBar = 2;

$objFooOne = Foo::getInstance();

if (
$objFooOne->intBar == $objFooTwo->intBar) {
echo
'it is a singleton';
} else {
echo
'it is not a singleton';
}

?>

以上代码将输出“it is a singleton”。这种方法明显的缺点是无法向构造函数传递参数。
-3
kenneth at kennethjorgensen dot com
15年前
一个简单的基础类,它使用 `get_called_class()` 创建单例实例。php at mikebird dot co dot uk 的一篇之前的帖子解释了如何做到这一点,但是扩展的静态变量需要你在子类中定义它们才能工作。

<?php

abstract class Singleton {
private static
$instances = array();

public function
__construct() {
$class = get_called_class();
if (
array_key_exists($class, self::$instances))
trigger_error("Tried to construct a second instance of class \"$class\"", E_USER_WARNING);
}

public static function
getInstance() {
$class = get_called_class();
if (
array_key_exists($class, self::$instances) === false)
self::$instances[$class] = new $class();
return
self::$instances[$class];
}
}

class
A extends Singleton {
}

class
B extends Singleton {
}

$a1 = A::getInstance();
$a2 = A::getInstance();
$b1 = B::getInstance();
$b2 = B::getInstance();

if (
get_class($a1) == "A" &&
get_class($a2) == "A" &&
get_class($b1) == "B" &&
get_class($b2) == "B" &&
$a1 === $a2 &&
$b1 === $b2)
echo
"All good\n";
else
echo
"FAIL!\n";

?>

你可能注意到了 `self::` 而不是 `static::` 的使用,这是因为我们希望静态变量是私有的,而使用 `static::` 将不允许我们这样做。
-2
martinpauly [at] google mail [dot] com
16 年前
这对变量也适用吗?

如果以下代码可以工作,那就太好了

<?php
class A {
protected static
$table = "table";
public static function
connect(){
//在此处执行一些操作
echo static::$table;
return static::
getInstance(); //getInstance() 函数现在可以根据调用它的上下文返回类 A 或类 B
}
...
}

class
B extends A {
protected static
$table = "subtable";
...
}

$table = B::connect(); //希望输出为:subtable
?>
-3
jrfish dot x at gmail dot com
13年前
考虑一下这个

<?php
class A
{

// 一些内容....

public static function getInstance()
{
return new
self();
}

}

class
B extends A
{
//一些内容...
}

$obj = B::getInstance();

//与以下代码对比

class A
{

// 一些内容....

public static function getInstance()
{
return new static();
}

}

class
B extends A
{
//一些内容...
}

$obj = B::getInstance();
?>

静态变量和常量也以同样的方式工作
-3
iamscrumpyjack
16 年前
我一直渴望看到这个问题得到解决。我非常期待 PHP 5.3 的正式发布……

在我的例子中,我一直在尝试执行以下操作

class A {
function __construct() {
echo "I was called by " . static::__CLASS__;
}
}

class B extends A {
function Foo() {
echo "I am class " . __CLASS__;
}
}

$b = new B; // 应该输出 "I was called by B"
$b->Foo(); // 应该输出 "I am class B"

目前我使用以下变通方法

class A {
function __construct($child) {
echo "I was called by " . $child;
}
}

class B extends A {
function __construct() {
parent::__construct(__CLASS__);
}

function Foo() {
echo "I am class " . __CLASS__;
}
}

$b = new B; // 输出 "I was called by B"
$b->Foo(); // 输出 "I am class B"

如你所见,我目前的变通方法有一些额外开销,并且不像延迟静态绑定方法那样严密。
-4
Anonymous
16 年前
尝试通过单例模式重新创建一个对象的继承静态部分。

<?php
/**
* PHP 5.3 之前版本的“可继承静态”
* << Library/Inheritable.php >>
*/

abstract class Inheritable_Static extends Singleton
{
}

abstract class
Inheritable
{
public static function
getStatic($className)
{
// 使用抽象单例
return Singleton::getInstance($className . '_Static') ;
}

public function
goStatic()
{
return
self::getStatic(get_class($this)) ;
}
}

/**
* 抽象类
* << Library/SayIt/Abstract.php >>
*/

abstract class SayIt_Abstract_Static extends Inheritable_Static
{
public
$format ;
}

abstract class
SayIt_Abstract extends Inheritable
{
protected
$_name ;

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

final public function
sayIt()
{
echo
sprintf($this->goStatic()->format, $this->_name) . "\n" ;
}

}

/**
* 具体类
* << Library/SayIt/Hello.php >>
*/

class SayIt_Hello_Static extends SayIt_Abstract_Static
{
}

class
SayIt_Hello extends SayIt_Abstract
{
public static function
getStatic() { return parent::getStatic(__CLASS__) ; }
}

/**
* 测试
*/

SayIt_Hello::getStatic()->format = 'Hello %s' ;

$w = new SayIt_Hello('World') ;
$j = new SayIt_Hello('Joe') ;

echo
$w->sayIt() ; // Hello World
echo $j->sayIt() ; // Hello Joe
-2
5imun at github dot com
2年前
从父类获取扩展类的静态属性时必须小心,下面的例子展示了使用 `property_exists` 方法 (getA2 方法) 代替 `isset` 结合 `static` 关键字 (getA1 方法) 来检查静态属性是否存在,可以得到更直观的结果。

<?php
class Foo
{
public static
string $A;

public static function
init() {
return static::class;
}

public static function
getA1() {
if (!isset(static::
$A)) {
static::
$A = static::class;
}
return static::
$A;
}

public static function
getA2() {
if (
property_exists(static::class, 'A')) {
static::
$A = static::class;
}
return static::
$A;
}
}
class
Bar extends Foo {}

$foo = new Foo();
echo
$foo->getA1();
echo
$foo->getA2();
echo
$foo->getA1();

$bar = new Bar();
echo
$bar->getA1();
echo
$bar->getA2();
echo
$bar->getA1();
?>

输出
Foo
Foo
Foo

Foo
Bar
Bar

注意 `$bar->getA1()` 返回 "Foo" 而不是许多人期望看到的 "Bar"。
To Top