延迟静态绑定

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

添加笔记

用户贡献笔记 33 笔记

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

<?php

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

class
Product extends Model
{
protected static
$name = 'Product';
}

Product::find();

?>

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

抽象类 A{

静态函数
create(){

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

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

}

}

B 扩展 A{
}

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

?>
MelkiySoft
6 年前
<?php

A
{

}

B 扩展 A
{
公共静态函数
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) { }
MikeT
1 年前
注意:static::class 不总是像你预期的那样工作
<?php
命名空间 NameSpace;

类 Class
{
静态函数
getClass()
{
return static::class;
}
}

Class::
getClass()
?>
可能返回 \NameSpace\Class 或 Class,具体取决于上下文
backnot
2 年前
在上面的示例(#3)中,为了使其正常工作,你可以将子类的函数从 'private' 更改为 'protected'(或 public),它将通过 'static' 被调用。

<?php
A {
私有函数
foo() {
echo
"success!\n";
}
公共函数
test() {
$this->foo();
static::
foo();
}
}

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

C 扩展 A {
保护函数
foo() { //注意这里的更改
echo 'hello world!';
}
}

$b = new B();
$b->test();
$c = new C();
$c->test(); // 'success' 'hello world'
?>
sskaje at gmail dot com
8 年前
static::class 和 self::class 可用于获取当前类名,
在 5.5 和 5.6 中有效
在 5.3 中失败。

<?php
a{
函数
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"

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

<?php
ParentClass
{
静态
$A = 'ParentVariable';

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

ChildClass 扩展 ParentClass
{
静态
$A = 'ChildVariable';

静态函数
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
Anonymous
2 年前
类 P_Class {
公共静态 $val = "Parent";
公共静态函数 setVal($val){
static::$val = $val;
}
公共静态函数 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"
tyler AT canfone [dot] COM
16 年前
@ php at mikebird

你可以通过 getInstance 方法将参数传递给你的构造函数,假设你运行的是 php5。

公共静态函数 getInstance($params = null) {
如果 (self::$objInstance == null) {
$strClass = static::getClass();
self::$objInstance = new $strClass($params);
}
return self::$objInstance;
}

这将把参数传递给你的构造函数。热爱 php。
steven dot karas+nospam at gmail dot com
13 年前
此函数可以用作 PHP >= 5.1.0 中迟绑定的解决方法。这个函数在其他地方还有另一个类似的版本,但使用了 eval。

<?php

函数 & static_var($class, $name)
{
如果 (
is_object($class))
{
$class = get_class($class);
}
否则如果 ( !
is_string($class))
{
抛出新
Exception('必须提供对象或类名', NULL);
}

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

?>
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
?>
jakub dot lopuszanski at nasza-klasa dot pl
13 年前
令人惊讶的是,即使使用 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)
?>
kx
15 年前
至少在 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"

?>
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
?>
adam dot prall at thinkingman dot com
14 年前
只是提醒一下,始终检查语法。虽然我喜欢 LSB,但我认为它不起作用。

static::$sKey 未设置

…直到我意识到我完全忘记了将其设为变量变量

$sKey = 'testStaticClassVarNameThatExistsInThisClassesScope';

static::$$sKey 已设置

…当然,这适用于 PHP 中的任何地方,但由于延迟静态绑定的(当前)新颖性,我从其他人那里看到了很多包含此特定错误的代码。
joost dot t dot hart at planet dot nl
14 年前
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

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

<?php
class 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() );

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

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

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

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

<?php
class 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;
}
}

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

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

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

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

<?php

// BaseClass 类将被任何需要静态继承解决方法的类扩展
class 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;
}
}

// 声明各种静态方法的通用类
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'];
}
}

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

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

// 实例调用

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

// 静态调用

// 将打印 'Generic'
echo 'Basic call to SomeClass::$name : '.SomeClass::basicClassName().'<br />';

// 将打印 'Generic'
echo 'Global call to SomeClass::$name : '.SomeClass::staticClassName().'<br />';

// 将打印 'Some'
BaseClass::setNextStatic('SomeClass');
echo
'Global call to SomeClass::$name with pre-set : '.SomeClass::staticClassName().'<br />';

// 将打印 'Some'
echo 'Internal call to SomeClass::$name : '.SomeClass::returnClassNameWith('This is a ').'<br />';

?>

此解决方法有两个问题
- 如果从全局环境调用静态方法,则需要在调用方法之前声明类的名称,否则解决方法将不起作用(参见第 3 和第 4 个示例)。但我认为良好的编程会很少从全局范围调用静态方法,因此如果你使用它,应该很容易修复。
- 此解决方法无法访问私有或受保护的静态变量,因为它使用 get_class_vars()。如果你找到更好的解决方案,请告知我们。

使用 Php 5.3.0,升级将很简单:只需从基本类中删除方法,并使用 static:: 搜索/替换对 getStatic() 和 setNextStatic() 的任何调用 - 或者可以使用 PHP_VERSION 值选择器来包含使用解决方法的 BaseClass 文件或使用 static 的 BaseClass 文件:
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;
}
?>
max at mastershrimp dot com
16 年前
如果你使用的是 PHP < 5.3.0,你可能会对以下解决延迟静态绑定的方法感兴趣:http://de2.php.net/manual/de/function.get-class.php#77698
gern_ at hotmail dot com
15 年前
用于 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;
}
?>
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
'它是一个单例';
} else {
echo
'它不是一个单例';
}

?>

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

<?php

抽象类 Singleton {
private static
$instances = array();

public function
__construct() {
$class = get_called_class();
if (
array_key_exists($class, self::$instances))
trigger_error("尝试构造类 \"$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
"一切正常\n";
else
echo
"失败!\n";

?>

你可能注意到使用了 self:: 而不是 static:: ,这是因为我们希望静态变量是私有的,而使用 static:: 将不允许我们这样做。
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
?>
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();
?>

同样适用于静态变量和常量
iamscrumpyjack
15 年前
我一直渴望看到这个问题得到解决。我非常期待 PHP 5.3 的正式版发布…

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

class A {
function __construct() {
echo "我被 " . static::__CLASS__ . " 调用";
}
}

class B extends A {
function Foo() {
echo "我是类 " . __CLASS__;
}
}

$b = new B; // 应该输出 "我被 B 调用"
$b->Foo(); // 应该输出 "我是类 B"

目前,我采用以下解决方法

class A {
function __construct($child) {
echo "我被 " . $child . " 调用";
}
}

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

function Foo() {
echo "我是类 " . __CLASS__;
}
}

$b = new B; // 输出 "我被 B 调用"
$b->Foo(); // 输出 "我是类 B"

如你所见,我当前的解决方法有一些开销,而且不像后期静态绑定方法那样严谨。
tfn dot yldrm at hotmail dot com
13 年前
5.2 版本上的静态绑定示例,以及真实的枚举
<?php
/**
* PHP 5.2 上的静态绑定
* @author Tufan Baris YILDIRIM
* @since 2010-10-26
*/
abstract class EnumBase
{
protected
$num = 0;
public
$toString;
public
$toInt;

public function
__construct($enumKeyOrVal)
{

unset(
$this->toString,$this->toInt);

$enums = $this->enums();

if(
empty(
$enumKeyOrVal)
||
!(isset(
$enums[$this->num = $enumKeyOrVal])
||
(
$this->num = array_search($enumKeyOrVal,$enums)) !== false)
)
$this->num = 0;

/**
* 5.3 版本
*/
/*
* if(
empty($enumKeyOrVal)
||
!(isset(static::$enums[$this->num = $enumKeyOrVal])
||
($this->num = array_search($enumKeyOrVal,static::$enums)) !== false)
)
$this->num = 0;
*/
}

# 升级到 5.3 后不需要。
public function vars()
{
return
get_class_vars(get_class($this));
}

public function
enums()
{
$vars = $this->vars();
return
$vars['enums'];
}

public function
__get($property)
{
if(
method_exists($this,'__'.$property))
return
$this->{'__'.$property}();
else
return
$this->__toString();
}

public function
__toInt()
{
return
$this->num;
}

public function
__toString()
{

$enums = $this->enums();

if(isset(
$enums[$this->num]))
{
return
$enums[$this->num];
}
else
{
return
$enums[0];
}

/**
* 5.3 版本
*/

/*
if(isset(static::$enums[$this->num]))
{
return static::$enums[$this->num];
}
else
{
return static::$enums[0];
}
*/
}
}

class
Positions extends EnumBase
{
public static
$enums = array(
0 => '未知',
1 => '守门员',
2 => '后卫',
3 => '中场',
4 => '前锋'
);
}


$a = new Positions('中场');
$b = new Positions(4);

$c = (string)$a; // 中场
$d = (string)$b; // 前锋
?>
匿名
13 年前
这对我很有效

<?php
abstract class ParentClass
{
static function
parent_method()
{
$child_class_str = self::get_child_class();
eval(
"\$r = ".$child_class_str."::abstract_static();");
return
$r;
}
// 子类必须重写来将自身添加到跟踪中

protected abstract static function abstract_static();

private static function
get_child_class()
{
$backtrace = debug_backtrace();
$num = count($backtrace);
for(
$i = 0; $i < $num; $i++)
{
if(
$backtrace[$i]["class"] !== __CLASS__)
return
$backtrace[$i]["class"];
}
return
null;
}
}

class
ChildClass extends ParentClass
{
static function
parent_method(){ return parent::parent_method(); }

protected static function
abstract_static()
{
return
__METHOD__."()";
}
// 来自 ParentClass
}

print
"调用的是: ". ChildClass::parent_method();
?>
匿名
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
5imun 在 github.com
2 年前
在从父类获取扩展类的静态属性时,您必须小心,在下面的示例中,您可以看到使用 property_exists(方法 getA2)而不是使用带有 static 关键字的 isset(方法 getA1)来检查静态属性是否存在,会得到更直观的的结果

<?php
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;
}
}
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"。
tom
15 年前
你可能会发现这对于被动代码测试很有用

<?php
BaseClass {
function
__get($id) {
throw new
Exception("试图访问未定义的属性 '$id'.");
}
function
__set($id) {
throw new
Exception("试图访问未定义的属性 '$id'.");
}
}

MyClass extends BaseClass {
// 我的实现
}
?>

使用上面描述的这些魔术方法将帮助你找到尝试访问未定义(和未记录)类成员的类。在大多数情况下:这是基于拼写错误的成员名称的错误。
jrfish dot x at gmail dot com
13 年前
同样适用于静态变量和常量
To Top