类与对象

构造函数和析构函数

  • 构造函数

具有构造函数的类会在每次创建新对象时先调用此方法,所以适合在使用对象前做一些初始化工作。

Note: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承。
  • 析构函数

析构函数会在某个对象的所有引用都被删除或者当对象被显式销毁时执行。

访问控制

被定义为公有的类成员可以在任何地方被访问。被定义为受保护的类成员则可以被其自身以及其子类和父类访问。被定义为私有的类成员则只能被其自身访问。例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?php
/**
* Define MyClass
*/
class MyClass
{
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';

function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}

$obj = new MyClass();
echo $obj->public; // 这行能被正常执行
echo $obj->protected; // 这行会产生一个致命错误
echo $obj->private; // 这行也会产生一个致命错误
$obj->printHello(); // 输出 Public、Protected 和 Private


/**
* Define MyClass2
*/
class MyClass2 extends MyClass
{
// 可以对 public 和 protected 进行重定义,但 private 而不能
protected $protected = 'Protected2';

function printHello()
{
echo $this->public;
echo $this->protected;
echo $this->private;
}
}

$obj2 = new MyClass2();
echo $obj2->public; // 这行能被正常执行
echo $obj2->private; // 未定义 private
echo $obj2->protected; // 这行会产生一个致命错误
$obj2->printHello(); // 输出 Public、Protected2 和 Undefined

抽象类

定义为抽象的类不能被实例化。如果某个类里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。

继承一个抽象类时,子类必须定义父类中的所有抽象方法;另外这些方法的访问控制必须和父类中一样(或者更为宽松)

Trait

PHP是单继承语言,trait是为这种单继承语言而准备的一种代码复用机制,Trait使开发人员可以自由的在不同层次结构内独立的类中复用方法。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /*1*/ }
function getReturnDescription() { /*2*/ }
}

class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
}

class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>

优先级
从基类继承的成员会被trait插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了trait的方法,而trait则覆盖了被继承的方法。例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}

class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

以上例程会输出:

Hello Universe!

重载

重载是指动态的”创建”类属性和方法。是通过魔术方法来实现的。

当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。

属性重载

  • 在给不可访问(或未定义)属性赋值时,__set()会被调用
  • 在读取不可访问(或未定义)属性时,__get()会被调用
  • 当对不可访问(或未定义)属性调用isset()或empty()时,__isset()会被调用
  • 当对不可访问(或未定义)属性调用unset()时,__unset()会被调用

方法重载

使用__call()函数__callStatic()函数

在对象中调用一个不可访问方法时,__call() 会被调用。

在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。