本文最后更新于:2024年3月18日 凌晨
PHP 声明类
- 如果想用面向对象的风格来设计你的PHP程序或者类库,你需要用class关键字来定义你的类,类定义包括类的名称,属性和方法等,类名不区分大小写,命名规则和PHP标识符的命名规则相同,类名stdClass为保留名称,不可用,下面是定义一个类的语法:
1 2 3 4 5 6 7 8 9
| class classname [extends baseclass] { [var $property [ = value ];...] [function functionname(args){ } ... ] }
|
声明类方法
- 方法即类内部定义的函数,尽管PHP没有特殊限制,绝大多数类方法通常都只操作方法所在的对象内部的数据,在将来PHP预定义的类方法命名会采用两个下划线
__
开头(现在已经用在了对象序列化方法__sleep()
和__wakup()
上),所以建议你自己的类方法命名不要采用__
开头。
- 在类方法中,
$this
变量包含当前对象的引用,指向方法所属的对象,例如,当你在birthday()
方法内调用$rasmus->birthday()
,用$this
和$rasums
是一样的,类方法可以使用$this
来访问当前对象的属性或者调用对象的其他方法。
- 下面是一个简单的Person类定义,说明如何使用变量
$this
:
1 2 3 4 5 6 7 8 9 10 11
| class Person{ var $name;
function get_name(){ return $this->name; }
function set_name($new_name){ $this->name = $new_name; } }
|
- 就像你看到的,
get_name()
和set_name()
方法使用了$this
来访问和设置当前对象的$name
属性。
- 为了声明一个方法为静态方法,可以使用static关键字,在静态方法内部,
$this
会失效。
1 2 3 4 5 6 7 8 9 10 11 12
| class HTML_Stuff{ static function start_table(){ echo "<table border='1'>\n"; } static function end_table(){ echo"</table>\n"; } }
HTML_Stuff::start_table();
HTML_Stuff::end_table();
|
- 如果使用final关键字来声明一个类方法,子类将无法覆写这个方法,例如:
1 2 3 4 5 6 7
| class Person{ var $name;
final function get_name(){ return $this->name; } }
|
声明属性
- 在Person类之前的定义当中,我们显示地声明了
$name
属性,属性的声明不是必须的,但对于维护你的代码的人来说,这是很有帮助的,声明属性是种很好的PHP编码习惯,当然你也可以在任何时候增加新的属性。
- 下面这个版本的Person类有一个未声明的
$name
属性:
1 2 3 4 5 6 7 8
| class Person{ function get_name(){ return $this->name; } function set_name($new_name){ $this->name = $new_name; } }
|
- 你可以赋予属性默认值,但默认值只能是常量,不能是表达式:
1 2 3
| var $name = 'J Doe'; var $age = 0; var $day = 60*50*24;
|
- 使用访问标识符,你可以改变属性的"可见度",在对象外部范围内可以访问到的属性应该被声明为public,只能被同一个类中的方法访问到的属性应当声明为private,另外,如果属性被声明为protected,那么属性只能被当前类及其子类访问到。
- 例如,你可以这样声明一个用户类:
1 2 3 4 5
| class Person{ protected $rowId = 0; public $username = 'Anyone can see me'; private $hidden = true; }
|
- PHP还允许你定义静态属性,这种属性可以通过对象的名称来直接访问,而不需要先实例化对象,例如:
1 2 3 4
| class Person{ static $global = 23; } $localCopy = Person::$global;
|
- 在类的实例中,你也可以用self关键字来调用静态属性,如
echo self::$global
;
- 如果一个未定义的属性被访问,并且类中定义了
__get()
或__set()
方法,这两个方法将优先给该属性赋值或者取得该属性的值。
- 例如,定义一个类来从数据库中读取数据,除非是在特定的条件下,你可能不想读取大量数据例如BLOB类型的数据,一个常见的实现方法就是,为需要访问的属性创建访问方法,不管在什么时候请求,都去读写数据库,另一种方法则是:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Person{ __get($property){ if($property == 'biography'){ $biography = "long text here..."; return $biography; } } __set($property,$value){ if($property == 'biography'){ } } }
|
声明类常量
- 和用
define()
定义的全局变量不同,PHP提供了一个方法在类内部定义常量,就像静态属性一样,类常量可以直接访问,不需要实例化,一旦定义了一个类常量,它的值就不能改变。
1 2 3 4 5
| class PaymentMethod{ const TYPE_CREDTTCARD = 0; const TYPE_CASH = 1; } echo PaymentMethod::TYPE_CREDITCARD;
|
继承
- 为了继承另一个类中的属性和方法,我们可以在类定义时使用extends关键字,加上父类的名称:
1 2 3 4 5 6
| class Person{ var $name,$address,$age; } class Emplotee extends Person{ var $position,$salary; }
|
- Employee类包含有自带的
$position
和$salary
属性,也包含有继承自Person类的$name
,$address
和$age
属性。
- 如果一个衍生类(子类)与它的父类具有相同名称的属性或方法,则衍生类中的属性和方法比父类中的优先级高,会覆写(override,也可称覆盖)父类中的属性和方法,访问类属性会返回子类中的属性值,引用类方法则会调用衍生类中的方法。
- 为了访问父类中的被覆写的方法,可以用
parent::method()
:
- 一个常见的错误是把父类的名称硬编码到对被覆写方法的调用中:
- 这样会出错,因为它在子类中直接引用了父类名,正确的作法是在extends语句中使用
parent::
- 如果一个方法为当前类所定义的,并且你想确保调用的是当前类的方法,可使用
self::method()
:
为了检查一个对象是否是一个特定的类的实例,或者是否实现了一个特定的接口(interface),你可以使用instanceof操作符:
1 2 3
| if($object instanceof Animal){ }
|
接口
- 接口(interface)提供了定义一个类所遵循的规则的途径,接口提供了类方法的原型和常量,任何实现(implement)该接口的类必须提供接口中所有方法的具体实现。
- 下面是定义一个接口的语法:
1 2 3 4 5
| interface interfacenamename{ [ function functionname(); ... ] }
|
- 为了声明一个类实现了一个接口,请使用implements关键字,若同时实现多个接口,用逗号将多个接口名称隔开:
1 2 3 4 5 6 7 8
| interface Printable{ function printOutput(); } class ImageComponent implements Printable{ function printOutput(){ echo "Printing an image...."; } }
|
- 接口可以继承自其它接口(可以是多个接口),只要它继承的接口和方法和子接口中的方法不重名即可。
抽象类方法
- PHP也提供了一种机制,让一个类中特定的方法在子类中必须实现----在父类中这些方法没有具体实现,仅提供方法名称,在这样的情况下,你可以提供一个抽象类方法(abstract method),另外,一个类中只要有一种方法定义为抽象方法,就要用abstract关键字将该类定义为抽象类(abstract class)
1 2 3 4 5 6 7 8
| abstract class Component{ abstract function printOutput(); } class ImageComponent extends Component{ function printOutput(){ echo "Pretty picture"; } }
|
- 抽象类不能直接实例化,要注意与某些语言不同,你不能为抽象方法提供一个默认的实现。
构造函数
1
| $person = new Person('Fred',35);
|
- 这些参数被传递给类的构造函数(或称构造方法,constructor),一个用来初始化类属性的特殊函数。
- 构造函数是类中名为
__construct()
,下面是Person类的构造函数:
1 2 3 4 5 6
| class Person{ function _ _construct($name,$age){ $this->name = $name; $this->age = $age; } }
|
- PHP并不支持构造函数链的自动调用,也就是说,当你实例化子类时,只有子类自己的构造函数会被调用,父类的构造函数是不会调用的,为了使父类的构造函数也被调用,你要在子类的构造函数中显式地调用父类的构造函数,在下例中,Employee类的构造函数调用了父类Person类的构造函数;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Person{ var $name,$address,$age; function Person($name,$address,$age){ $this->name = $name; $this->address = $address; $this->age = $age; } } class Employee extends Person{ var $position,$salary; function Employee($name,$address,$age,$position,$salary){ $this->Person($name,$address,$age); $this->position = $position; $this->salary = $salary; } }
|
析构函数
- 从PHP5开始,我们可以在类中使用析构函数(或析构方法,destructor),当一个对象被销毁时,比如一个对象的最后一个引用被删除时,或者脚本执行结束时,就会调用析构函数,其实PHP本身会在程序执行结束时自动清理所有资源,所以析构函数的作用有限,但可用来记录一个对象的销毁,析构函数是类中名为
__destruction()
思维函数:
1 2 3 4 5
| class Building{ function __destruction(){ echo "A building is being destroyed!"; } }
|