需要的数据库

CREATE TABLE `user` (
	`uid` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
	`name` varchar(50) NOT NULL COMMENT '姓名',
	`age` smallint(3) unsigned NOT NULL COMMENT '年龄',
	PRIMARY KEY (`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

INSERT INTO `user` VALUES ('1', '欧阳克', '18');
INSERT INTO `user` VALUES ('2', '老顽童', '50');
INSERT INTO `user` VALUES ('3', '黄蓉', '33');
INSERT INTO `user` VALUES ('4', '郭靖', '18');
INSERT INTO `user` VALUES ('5', '一灯大师', '80');
INSERT INTO `user` VALUES ('6', '洪七公', '79');

:-: 一、创建类

# 创建类
class Animal{

}
# 调用类(实例化)
$monkey = new Animal();    // 猴子
$rabbit = new Animal();    // 兔子

[danger] 备:类如果只能使用一次,那我们没必须用类,每次直接写代码就可以了。所以类可以实例化多次(N次),次数无限制。

var_dump($monkey == $rabbit);
echo '<br>';

var_dump($monkey === $rabbit);
echo '<br>';
var_dump($monkey instanceof Animal);
echo '<br>';

:-: 二、类属性

在类里直接写代码,是错误的

# 错误示例
class Animal{
	echo 111;
}

1、属性(变量)

class People{
	$name = '杨幂';	//会报错,必须有修饰符
	$age  = 31;		//会报错,必须有修饰符
	// 属性 设置了初始值
	public $name = '杨幂';
	public $age  = 31;
}
# 外部访问:需要通过访问限定符、或修饰符
$yangmi = new People;
echo $yangmi->name;

属性重新赋值

$yangmi->name = '欧阳克';
$yangmi->age = 18;
echo $yangmi->name.$yangmi->age;

2、行为(方法)

class People{
	// 属性
	public $name = '杨幂';
	public $age = 31;
	// 方法,默认就是public ,不加也是
	public function getInfo(){
		echo '姓名:杨幂,年龄:31';
		return '姓名:杨幂,年龄:31';
	}
	// 方法
	public function getInfo1(){
		// self : 当前类
		$obj = new self();
		// 输出对象属性
		return  '姓名: ' .$obj->name .', 年龄: ' . $obj->age . '<br>';
	}
	// 方法
	public function getInfo2(){
		// 因为该方法必须通过对象调用,所有没必要在类中实例化
		// 直接引用该类的实例化对象即可
		// 在类中使用伪变量: "$this" 引用当前类的实例
		// $this = new self();  相当于先执行了这条语句,尽管你不需要这样做
		return  '姓名: ' .$this->name .', 年龄: ' . $this->age . '<br>';
	}
	// 方法
	public function getInfo3(){
		// 当前类
		$obj = new People();
		$obj->name = '欧阳克';
		$obj->age = 18;
		// 输出对象属性
		return  '姓名: ' .$obj->name .', 年龄: ' . $obj->age . '<br>';
	}
}
// 类实例化
$yangmi = new People();
echo $yangmi->getInfo();
echo $yangmi->getInfo1();
echo $yangmi->getInfo2();
echo $yangmi->getInfo3();
// 查看类中定义的对象方法: public 才会显示出来
$methods = get_class_methods('People');
echo '<pre>'.print_r($methods,true);
echo '<hr>';

:-: 三、构造方法(魔术方法)

class People{
	// 属性
	public $name;
	public $age;
	// 构造方法
	public function __construct($name, $age){
		echo '开始执行';
		$this->name = $name;
		$this->age = $age;
	}
	// 方法
	public function getInfo(){
		return  '姓名: ' .$this->name .', 年龄: ' . $this->age . '<br>';
	}
}
// 实例化
$obj = new People('杨幂',31);
echo $obj->getInfo();

:-: 四、析构方法(魔术方法)

class People{
	// 属性
	public $name;
	public $age;
	// 构造方法
	public function __construct($name, $age){
		echo '开始执行';
		$this->name = $name;
		$this->age = $age;
	}
	// 方法
	public function getInfo(){
		return  '姓名: ' .$this->name .', 年龄: ' . $this->age . '<br>';
	}
	// 析构方法
	public function __destruct(){
		echo '类执行完毕,要关闭了';
	}
}
// 实例化
$obj = new People('杨幂',31);
echo $obj->getInfo();
$obj = null; // 如果没有手动释放,就会在最后执行析构方法

实战:自动连接数据库

class Db{
	// 连接参数
	public $dsn;
	public $user;
	public $password;
	// 连接属性
	public $pdo;
	// 连接方法
	public function connect(){
		// 使用PDO方式管理数据库, 连接成功则返回PDO对象,赋值给对象属性pdo
		$this->pdo = new PDO($this->dsn, $this->user, $this->password);
	}
	// 希望在实例化时, 自动连接数据库, 这个需求很常见
	public function __construct($dsn, $user, $password){
		// 初始化对象属性
		$this->dsn = $dsn;
		$this->user = $user;
		$this->password = $password;
		// 自动调用对象方法,连接数据库
		$this->connect();
	}
	// 析构方法
	public function __destruct(){
		$this->pdo = null;
	}
}
// 实例化
$db = new Db('mysql:host=localhost;dbname=ouyangke', 'root', 'root');
if ($db->pdo) {
	echo '<h2>连接成功</h2>';
}
// 读取数据库测试
$stmt = $db->pdo->prepare('select * from user');
$stmt->execute();
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $user) {
	print_r($user); echo '<br>';
}

:-: 五、类的继承:类的第一特点

class People{
	// 对象属性
	public $name;
	public $age;
	// 构造方法
	public function __construct($name, $age){
		$this->name = $name;
		$this->age = $age;
	}
	// 方法
	public function getInfo(){
		return  '姓名: ' .$this->name .', 年龄: ' . $this->age;
	}
}
// 子类Sub1, 代码复用
class Woman extends People{
	// ...
}
// 实例化子类Woman, 尽管子类中无任何成员,但是它可以直接调用父类People中的全部成员
$sub1 = new Woman('杨幂', 31);
echo $sub1->getInfo() . '<br>';

实现子类里的方法

// 子类Woman, 增加属性和方法,扩展父类功能
class Woman extends People{
	public $wages;  // 工资
	// 子类的构造方法
	public function __construct($name, $age, $wages){
		// 调用父类的构造方法,否则还要手工把父类中的属性初始化语句全部写一遍
		// parent:: 调用被覆写的父类方法内容
		parent::__construct($name, $age);
		// 只需要添加子类中的成员初始化代码
		$this->wages = $wages;
	}
	// 计算一年工资
	public function total(){
	   return $this->wages*12;
	}
}
// 实例化子类
$sub2 = new Woman('杨幂',31,500000);
echo $sub2->name . '的年薪: ' . $sub2->total() . '<br>';

子类重写父类方法

// 如果父类有这个方法,子类也用了这个方法
// 第三个子类, 继承自Woman, 而Star又继承自People,这就形成了多层给的继承关系
class Star extends Woman{
	// 重写父类total()方法
	public function total(){
		$total = parent::total();
		// 判断工资单位
		switch ($total) {
			case $total>100000000:
				$total = ($total/10000).'亿';
				break;
			case $total>10000:
				$total = ($total/10000).'万';
				break;
			default:
				$total = $total;
				break;
		}
		return $total;
	}
}
// 实例化子类
$sub3 = new Star('杨幂',31,500000);
echo $sub3->name . '的年薪: ' . $sub3->total() . '<br>';
echo '<hr>';

:-: 六、类的封装:类的第二特点

成员(变量)的封装

class Woman{
	// 属性
	public $name;			// 姓名
	protected $age;			// 年龄
	private $wages;			// 工资
	// 构造方法
	public function __construct($name, $age, $wages){
		$this->name = $name;
		$this->age = $age;
		$this->wages = $wages;
	}
}
// 类实例化
$obj = new Woman('杨幂',31,500000);

echo $obj->name, '<br>';

// echo $obj->age, '<br>';    // 会报错

// echo $obj->wages, '<br>';    // 会报错

// 继承后访问
class Star extends Woman{
	public function info(){
		echo $this->name, '<br>';
		echo $this->age, '<br>';
		// echo $this->wages, '<br>';    // 会报错
	}
}
// 类实例化
$obj1 = new Star('baby',28,400000);

echo $obj1->name, '<br>';

// echo $obj->age, '<br>';    // 会报错

// echo $obj->wages, '<br>';    // 会报错

echo $obj1->info();
echo '<hr>';

行为(方法)的封装

class Woman{
	// 属性
	public $name;			// 姓名
	protected $age;			// 年龄
	private $wages;			// 工资
	// 构造方法
	public function __construct($name, $age, $wages){
		$this->name = $name;
		$this->age = $age;
		$this->wages = $wages;
	}
	public function name(){
		return '我的名字叫:'.$this->name.'<br>';
	}
	protected function age(){
		return '我的年龄是:'.$this->age.'<br>';
	}
	private function wages(){
		return '我的工资是:'.$this->wages.'<br>';
	}
	public function all(){
		echo $this->name();
		echo $this->age();
		echo $this->wages();
	}
}
// 类实例化
$obj = new Woman('杨幂',31,500000);

echo $obj->name();

//echo $obj->age();    // 会报错

//echo $obj->wages();    // 会报错

echo $obj->all();

class Star extends Woman{
	public function info(){
		echo $this->name();
		echo $this->age();
		// echo $this->wages(); //私有的会报错
	}
	public function a(){
		echo $this->all();
	}
}
// 类实例化
$obj1 = new Star('baby',28,400000);
echo $obj1->a();
echo '<hr>';

关键词 类外声明 声明类 声明属性 声明方法 解释
const 定义类常量
extends 扩展类,用一个类去扩展它的父类
public 公用属性或方法
protected 私有属性或方法
private 受保护的属性或方法
abstract 抽象类或方法
final 类不能被继承

:-: 七、类属性与类方法(静态成员)

class People{
	// 属性
	public $name;
	// 属性
	public $age;
	// 属性: 静态属性
	public static $country = '中国';
	// 构造方法
	public function __construct($name, $age){
		$this->name = $name;
		$this->age = $age;
		// $this->country = $country;	// 会报错
		// 尽管可以在构造方法中初始化静态属性,但不建议这样做,否则静态属性,无法在对象之间共享
	}
	// 对象方法
	public function getInfo1(){
		// 这个方法可以用对象访问,方法中访问了静态属性,实现了类属性在对象中的共享
		// return $this->name . '年龄是: ' . $this->age. '国家是:' . $this->country; // 这样会报错
		return $this->name . '年龄是: ' . $this->age. '国家是:' . self::$country;
	}
	// 类方法: 静态方法
	public static function getInfo2(){
		// 静态方法是类方法, 不能用对象调用,所以内部也不允许使用对象引用$this
		// 如果静态方法中,一定要用到对象属性或方法,可以用参数传入
		return $this->name . '年龄是: ' . $this->age . '国家是:' . self::$country;
	}
	// 静态方法: 以方法传参方式调用对象属性/方法
	public static function getInfo3($name,$age){
		// return $this->name; // 会报错,在静态方法里,不能访问非静态成员
		// 可以用self调用,也可以用本类名调用。 最好在本类用self,在外部用类名
		return $name . '年龄是: ' . $age . '国家是:' . Demo1::$country;
	}
}
$obj = new People('范冰冰',33);
echo $obj->name, '<br>';
echo $obj->age, '<br>';
// echo $obj->country, '<br>';	//会报错
echo People::$country;	// 应该以这种方式访问静态属性
echo '<br>';
echo $obj->getInfo1(), '<br>';
// echo $obj->getInfo2(), '<br>';	// 会报错
// echo People::getInfo2(), '<br>';	// 会报错
echo People::getInfo3($obj->name,$obj->age);
echo '<br>';
// 对象不能访问静态属性,但是可以访问静态方法
echo $obj->getInfo3($obj->name,$obj->age);

// 静态成员可以重新赋值。在创建很多对象,值不会因为创建的对象改变。
People::$country = 'china';
$obj1 = new People('杨幂',31);
echo People::$country;
echo '<hr>';

:-: 八、类常量

define('COUNTRY','中国');
class People{
	// 类常量也类属性一样,也是属于类的, 必须用类访问,不能用对象访问
	const COUNTRY = '中国';
	// 类常量与类属性的区别是: 类常量不允许修改,而类属性可以修改
	public static $sex = '女';
	private $name;
	public function __construct($name){
		$this->name = $name;
	}
	public function getInfo(){
		// 类常量在类的内部,访问方式与类属性是一样的
		return $this->name.'的性别是:' . self::$sex.',国籍是: ' . self::COUNTRY;
	}
}
$obj = new People('刘诗诗');
// 访问类属性
echo People::$sex, '<br>';
// 访问类常量
echo People::COUNTRY, '<br>';
// 访问对象方法: 该方法又访问了类属性与类常量
echo $obj->getInfo();
echo '<hr>';
// 修改类属性
People::$sex = '保密';
// 修改类常量: 报错
//People::COUNTRY = '美国';
// 可以看到类属性:$sex发生了变化
echo $obj->getInfo();
echo '<hr>';

:-: 九、属性重载

class People{
	private $name;
	private $age;
	protected $country = '中国';
	// public $country = '中国';
	// 构造方法
	public function __construct($name, $age){
		$this->name = $name;
		$this->age = $age;
	}
	// __get($name):当获取未定义可不见属性时触发
	// $name 是属性名
	public function __get($name){
		if ($name === 'country') {
			// 仅允许name=='admin'的用户可以查看country字段内容
			return ($this->name === 'admin') ? $this->$name : '无权查看';;
		}
		return $this->$name;
	}
	// __set($name, $value):当给未定义可不见属性赋值时触发
	public function __set($name, $value){
		// 直接返回, 极少这样做,这样做相当于把类属性直接设置为:public
		// $this->$name = $value;
		// 添加过滤机制
		if ($name === 'age') {
		   return $this->name === 'admin' ? $this->$name = $value : '无权修改';
		}
		return $this->$name = $value;
	}
	// __isset($name): 当检测未定义可不见属性时
	public function __isset($name){
		if ($this->name === 'admin') {
			if (isset($this->$name)){
				echo '存在该属性';
			} else {
				echo '没有该属性';
			}
		} else {
			echo '无权检测';
		}
	}
	//__unset($name): 当注销未定义可不见属性时触发
	public function __unset($name){
		if ($this->name === 'admin') {
			unset($this->$name);
		} else {
			echo '无法删除';
		}
	}
}
$obj = new People('迪丽热巴', 26);
echo $obj->name, '<br>';
echo $obj->country, '<br>';
// 怎么才能查看 country, 只能用'admin'来实例化
$obj = new People('admin', 50);
echo $obj->country, '<br>';
// 直接修改 age, 类中没有__set()会报错
$obj->age = 80;
// 查看age字段值
echo $obj->age, '<br>';
// 检测是否存在age字段
isset($obj->age);
echo '<br>';
// 删除salary属性
unset($obj->age);
echo '<br>';
isset($obj->age);
echo '<hr>';

:-: 十、方法重载

class People{
	// __call(): 访问不存在/不可见对象方法时触发,有两个参数,第一个是方法名,第二个方法的参数
	public function __call($name, $arguments){
		return '方法名: '.$name.'<br>方法参数列表: ' . '<pre>'.print_r($arguments, true).'不存在';
	}
	// __callStatic(): 访问不存在/不可见的类方法(静态)方法时触发
	public static function __callStatic($name, $arguments){
		return '方法名: '.$name.'<br>方法参数列表: ' . '<pre>'.print_r($arguments, true).'不存在';
	}
}
$obj = new People();
// 访问不存在或无权访问的对象方法
echo $obj->getInfo1(10,20,30);
echo '<hr>';
// 访问不存在或无权访问的静态类方法
echo Demo4::getInfo2('html','css', 'javascript');
echo '<hr>';

:-: 十一、小案例

function sum($a, $b) {
	return $a . ' + ' . $b . ' = ' . ($a+$b);
}
// 正常函数调用
echo sum(20, 40);
echo '<br>';
// 以回调的方式执行该函数
echo call_user_func('sum', 50, 20);
echo '<br>';
// call_user_func_array(), 第二个参数是数组格式,不能省略
echo call_user_func_array('sum', [30, 80]);
echo '<hr>';
// 现在换个思路, 将函数放在一个类中, 再来调用这个方法/函数
class Test1{
	// 对象方法
	public function sum($a, $b){
		return $a . ' + ' . $b . ' = ' . ($a+$b);
	}
}
// 如果以回调方式执行对象方法呢?
$obj = new Test1();
echo call_user_func([$obj,'sum'], 50, 20);
echo '<br>';
// 仅以call_user_func_array()举例, call_user_func()原理一样
echo call_user_func_array([$obj,'sum'], [10,30]);
echo '<br>';
// 如果仅调用一次,可以简化一下对象创建方式
echo call_user_func_array([new Test1(),'sum'], [15,35]);
echo '<hr>';
// 如果是一个静态方法,如果调用呢?
class Test2{
	// 对象方法 (乘法运算)
	public static function mul($a, $b){
		return $a . ' * ' . $b . ' = ' . ($a*$b);
	}
}
// 直接将类名与方法写在一个字符串即可
echo call_user_func_array('Test2::mul', [10,30]);
echo '<br>';
// 将类名与类方法分开,放在一个数组中
echo call_user_func_array(['Test2','mul'], [10,30]);
echo '<br>';
echo '类名是: '. Test2::class;  // 返回一个类名字符串
echo '<br>';
// 所以这样写,也是正确的
echo call_user_func_array([Test2::class,'mul'], [10,30]);

下面是一个sql语句类的案例

require 'query.php';
class Db
{
	// 数据库连接对象
	protected static $pdo = null;
	// 数据库连接方法, 每次查询时再连接, 实现真正的惰性连接,节省系统开销
	public static function connection(){
		// 为简化,这里直接使用字面量参数连接数据库,真实项目中应该将参数放在配置文件中
		self::$pdo = new PDO('mysql:host=localhost;dbname=ouyangke','root','root');
	}
	// 这是查询类操作的入口, 通过静态魔术方法进行跳转,实现对象方法的跨类调用
	public static function __callStatic($name, $arguments){
		// 创建pdo对象,并连接数据库
		self::connection();
		// 实例化查询类,将连接对象做为参数
		$query = new query(self::$pdo);
		// 执行查询类Query中的对象方法, 注意参数是数组,我只需要第一个参数:表名, 所以加了索引键名
		return call_user_func_array([$query,$name],[$arguments[0]]);
	}
}

// 客户端的链式调用
// 以Db类做入整数数据库操作的入口, SQL语句的各个部分用对象方法提供
// 链式操作是现代PHP框架的基础,非常有用
$users = Db::table('user')
		->field('uid,name,age')
		->where('uid > 1')
		->limit(5)
		->select();
// 遍历查询结果
foreach ($users as $user) {
	print_r($user); echo '<br>';
}

query.php

<?php
// 数据库查询类

class query
{
	// 连接对象
	public $pdo = null;

	// 数据表名
	public $table = '';

	// 字段列表
	public $field = '';

	// 查询条件
	public $where = '';

	// 显示数量
	public $limit = 0;

	// 构造方法,初始化连接对象
	public function __construct($pdo)
	{
		// 连接对象是对象方法的共享属性
		$this->pdo = $pdo;
	}
	// 调用表名
	public function table($tablName)
	{
		$this->table = $tablName;

		// 返回当前对象,便于链式调用该对象的其它方法
		return $this;
	}
	// 设置查询字段
	public function field($fields)
	{
		$this->field = $fields;
		return $this;
	}
	// 设置查询条件
	public function where($where)
	{
		$this->where = $where;
		return $this;
	}
	// 设置显示数量
	public function limit($limit)
	{
		$this->limit = $limit;
		return $this;
	}
	// 创建SQL查询语句对象,并返回查询结果
	public function select()
	{
		// 查询条件分开设置, 可以确保链式方法独立
		$fields = empty($this->field) ? '*' : $this->field;
		$where = empty($this->where) ? '' : ' WHERE '.$this->where;
		$limit = empty($this->limit) ? '' : ' LIMIT '.$this->limit;

		// 接装SQL语句
		$sql = 'SELECT '.$fields.' FROM '.$this->table. $where . $limit;
		// 预处理查询
		$stmt = $this->pdo->prepare($sql);
		$stmt->execute();
		return $stmt->fetchAll(PDO::FETCH_ASSOC);

	}
}

:-: 十二、自动加载

// 查看当前脚本所在的目录
echo __DIR__,  '<br>';

include __DIR__ . '/inc/Test1.php';
include __DIR__ . '/inc/Test2.php';
include __DIR__ . '/inc/Test3.php';
# 如果当前脚本使用了几十上百这样的类, 上面的方式就很不人性
# 使用下面的自动加载机制, 会根据客户端调用的类, 自动进行加载,效率高, 不出错
// php标准函数库中提供了一个自动加载文件的注册函数,可以实现这个功能
// 这个函数,在当前脚本引用一个未加载的文件时, 会自动调用它的回调方法来加载这个文件
spl_autoload_register(function ($class){
	//	include __DIR__ . '/inc/Test1.php';
	//	将include中的类名Test1用变量替换掉,这样就实现了最简单的自动加载
	//	后面我们会使用命名空间来完善这个函数,目前大家先理解到这里即可
	include __DIR__ . '/inc/'.$class.'.php';
});

$test1 = new Test1();
echo $test1->get(), '<br>';

$test1 = new Test2();
echo $test1->get(), '<br>';

$test1 = new Test3();
echo $test1->get(), '<br>';
echo '<hr>';

:-: 十三、抽象类

一个抽象类必须被扩展为一个特定的类,我们才能创建类实例,使用类中功能

abstract class a{
	public $name;
	public function __construct($name){
		$this->name = $name;
	}
	// 不管有多少个普通方法,只要有一个抽象方法,就是抽象类
	public function af(){
		echo $this->name;
	}
	// 抽象方法不能有内容,里面不能有代码,{}:不能有
	abstract public function aff();
}
// 抽象类不能实例化,不能new,只能继承
// 我们就用b类,继承 a抽象类
class b extends a{
	// b类 继承 a抽象类后:必须把a抽象类 ,里面的抽象方法,重新写一遍(实现)
	public function aff(){
		echo $this->name;
	}
}
// 实现后,我们可以调用子类,进行实例化,然后调用成员方法和成员变量。
$a = new b('欧阳克');
// 为什么抽象类里的af方法能调用呢,因为它是普通方法。
$a->af();
echo '<br/>';
// 这里的方法为什么能调用呢? 因为b类,继承了a抽象类的方法后:实现成为普通类。
$a->aff();
abstract class Person{
	protected $name;
	protected function __construct($name='peter zhu'){
		$this->name = $name;
	}
	// 该方法不需要重写, 可以通过子类对象访问,应该设置为public
	public function getName(){
		return $this->name;
	}
	// 修改属性方法,设置为抽象方法,交给子类实现
	abstract protected function setName($value);
}
// 当子类继承 抽象父类,普通的方法,可以直接使用,抽象方法,必须重新实现。
class Stu extends Person{
	// 注意: 构造方法不会自动继承, 必须手动重写
	public function __construct($name='peter zhu'){
		parent::__construct($name);
	}
	// 1,它的父类,有这个抽象方法,这里必须重新写,带着具体的代码。
	// 2,类实例化后,调用这个方法,就是直接调用这个方法,跟抽象方法没关系。
	public function setName($value){
		$this->name = $value;
	}
}
$stu = new Stu('猪哥');
echo 'php中文网创始人: ' . $stu->getName() . '<br>';
// 调用子类的重写的抽象方法setName(),来设置属性
$stu->setName('灭绝师太');
// 3,用setName传值后,值给到父抽象类里的$name,用父抽象类的getName方法可以输出传值
echo 'php中文网前端讲师: ' . $stu->getName() . '<br>';
echo '<hr>';

:-: 十三、接口

interface iVehicle{
	const COUNTRY = '中国';
	// 驱动方式: 汽车, 新能源
	public function setFuel($fuel);
	// 用途
	public function setPurpose($purpose);
}
// Car 类 实现了接口:  iVehicle,关键词:implements
// 抽象类 实现接口:  iVehicle,关键词:implements
// 接口 可以 继承接口:extends
// 类 可以 同时 继承 和实现(先继承,在实现)
// 可以实现多个接口,用逗号隔开
class Car implements iVehicle{
	public $fuel;
	public $purpose;
	// 构造方法
	public function __construct($fuel='汽油', $purpose='家用'){
		$this->fuel = $fuel;
		$this->purpose = $purpose;
	}
	// 必须实现的接口方法
	public function setFuel($fuel){
		$this->fuel = $fuel;
	}
	// 必须实现的接口方法
	public function setPurpose($purpose){
		$this->purpose = $purpose;
	}
	// 类中自定义的对象方法
	public function getInfo(){
		return $this->fuel . $this->purpose . '车 <br>';
	}
}

// 客户端代码
$car = new Car();
echo $car->getInfo();

$car->setFuel('新能源');
$car->setPurpose('公交');
echo $car->getInfo();
echo '<hr>';

如果暂时只能实现接口中的部分方法, 可以用一个抽象来实现这个接口

interface iVehicle{
	const COUNTRY = '中国';
	// 驱动方式: 汽车, 新能源
	public function setFuel($fuel);
	// 用途
	public function setPurpose($purpose);
}

abstract class Auto implements iVehicle{
	public $fuel;
	// 只实现接口中的setFuel()方法, 另一个方法并未实现
	public function setFuel($fuel){
		$this->fuel = $fuel;
	}
}

// 再创建一个类,来继承扩展这个抽象类  Auto
class Car1 extends Auto{
	public $purpose;

	// 构造方法
	public function __construct($fuel='汽油', $purpose='家用'){
		$this->fuel = $fuel;
		$this->purpose = $purpose;
	}
	// 这个方法原来在接口中声明的,在它继承的抽象类中并没有声明
	public function setPurpose($purpose){
		$this->purpose = $purpose;
	}
	// 自定义的方法
	public function getInfo(){
		return $this->fuel . $this->purpose . '车 <br>';
	}
}

// 客户端代码

$car1 = new Car1();
echo $car1->getInfo();

$car1->setFuel('天然气');
$car1->setPurpose('家用');
echo $car1->getInfo();

:-: 十四、接口按实战案例

// 定义一个接口,实现数据库常用操作:增删改查
interface iCurd
{
	// 增加数据
	public function create($data);

	// 读取数据
	public function read();

	// 更新数据
	public function update($data, $where);

	// 删除数据
	public function delete($where);
}

// 创建Db类, 实现iCurd接口,完成基本的数据库操作
class Db implements iCurd{
	//数据库的连接对象
	protected $pdo = null;

	// 数据表名
	protected $table;

	// 构造方法: 连接数据库,并设置默认数据表名称
	public function __construct($dsn, $user, $password, $table='staff'){
		$this->pdo = new PDO($dsn, $user, $password);
		$this->table = $table;
	}

	// 读取
	public function read($fields='*', $where='', $limit='0, 5'){
		// 设置查询条件
		$where = empty($where) ? '' : ' WHERE ' . $where;
		// 设置显示数量
		$limit = ' LIMIT ' . $limit;

		// 预处理查询操作
		$sql = 'SELECT '.$fields.' FROM '.$this->table.$where.$limit;
		$stmt = $this->pdo->prepare($sql);
		$stmt->execute();

		// 返回二维数组表示的查询结果集
		return $stmt->fetchAll(PDO::FETCH_ASSOC);
	}

	// 新增, 参数是数组: 新记录的键值对
	public function create($data){
		// 字段列表
		$fields = ' (name,age,sex,position,mobile,hiredate)';
		// 值列表
		$values = '(:name,:age,:sex,:position,:mobile,:hiredate)';
		// 创建SQL语句
		$sql = 'INSERT INTO '.$this->table.$fields.' VALUES '.$values;

		// 预处理执行新增操作
		$stmt = $this->pdo->prepare($sql);
		$stmt->execute($data);

		// 返回新增数量, 新增记录的ID组成的数组
		return [
			'count'=>$stmt->rowCount(),
			'id'=>$this->pdo->lastInsertId()
		];
	}

	// 更新, 为了数据安全, 不允许无条件更新
	public function update($data, $where){
		// 难点在于SET 参数的处理上,利用传入的$data数组,进行拆装

		// 获取数组的键名组成的数组
		$keyArr = array_keys($data);
		$set = '';
		// 遍历键名表示的字段列表,拼装预处理需要的sql语句,注意占符符的表示
		foreach ($keyArr as $value) {
			$set .= $value . ' = :' .$value. ', ';
		}
		// 去掉最后一个逗号, 注意每个逗号后有一个空格,去除时也要带上这个空格
		$set = rtrim($set,', ');

		// 预处理执行更新操作
		$sql = 'UPDATE '.$this->table.' SET '.$set .' WHERE ' .$where;
		$stmt = $this->pdo->prepare($sql);
		$stmt->execute($data);

		// 返回被更新的记录数量
		return $stmt->rowCount();
	}

	// 删除: 与更新一样, 这也是危险的写操作, 不允许无条件删除
	public function delete($where){
		// 预处理执行删除操作
		$sql = 'DELETE FROM '.$this->table.' WHERE '.$where;
		$stmt = $this->pdo->prepare($sql);
		$stmt->execute();
		return $stmt->rowCount();
	}
}

// 客户端的测试代码
// 实例化Db类
$dsn = 'mysql:host=localhost;dbname=ouyangke';
$user = 'root';
$password = 'root';
$db = new Db($dsn, $user, $password);


// 遍历读取
foreach ($db->read() as $item) {
	print_r($item); echo '<br>';
}

echo '<hr>';

// 新增数据
$data = [
	'name'=>'郭靖',
	'age'=>30,
	'sex'=>1,
	'position'=>'金刀驸马',
	'mobile'=>'13666668888',
	'hiredate'=>time()
];
$res = $db->create($data);
echo '成功新增'.$res['count'].'条记录,最新记录的主键ID是: '.$res['id'];

echo '<hr>';

// 更新记录
$data = [
	'age' => 5,
	'position'=>'抗金英雄'
];
$where = 'id = 5';
echo '成功更新了: ' .$db->update($data, $where). ' 条记录';

echo '<hr>';

// 删除记录
$where = 'id = 5';
echo '成功更新了: ' .$db->delete($where). ' 条记录';

:-: 十五、后期静态绑定

class A{
	public static function who(){
		echo 111;
	}
	public function test(){
		// self::who();	// 猜一下,是调用它自己的who,还是子类的who呢?

		// 那么如何在这种静态继承的上下文环境中, 静态调用类中方法的时候,正确识别调用者呢?
		// 可以将self 关键字改为: static ,
		// 注意: static 除了可以用在静态方法中, 也可以用在普通对象方法中
		static::who();
	}
}

// B继承了A,重写A类里面的who方法。
class B extends A{
	public static function who(){
		echo 222;
	}
}

$a = new B();
echo $a->test();

[info] 表格表达类相关的关键词

关键词 类外声明 声明类 声明属性 声明方法 解释
const 定义类常量
extends 扩展类,用一个类去扩展它的父类
public 公用属性或方法
protected 私有属性或方法
private 受保护的属性或方法
abstract 抽象类或方法
final 类不能被继承
interface 创建接口
implements 实现接口
parent:: 访问父类
$this-> 访问本类
self:: 访问静态
static:: 后期静态绑定
namespace 创建命名空间