测试环境
PHP version 5.3.29/5.6.29/7.1.0
Apache 2.4.25
PHP 对待对象的方式与引用和句柄相同,即每个变量都持有对象的引用,而不是整个对象的拷贝。
类常量
Example-1
自 PHP 5.5 起,关键词
class
也可用于类名的解析。使用 ClassName::class 可以获取一个字符串,包含了类 ClassName 的完全限定名称,这对使用了 命名空间 的类尤其有用。
1 |
|
Example-2
属性和方法位于不同的“存储空间”,这就意味着不能直接调用一个赋值给属性的匿名方法,需要先把属性赋值给一个变量。
PHP 7.0.0 之后则可以将该属性用
()
括起来然后直接调用该匿名方法。
1 |
|
Example-3
PHP 5.4 引进了一种新的创建对象后调用成员方法的表达式。
1 |
|
属性
Example-1
属性常量的初始化值必须是常量,PHP 5.6 之后也可以是常量表达式。
1 |
|
Example-2
$this
可以强转成数组。如果一个 object 类型转换为 array,则结果为一个数组,其单元为该对象的属性。键名将为成员变量名,不过有几点例外:静态属性不可访问;私有变量前会加上类名作前缀;受保护变量前会加上一个 ‘*’ 做前缀。这些前缀的前后都各有一个 NULL 字符(\0)。
1 |
|
构造方法
与其它方法不同,当__construct()
被与父类 __construct()
具有不同参数的方法覆盖时,PHP 不会产生一个 E_STRICT 错误信息。即构造方法可以重载。
Example-1
尝试从析构方法中抛出异常会导致fatal error,然而以下代码在PHP 7.1.0上会导致出错,在PHP 5.3.29 和PHP 5.6.29都能正常运行。
1 |
|
访问控制
对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。
被定义为公有的类成员可以在任何地方被访问。
被定义为受保护的类成员则可以被其自身以及其子类和父类访问。
被定义为私有的类成员则只能被其定义所在的类访问。
Example-1
上述说法并不严谨,通过
call_user_func()
和Closure::bind()
复制一个指定的绑定对象和类作用域的闭包,可以在类外访问类成员内部的私有成员和受保护成员。
1 |
|
Example-2
PHP 5.3 之后可以使用简单的变量动态调用类的静态方法或常量,但是使用对象属性调用类的静态方法或类常量,在PHP 7.0之前的版本会抛出语法错误。
1 |
|
抽象类
Example-1
不能在抽象类的内部通过self实现抽象类内部的抽象方法。
1 |
|
Example-2
继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。
此外抽象方法的调用方式必须匹配,即类型和所需参数数量必须一致。(如果子类参数列表中有默认参数则例外)
1 |
|
子类可以定义父类抽象方法中不存在的可选参数。
1 |
|
子类的方法调用可以和抽象父类的类型和参数不一致,如重写了子类的默认参数。
1 |
|
Example-3
抽象类的抽象方法可以是静态的,但最好不要这么做。虽然在PHP 7.1上无错误,但是在PHP5.6.29上提示Strict standards:静态方法不能用在抽象类里。这是因为静态方法只能由该类调用,但是抽象类意味着一定要有其他的类去实现它的方法。
虽然抽象类不能被实例化,但是抽象类可以调用类内的静态非抽象方法。
1 |
|
Example-4
抽象类不一定是基类,也可以继承自非抽象类。
1 |
|
对象接口
- 要实现一个接口,使用
implements
操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。 - 类可以实现多个接口,用逗号来分隔多个接口的名称。 接口也可以多继承,通过使用
extends
操作符。 - 实现多个接口时,接口中的方法不能有重名。
- 类要实现接口,必须使用和接口中所定义的方法完全一致的方式,否则会导致致命错误。
- 接口中也可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所重写。
Example-1
实现接口的方法时,如果方法的参数中设置了默认值,则可以和接口中定义的方法不一致,且不会报错。
1 |
|
Example-2
接口中的常量不能被子类或子接口重写,但是可以使用一个(抽象)类实现该接口,然后另一个类继承实现该接口的类,就可以重写接口常量。
1 |
|
Example-3
可以在类声明之前实例化该类的对象,但是并不能在实现接口之前实例化实现接口的类。
1 |
|
Trait
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。详见手册
Example-1
从基类继承的成员会被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
1 |
|
Example-2
如果 trait 定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。如果该属性在类中的定义与在 trait 中的定义兼容(同样的可见性和初始值)则错误的级别是 E_STRICT,否则是一个致命错误。
1 |
|
匿名类
Example-1
PHP 7 开始支持匿名类。可以传递参数到匿名类的构造器,也可以继承(extends)其他类、实现接口(implement s),以及像其他普通的类一样使用 trait。
1 |
|
Example-2
匿名类由引擎分配名字,如下所示:
1 |
|
重载
PHP所提供的”重载”(overloading)是指动态地”创建”类属性和方法。我们是通过魔术方法(magic methods)来实现的。
当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。
所有的重载方法都必须被声明为 public。
Example-1
程序运行时已经存在类的实例化对象的属性,但是被unset()之后,也可以实现重载。
1 |
|
Example-2
可以通过魔术方法在类外获取私有属性。但是不应该在程序运行时动态改变属性值。
1 |
|
Example-3
调用对象不存在的方法时请区分静态方法和非静态方法,静态方法用
__callstatic()
调用,非静态方法用__call()
调用。而且非静态方法只能通过类的实例化对象调用,否则会报错。
1 |
|
Final关键字
如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。
属性不能被定义为 final,只有类和方法才能被定义为 final。
Example-1
类的私有成员不能被继承,所以子类中可以定义父类的同名方法,但是如果父类定义了
private final
方法,子类中一定不要出现与同类同名的方法,否则会报错。
1 |
|
后期静态绑定
“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为”静态绑定”,因为它可以用于(但不限于)静态方法的调用。
Example-1
后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用
parent::
或者self::
将转发调用信息。
1 |
|
对象和引用
PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。在php5,一个对象变量只是保存一个标识符来访问真正的对象内容。
Example-1
当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。
请注意unset删除对象和给对象赋值为null的区别。
1 |
|
序列化和反序列化
所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。
Example-1
对象的静态成员不能被序列化,
protected
类型的受保护属性前加*
做前缀,private
类型的私有属性前加类名做前缀,反序列化后只保存对象的变量,不保留方法。
1 | // file "classa.php" |
1 | // file "page1.php" |
1 | // file "page2.php" |