基础知识
1. 范围解析操作符 (::)
可以用于访问静态成员, 类常量, 还可以用于覆盖类中的属性和方法
self,parent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的
parent 用于调用父类中被覆盖的属性或方法(出现在哪里, 就将解析为相应类的父类)
self 用于调用本类中的方法或属性(出现在哪里, 就将解析为相应的类; 注意与 $this 区别,$this 指向当前实例化的对象)
当一个子类覆盖其父类中的方法时, PHP 不会调用父类中已被覆盖的方法是否调用父类的方法取决于子类
2. PHP 内核将类的继承实现放在了 "编译阶段"
- <?php
- class A{
- const H = 'A';
- const J = 'A';
- static function testSelf(){
- echo self::H; // 在编译阶段就确定了 self 解析为 A
- }
- }
- class B extends A{
- const H = "B";
- const J = 'B';
- static function testParent(){
- echo parent::J; // 在编译阶段就确定了 parent 解析为 A
- }
- /* 若重写 testSelf 则能输出 B, 且 C::testSelf()也是输出 B
- static function testSelf(){
- echo self::H;
- }
- */
- }
- class C extends B{
- const H = "C";
- const J = 'C';
- }
- B::testParent();
- B::testSelf();
- echo "\n";
- C::testParent();
- C::testSelf();
运行结果:
AA
AA
结论:
self:: 和 parent:: 出现在某个类 X 的定义中, 则将被解析为相应的类 X, 除非在子类中覆盖父类的方法
3.Static(静态)关键字
作用:
- 在函数体内的修饰变量的 static 关键字用于定义静态局部变量
- 用于修饰类成员函数和成员变量时用于声明静态成员
- (PHP5.3 之后)在作用域解析符 (::) 前又表示静态延迟绑定的特殊类
例子:
定义静态局部变量(出现位置: 局部函数中)
特征: 静态变量仅在局部函数域中存在, 但当程序执行离开此作用域时, 其值并不丢失
- <?php
- function test()
- {
- static $count = 0;
- $count++;
- echo $count;
- if ($count <10) {
- test();
- }
- $count--;
- }
定义静态方法, 静态属性
a)声明类属性或方法为静态, 就可以不实例化类而直接访问
b)静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)
c)如果没有指定访问控制, 属性和方法默认为公有
d)由于静态方法不需要通过对象即可调用, 所以伪变量 $this 在静态方法中不可用
e)静态属性不可以由对象通过 -> 操作符来访问
f)用静态方式调用一个非静态方法会导致一个 E_STRICT 级别的错误
g)就像其它所有的 PHP 静态变量一样, 静态属性只能被初始化为文字或常量, 不能使用表达式所以可以把静态属性初始化为整数或数组, 但不能初始化为另一个变量或函数返回值, 也不能指向一个对象
a. 静态方法例子(出现位置: 类的方法定义)
- <?php
- class Foo {
- public static function aStaticMethod() {
- // ...
- }
- }
- Foo::aStaticMethod();
- $classname = 'Foo';
- $classname::aStaticMethod(); // 自 PHP 5.3.0 后, 可以通过变量引用类
- ?>
b. 静态属性例子(出现位置: 类的属性定义)
- <?php
- class Foo
- {
- public static $my_static = 'foo';
- public function staticValue() {
- return self::$my_static; //self 即 FOO 类
- }
- }
- class Bar extends Foo
- {
- public function fooStatic() {
- return parent::$my_static; //parent 即 FOO 类
- }
- }
- print Foo::$my_static . "\n";
- $foo = new Foo();
- print $foo->staticValue() . "\n";
- print $foo->my_static . "\n"; // Undefined "Property" my_static
- print $foo::$my_static . "\n";
- $classname = 'Foo';
- print $classname::$my_static . "\n"; // As of PHP 5.3.0
- print Bar::$my_static . "\n";
- $bar = new Bar();
- print $bar->fooStatic() . "\n";
- ?>
c. 用于后期静态绑定(出现位置: 类的方法中, 用于修饰变量或方法)
下面详细分析
后期静态绑定(late static binding)
自 PHP 5.3.0 起, PHP 增加了一个叫做后期静态绑定的功能, 用于在继承范围内引用静态调用的类
1. 转发调用与非转发调用
转发调用 :
指的是通过以下几种方式进行的静态调用: self::,parent::,static:: 以及 forward_static_call()
非转发调用 :
明确指定类名的静态调用(例如 Foo::foo())
非静态调用(例如 $foo->foo())
2. 后期静态绑定工作原理
原理: 存储了在上一个非转发调用 (non-forwarding call) 中的类名意思是当我们调用一个转发调用的静态调用时, 实际调用的类是上一个非转发调用的类
例子分析:
- <?php
- class A {
- public static function foo() {
- echo __CLASS__."\n";
- static::who();
- }
- public static function who() {
- echo __CLASS__."\n";
- }
- }
- class B extends A {
- public static function test() {
- echo "A::foo()\n";
- A::foo();
- echo "parent::foo()\n";
- parent::foo();
- echo "self::foo()\n";
- self::foo();
- }
- public static function who() {
- echo __CLASS__."\n";
- }
- }
- class C extends B {
- public static function who() {
- echo __CLASS__."\n";
- }
- }
- C::test();
- /*
- * C::test(); // 非转发调用 , 进入 test()调用后, 上一次非转发调用存储的类名为 C
- *
- *// 当前的上一次非转发调用存储的类名为 C
- * public static function test() {
- * A::foo(); // 非转发调用, 进入 foo()调用后, 上一次非转发调用存储的类名为 A, 然后实际执行代码 A::foo(), 转 0-0
- * parent::foo(); // 转发调用, 进入 foo()调用后, 上一次非转发调用存储的类名为 C, 此处的 parent 解析为 A , 转 1-0
- * self::foo(); // 转发调用, 进入 foo()调用后, 上一次非转发调用存储的类名为 C, 此处 self 解析为 B, 转 2-0
- * }
- *
- *
- * 0-0
- *// 当前的上一次非转发调用存储的类名为 A
- * public static function foo() {
- * static::who(); // 转发调用, 因为当前的上一次非转发调用存储的类名为 A, 故实际执行代码 A::who(), 即 static 代表 A, 进入 who()调用后, 上一次非转发调用存储的类名依然为 A, 因此打印 A
- * }
- *
- * 1-0
- *// 当前的上一次非转发调用存储的类名为 C
- * public static function foo() {
- * static::who(); // 转发调用, 因为当前的上一次非转发调用存储的类名为 C, 故实际执行代码 C::who(), 即 static 代表 C, 进入 who()调用后, 上一次非转发调用存储的类名依然为 C, 因此打印 C
- * }
- *
- * 2-0
- *// 当前的上一次非转发调用存储的类名为 C
- * public static function foo() {
- * static::who(); // 转发调用, 因为当前的上一次非转发调用存储的类名为 C, 故实际执行代码 C::who(), 即 static 代表 C, 进入 who()调用后, 上一次非转发调用存储的类名依然为 C, 因此打印 C
- * }
- */
故最终结果为:
- A::foo()
- A
- A
- parent::foo()
- A
- C
- self::foo()
- A
- C
3. 更多静态后期静态绑定的例子
a)Self, Parent 和 Static 的对比
- <?php
- class Mango {
- function classname(){
- return __CLASS__;
- }
- function selfname(){
- return self::classname();
- }
- function staticname(){
- return static::classname();
- }
- }
- class Orange extends Mango {
- function parentname(){
- return parent::classname();
- }
- function classname(){
- return __CLASS__;
- }
- }
- class Apple extends Orange {
- function parentname(){
- return parent::classname();
- }
- function classname(){
- return __CLASS__;
- }
- }
- $apple = new Apple();
- echo $apple->selfname() . "\n";
- echo $apple->parentname() . "\n";
- echo $apple->staticname();
- ?>
运行结果:
Mango
Orange
Apple
b)使用 forward_static_call()
- <?php
- class Mango
- {
- const NAME = 'Mango is';
- public static function fruit() {
- $args = func_get_args();
- echo static::NAME, "" . join(' ', $args) ."\n";
- }
- }
- class Orange extends Mango
- {
- const NAME = 'Orange is';
- public static function fruit() {
- echo self::NAME, "\n";
- forward_static_call(array('Mango', 'fruit'), 'my', 'favorite', 'fruit');
- forward_static_call('fruit', 'my', 'father\'s','favorite','fruit');
- }
- }
- Orange::fruit('NO');
- function fruit() {
- $args = func_get_args();
- echo "Apple is" . join('', $args)."\n";
- }
- ?>
运行结果:
- Orange is
- Orange is my favorite fruit
- Apple is my father's favorite fruit
c)使用 get_called_class()
- <?php
- class Mango {
- static public function fruit() {
- echo get_called_class() . "\n";
- }
- }
- class Orange extends Mango {
- //
- }
- Mango::fruit();
- Orange::fruit();
- ?>
运行结果:
Mango
Orange
应用
前面已经提到过了, 引入后期静态绑定的目的是: 用于在继承范围内引用静态调用的类
所以, 可以用后期静态绑定的办法解决单例继承问题
先看一下使用 self 是一个什么样的情况:
- <?php
- // new self 得到的单例都为 A
- class A
- {
- protected static $_instance = null;
- protected function __construct()
- {
- //disallow new instance
- }
- protected function __clone(){
- //disallow clone
- }
- static public function getInstance()
- {
- if (self::$_instance === null) {
- self::$_instance = new self();
- }
- return self::$_instance;
- }
- }
- class B extends A
- {
- protected static $_instance = null;
- }
- class C extends A{
- protected static $_instance = null;
- }
- $a = A::getInstance();
- $b = B::getInstance();
- $c = C::getInstance();
- var_dump($a);
- var_dump($b);
- var_dump($c);
运行结果:
- E:\code\php_test\apply\self.php:37:
- class A#1 (0) {
- }
- E:\code\php_test\apply\self.php:38:
- class A#1 (0) {
- }
- E:\code\php_test\apply\self.php:39:
- class A#1 (0) {
- }
通过上面的例子可以看到, 使用 self, 实例化得到的都是类 A 的同一个对象
再来看看使用 static 会得到什么样的结果
- <?php
- // new static 得到的单例分别为 D,E 和 F
- class D
- {
- protected static $_instance = null;
- protected function __construct(){}
- protected function __clone()
- {
- //disallow clone
- }
- static public function getInstance()
- {
- if (static::$_instance === null) {
- static::$_instance = new static();
- }
- return static::$_instance;
- }
- }
- class E extends D
- {
- protected static $_instance = null;
- }
- class F extends D{
- protected static $_instance = null;
- }
- $d = D::getInstance();
- $e = E::getInstance();
- $f = F::getInstance();
- var_dump($d);
- var_dump($e);
- var_dump($f);
运行结果:
- E:\code\php_test\apply\static.php:35:
- class D#1 (0) {
- }
- E:\code\php_test\apply\static.php:36:
- class E#2 (0) {
- }
- E:\code\php_test\apply\static.php:37:
- class F#3 (0) {
- }
可以看到, 使用 static 可以解决 self 时出现的单例继承问题
来源: http://www.jb51.net/article/136794.htm