最终我们可以实现一些 ActiveRecord 方法了
<?php
类 Model
{
public static function find()
{
echo static::$name;
}
}
类 Product 扩展 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
类 Model
{
public static function find()
{
echo static::$name;
}
}
类 Product 扩展 Model
{
protected static $name = 'Product';
}
Product::find();
?>
输出:'Product'
对于带有静态工厂方法的抽象类,您可以使用`static`关键字代替`self`,如下所示
<?php
抽象类 A{
static function create(){
//return new self(); //致命错误:无法实例化抽象类 A
return new static(); //这是正确的方法
}
}
类 B 扩展 A{
}
$obj=B::create();
var_dump($obj);
?>
<?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) { }
警告:`static::class`并不总是像你预期的那样工作
<?php
命名空间 NameSpace;
类 Class
{
static function getClass()
{
return static::class;
}
}
Class::getClass()
?>
可能会返回 `\NameSpace\Class` 或 `Class`,取决于上下文
在上面的例子(#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'
?>
`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"
*/
理解最简单的方法是运行这段脚本
<?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
类 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"
@ 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。
此函数可用作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);
}
?>
我已经使用延迟静态绑定实现了枚举。
<?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
类 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() );
?>
示例:仅当父类中未定义时,才在子类中设置静态属性。许多人期望输出为“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
这是一个针对静态继承问题的简单解决方法。它并不完美,但有效。
<?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` 文件:
- 我认为这也会非常有用。
- 我的问题是,仅仅 `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 'it is a singleton';
} else {
echo 'it is not a singleton';
}
?>
以上代码将输出“it is a singleton”。这种方法明显的缺点是无法向构造函数传递参数。
一个简单的基础类,它使用 `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::` 将不允许我们这样做。
这对变量也适用吗?
如果以下代码可以工作,那就太好了
<?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 "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"
如你所见,我目前的变通方法有一些额外开销,并且不像延迟静态绑定方法那样严密。
尝试通过单例模式重新创建一个对象的继承静态部分。
<?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 方法) 代替 `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"。