有了前面的基础知识,就可以开始学习 PHP 中的面向对象了。

类的定义

简单来说定义一个类的方式是这样的:


<?php
class Person {
	public $name = 'Meniny';
	public function getName() {
		return $this->name; 
	}
}
?>

类的实例化

那么创建这个类的实例则是这样的:


<?php
$person = new Person();
?>

或者兜个圈子:


$clsName = 'Person';
$person = new $clsName();

访问控制

上面用到的访问控制关键字,和主流语言没什么不同:

  • public : 公开

  • protected : 受保护

  • private : 私有

注意: 属性变量必须声明访问控制,方法可以省略方法控制声明,默认为 public


<?php
class Guy {
	public $name = 'Tom';
	protected $age = 20;
	private $wealth = 100000;
}

$guy = new Guy();
echo $guy->name; 		// ✔ 正确
echo $guy->age;		// ✗ 错误
echo $guy->wealth;	// ✗ 错误
?>

静态方法

此外,你还可以使用 static 关键字来修饰类中的方法使其成为静态方法,静态方法不需要实例对象,可以通过类名调用,操作符为双冒号(::)。


<?php
class Pet {
	public static function getOwner() {
		return 'Meniny'; 
	}
}
echo Pet::getOwner();
?>

或者再兜个圈子:


$func = 'getOwner';
$cls = 'Pet';
echo $cls::$func();

类的继承

PHP 中的类同样可以集成,通常实现一个类的集成是这样的:


<?php
class Creature {
	// ....
}

class Human extends Creature {
	// ....
}
?>

构造与析构

在类中我们可以使用 __construct() 定义构造函数,供该类实例化时调用。

注意,__construct() 前面是两个连续的下划线(_)


<?php
class Car {
	function __construct() {
		print "construct()";
	}
}

$car = new Car();
?>

在执行上面 $car = new Car(); 代码时,构造函数将会被调用,因此在控制台应当有字符串 construct() 输出。

如果定义了构造函数,那么父类中的构造函数将不会再被调用,除非进行显式调用。


<?php
class Creature {
	function __construct() {
		print "Some Creature\n";
	}
}

class Human extends Creature {
	function __construct() {
		parent::__construct();
		print "Human\n";
	}
}

$human = new Human();
?>

类似的,我们还可以使用 __destruct() 定义析构函数,供对象销毁时调用。


<?php
class Car {
	function __construct() {
		print "construct()";
	}
	function __destruct() {
		print "destruct()";
	}
}

$car = new Car(); // 输出"construct()"
unset($car); // 输出"destruct()"
?>

因为 PHP 代码之行结束后主动回收和销毁对象,因此通常来说,很少显式对对象进行销毁操作。

重载

PHP 中的重载指的是属性和方法的动态创建。

属性重载

属性重载通过 __set__get__isset__unset 来实现。


<?php
class Car {
    private $ary = array();
    
    public function __set($key, $val) {
        $this->ary[$key] = $val;
    }
    
    public function __get($key) {
        if (isset($this->ary[$key])) {
            return $this->ary[$key];
        }
        return null;
    }
    
    public function __isset($key) {
        if (isset($this->ary[$key])) {
            return true;
        }
        return false;
    }
    
    public function __unset($key) {
        unset($this->ary[$key]);
    }
}
$car = new Car();
// 动态创建
$car->name = 'BMW';
echo $car->name;
?>

方法重载

方法重载通过 __call 来实现。如果调用不存在的方法的将转为参数调用 __call 方法,如果调用不存在的静态方法用 __callStatic 重载。


<?php
class Car {
    public $price = 100000;
    public function __call($name, $args) {
        if ($name == 'raiseThePrice') {
            $this->price += 2000;
        }
    }
}
$car = new Car();
// 调用不存在的方法
$car->raiseThePrice();
echo $car->price;
?>

克隆对象

在某些情况下,我们可能需要将某个对象复制一份使用,这时我们可以使用 clone 关键字来进行克隆操作,通过调用 __clone() 方法设置属性。


<?php
class Sheep {
    public $name = 'Dolly';
    public function __clone() {
        $obj = new Sheep();
        $obj->name = $this->name;
    }
}
$s1 = new Sheep();
$s1->name = 'Dolly the Sheep';
$s2 = clone $s1;
var_dump($s2);
?>

对象序列化与反序列化

  • 要实现对象序列化,需要通过 serialize 方法进行操作。

  • 要实现对象反序列化,需要通过 unserialize 方法进行操作。


<?php
	class Secret {
		public $name = 'the Secret';
	}
	$a = new Secret();
	// 序列化
	$str = serialize($a); 
	echo $str."\n";
	// 反序列化
	$b = unserialize($str); 
	var_dump($b);
?>

对象比较

对于同一个类的两个实例,要判断是否所有属性都相等,可以使用 == 运算符。

对于两个变量是否引用字同一对象时,可以使用 === 运算符。