PHP 声明类

本文最后更新于: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表格的行和列。
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():
1
parent::birthday();// 调用父类中的birthday()方法。
  • 一个常见的错误是把父类的名称硬编码到对被覆写方法的调用中:
1
Creature::birthday();//Creature是父类。
  • 这样会出错,因为它在子类中直接引用了父类名,正确的作法是在extends语句中使用parent::
  • 如果一个方法为当前类所定义的,并且你想确保调用的是当前类的方法,可使用self::method():
1
self::birthday();// 调用当前类的birthday()方法。

为了检查一个对象是否是一个特定的类的实例,或者是否实现了一个特定的接口(interface),你可以使用instanceof操作符:

1
2
3
if($object instanceof Animal){
// 如果是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!";
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!