最后我们终于可以实现一些 ActiveRecord 方法了
<?php
class Model
{
public static function find()
{
echo static::$name;
}
}
class Product extends Model
{
protected static $name = 'Product';
}
Product::find();
?>
输出:'Product'
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
最后我们终于可以实现一些 ActiveRecord 方法了
<?php
class Model
{
public static function find()
{
echo static::$name;
}
}
class Product extends Model
{
protected static $name = 'Product';
}
Product::find();
?>
输出:'Product'
对于具有静态工厂方法的抽象类,可以使用 static 关键字而不是 self,如下所示
<?php
抽象类 A{
静态函数 create(){
//return new self(); //致命错误:无法实例化抽象类 A
return new static(); //这是正确的方法
}
}
类 B 扩展 A{
}
$obj=B::create();
var_dump($obj);
?>
<?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) { }
注意:static::class 不总是像你预期的那样工作
<?php
命名空间 NameSpace;
类 Class
{
静态函数 getClass()
{
return static::class;
}
}
Class::getClass()
?>
可能返回 \NameSpace\Class 或 Class,具体取决于上下文
在上面的示例(#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'
?>
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"
*/
最简单的理解方式是运行这段脚本
<?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
类 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"
@ php at mikebird
你可以通过 getInstance 方法将参数传递给你的构造函数,假设你运行的是 php5。
公共静态函数 getInstance($params = null) {
如果 (self::$objInstance == null) {
$strClass = static::getClass();
self::$objInstance = new $strClass($params);
}
return self::$objInstance;
}
这将把参数传递给你的构造函数。热爱 php。
此函数可以用作 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);
}
?>
我已经使用迟绑定实现了枚举。
<?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
?>
令人惊讶的是,即使使用 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)
?>
至少在 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"
?>
关于静态参数,它们按预期工作。
<?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
?>
只是提醒一下,始终检查语法。虽然我喜欢 LSB,但我认为它不起作用。
static::$sKey 未设置
…直到我意识到我完全忘记了将其设为变量变量
$sKey = 'testStaticClassVarNameThatExistsInThisClassesScope';
static::$$sKey 已设置
…当然,这适用于 PHP 中的任何地方,但由于延迟静态绑定的(当前)新颖性,我从其他人那里看到了很多包含此特定错误的代码。
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
?>
我发现了一个有趣的事情。类名字符串必须直接从“扁平”变量访问。从类实例传递的数组中获取其变量的延迟静态绑定代码会抛出语法错误。错误?
<?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() );
?>
在子类中从父类设置静态属性的示例,只有在未定义的情况下才设置,许多人会期望输出为“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
这是我针对静态继承问题做出的一个小解决方法。它并不完美,但它有效。
<?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 文件:
我认为这也会非常有用。
我的问题是,“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;
}
?>
如果你使用的是 PHP < 5.3.0,你可能会对以下解决延迟静态绑定的方法感兴趣:http://de2.php.net/manual/de/function.get-class.php#77698
用于 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
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 '它不是一个单例';
}
?>
上面的代码将输出 '它是一个单例'。这种方法的明显缺点是无法向构造函数传递参数。
简单的基本类,使用 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:: 将不允许我们这样做。
这对于变量也适用吗?
如果以下内容有效,那就太棒了
<?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
?>
请考虑以下情况
<?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();
?>
同样适用于静态变量和常量
我一直渴望看到这个问题得到解决。我非常期待 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"
如你所见,我当前的解决方法有一些开销,而且不像后期静态绑定方法那样严谨。
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; // 前锋
?>
这对我很有效
<?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();
?>
尝试通过单例模式重新创建对象的继承静态部分。
<?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
在从父类获取扩展类的静态属性时,您必须小心,在下面的示例中,您可以看到使用 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"。