1 自动加载类

1.1 类的规则

  1. 一个文件中只能放一个类(必须)
  2. 文件名和类名同名(必须)
  3. 类文件以.class.php结尾(不是必须)

1.2 手动加载类

在PHP页面上加载类文件

1
2
3
4
5
6
7
8
9
10
11
<?php
require './Goods.class.php';    //手动加载类文件
require './Book.class.php'; //手动加载类文件
require './Phone.class.php'; //手动加载类文件
//测试
$book=new Book();
$book->setName('面向对象编程');
$phone=new Phone();
$phone->setName('苹果6s');
$book->getName();
$phone->getName();

1.3 自动加载类

当缺少类的时候自动的调用__autoload()函数,并且将缺少的类名作为参数传递给__autoload()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
/*
*作用:自动加载类
*@param $class_name string 缺少的类名
*/
function __autoload($class_name) {
require "./{$class_name}.class.php";
}
//测试
$book=new Book();
$book->setName('面向对象编程');
$phone=new Phone();
$phone->setName('苹果6s');
$book->getName();
$phone->getName();

💡 注意:__autoload()函数在PHP7.2以后就不支持了。

1.4 注册加载类

通过spl_autoload_register()注册__autoload()函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
//方法一:
/*
//加载类函数
function loadClass($class_name) {
require "./{$class_name}.class.php";
}
//注册加载类函数
spl_autoload_register('loadClass');
*/

//方法二:
spl_autoload_register(function($class_name){
require "./{$class_name}.class.php";
});

//测试
$book=new Book();
$book->setName('面向对象编程');
$phone=new Phone();
$phone->setName('苹果6s');
$book->getName();
$phone->getName();

1、spl_autoload_register()可以注册多个自动加载函数

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function load1($class) {
require "./{$class}.class.php";
}
function load2($class) {
require "./{$class}.php";
}
function load3($class) {
require "./{$class}.fun.php";
}
spl_autoload_register('load1');
spl_autoload_register('load2');
spl_autoload_register('load3');

2、PHP5.1以后就开始支持此函数。

1.5 类文件存储不规则的加载方法

将类名和文件地址做一个映射,组成一个关联数组。

1
2
3
4
5
6
$map=array(
   //类名 => 类文件地址
'Goods' => './aa/Goods.class.php',
'Book' => './bb/Book.class.php',
'Phone' => './cc/Phone.class.php'
);

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
spl_autoload_register(function($class_name){
//类名和文件地址映射成一个关联数组
$map=array(
'Goods' => './aa/Goods.class.php',
'Book' => './bb/Book.class.php',
'Phone' => './cc/Phone.class.php'
);
//在映射数组中找到就包含
if(isset($map[$class_name]))
require $map[$class_name];
});
//测试
$book=new Book();
$book->setName('面向对象编程');
$phone=new Phone();
$phone->setName('苹果6s');
$book->getName();
$phone->getName();

在项目中,绝大部分都是规则存储的,不规则的比较少。

2 clone和__clone()

思考:创建对象的方式有哪些?

1
2
方法一:实例化
方法二:克隆

例题

1
2
3
4
5
6
7
8
9
10
<?php
class Student {
   //执行clone指令的时候自动执行
public function __clone() {
echo '正在克隆对象<br>';
}
}
$stu1=new Student;
$stu2=clone $stu1; //克隆对象
var_dump($stu1,$stu2);  //object(Student)#1 (0) { } object(Student)#2 (0) { }

小结:

1、clone的创建对象的方法之一

2、当执行clone指令的时候,会自动的调用__clone()方法

3 设计模式

3.1 单例模式

1.4.1 单例模式

一个类只能有一个对象

应用场景:多次请求数据库只需要一个连接对象。

实现:三私一公

1
2
3
4
1、私有的静态属性用来保存对象的单例
2、私有的构造方法用来阻止在类的外部实例化
3、私有的__clone阻止在类的外部clone对象
4、公有的静态方法用来获取对象的单例

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
//三私一公
class DB {
//静态的属性用来保存对象的单例
private static $instance;
//私有的构造方法阻止在类的外部实例化
private function __construct() {

}
//私有的__clone()阻止在类的外部clone对象
private function __clone() {

}
public static function getInstance() {
//保存的值不属于DB类的类型就实例化
if(!self::$instance instanceof self)
self::$instance=new self();
return self::$instance;
}
}
//测试
$db1=DB::getInstance();
$db2=DB::getInstance();
var_dump($db1,$db2); //object(DB)#1 (0) { } object(DB)#1 (0) { }

3.2 工厂模式

特点:传递不同的参数获取不同的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class ProductsA {
}
class ProductsB {
}
//工厂模式
class ProductsFactory {
public function create($num) {
switch($num) {
case 1:
return new ProductsA;
case 2:
return new ProductsB;
default:
return null;
}
}
}
//测试
$factory=new ProductsFactory();
$obj1=$factory->create(1);
$obj2=$factory->create(2);
var_dump($obj1,$obj2); //object(ProductsA)#2 (0) { } object(ProductsB)#3 (0) { }

3.3 策略模式

特点:传递不同的参数调用不同的策略(方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class Walk {
public function way() {
echo '走着去<br>';
}
}
class Bus {
public function way() {
echo '坐车去<br>';
}
}
//策略模式
class Student {
public function play($obj) {
$obj->way();
}
}
//测试
$stu=new Student;
$stu->play(new Walk()); //走着去
$stu->play(new Bus()); //坐车去

4 序列化与反序列化

在PHP中,数组和对象无法保存,如果需要保存就要将数组或对象转换成一个序列。

序列化:将数组或对象转换成一个序列(serialize)

反序列化:将序列化的字符串转换成数组或对象。(unserialize)

4.1 数组的序列化与反序列化

1
2
3
4
5
6
7
8
9
10
11
12
<?php
//数组的序列化
/*
$stu=['tom','berry','ketty'];
$str=serialize($stu); //序列化
file_put_contents('./stu.txt',$str);
*/

//数组的反序列化
$str=file_get_contents('./stu.txt');
$stu=unserialize($str); //反序列化
print_r($stu); //Array ( [0] => tom [1] => berry [2] => ketty )

4.2 对象的序列化与反序列化

💡 注意:对象的反序列化需要有类的参与,如果没有类在反序列化时候无法确定类

5 魔术方法

1
2
3
4
已经学过的魔术方法
__construct() 构造方法
__destruct() 析构方法
__clone() 克隆方法

5.1 __tostring()、__invoke()

__tostring():将对象当成字符串使用的时候自动调用

__invoke():将对象当成函数使用的时候自动调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
class Student {
//把对象当成字符串使用的时候自动执行
public function __tostring() {
return '这是一个对象,不是字符串<br>';
}
//把对象当成函数使用的时候自动执行
public function __invoke() {
echo '这是一个对象,不是函数<br>';
}
}
$stu=new Student;
echo $stu; //当成字符串使用
$stu(); //当成函数使用

5.2 __set()、__get()、__isset()、__unset()

1
2
3
4
__set($k,$v):给无法访问的属性赋值的时候自动执行
__get($k):获取无法访问的属性值的时候自动调用
__isset($k):判断无法访问的属性是否存在自动调用
__unset($k):销毁无法访问的属性的时候自动执行

5.3 __call()、__callstatic()

1
2
__call():调用无法访问的方法时自动执行
__callstatic():调用无法访问的静态方法时自动执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Student {
/**
*作用:调用无法访问的方法时自动执行
*@param $fn_name string 方法名
*@param $fn_args array 参数数组
*/
public function __call($fn_name,$fn_args) {
echo "{$fn_name}不存在<br>";
}
//调用无法访问的静态方法时自动执行
public static function __callstatic($fn_name,$fn_args) {
echo "{$fn_name}静态方法不存在<br>";
}
}
//测试
$stu=new Student;
$stu->show(10,20);

Student::show();

5.4 __sleep()、__wakeup()

1
2
__sleep():当序列化的时候自动调用
__wakeup():当反序列化的时候自动调用
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
<?php
class Student {
private $name;
private $sex;
private $add='中国';
public function __construct($name,$sex) {
$this->name=$name;
$this->sex=$sex;
}
/**
*序列化的时候自动调用
*@return array 序列化的属性名
*/
public function __sleep() {
return array('name','sex');
}
//反序列化的时候自动调用
public function __wakeup() {
$this->type='学生';
}
}
//测试
$stu=new Student('tom','男');
$str=serialize($stu); //序列化
$stu=unserialize($str); //反序列化
print_r($stu);

6 模拟方法重载

通过魔术方法模拟方法重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Math {
public function __call($fn_name,$fn_args) {
$sum=0;
foreach($fn_args as $v) {
$sum+=$v;
}
echo implode(',',$fn_args).'的和是:'.$sum,'<br>';
}
}
//利用魔术方法模拟方法重载
$math=new Math();
$math->call(10,20);
$math->call(10,20,30);
$math->call(10,20,30,40);

7 遍历对象

通过foreach遍历对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class Student {
public $name='tom';
protected $sex='男';
private $age=22;

public function show() {
foreach($this as $k=>$v) {
echo "{$k}-{$v}<br>";
}
}
}
//测试
$stu=new Student;
foreach($stu as $k=>$v) {
echo "{$k}-{$v}<br>";
}
echo '<hr>';
$stu->show();

结论:遍历到当前位置所能访问到属性

8 封装MySQL的单例

8.1 分析

1、实现单例

2、连接数据库

3、对数据进行操作

8.2 步骤

第一步:实现单例

第二步:初始化参数

第三步:连接数据库

第四步:操作数据

1、执行数据操作语句(增、删、改)

2、执行数据查询语句

a) 返回二维数组

b) 返回一维数组

c)返回一行一列

8.3 代码实现

第一步:实现单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class MySQLDB {
private static $instance;
private function __construct() {

}
private function __clone() {

}
public static function getInstance() {
if(!self::$instance instanceof self)
self::$instance=new self();
return self::$instance;
}
}
//测试
$db=MySQLDB::getInstance();
var_dump($db);

注意:A instanceof B,表示A是否是B的类型,返回bool值

第二步:初始化参数

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
<?php
//封装MySQL单例
class MySQLDB {
private $host; //主机地址
private $port; //端口号
private $user; //用户名
private $pwd; //密码
private $dbname; //数据接名
private $charset; //字符集
private $link; //连接对象
private static $instance;
private function __construct($param) {
$this->initParam($param);
}
private function __clone() {

}
//获取单例
public static function getInstance($param=array()) {
if(!self::$instance instanceof self)
self::$instance=new self($param);
return self::$instance;
}
//初始化参数
private function initParam($param) {
$this->host=$param['host']??'127.0.0.1';
$this->port=$param['port']??'3306';
$this->user=$param['user']??'';
$this->pwd=$param['pwd']??'';
$this->dbname=$param['dbname']??'';
$this->charset=$param['charset']??'utf8';
}
}

//测试
//配置参数
$param=array(
'user' => 'root',
'pwd' => 'root',
'dbname' => 'data'
);
//获取单例
$db=MySQLDB::getInstance($param);
var_dump($db);

第三步:连接数据库

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
47
48
49
50
51
52
53
54
55
56
<?php
//封装MySQL单例
class MySQLDB {
private $host; //主机地址
private $port; //端口号
private $user; //用户名
private $pwd; //密码
private $dbname; //数据接名
private $charset; //字符集
private $link; //连接对象
private static $instance;
private function __construct($param) {
$this->initParam($param);
$this->initConnect();
}
private function __clone() {

}
//获取单例
public static function getInstance($param=array()) {
if(!self::$instance instanceof self)
self::$instance=new self($param);
return self::$instance;
}
//初始化参数
private function initParam($param) {
$this->host=$param['host']??'127.0.0.1';
$this->port=$param['port']??'3306';
$this->user=$param['user']??'';
$this->pwd=$param['pwd']??'';
$this->dbname=$param['dbname']??'';
$this->charset=$param['charset']??'utf8';
}
//连接数据库
private function initConnect() {
$this->link=@mysqli_connect($this->host,$this->user,$this->pwd,$this->dbname);
if(mysqli_connect_error()){
echo '数据库连接失败<br>';
echo '错误信息:'.mysqli_connect_error(),'<br>';
echo '错误码:'.mysqli_connect_errno(),'<br>';
exit;
}
mysqli_set_charset($this->link,$this->charset);
}
}

//测试
//配置参数
$param=array(
'user' => 'root',
'pwd' => 'root',
'dbname' => 'data'
);
//获取单例
$db=MySQLDB::getInstance($param);
var_dump($db);

第四步:数据操作的功能

1、执行增、删、改操作

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
//封装MySQL单例
class MySQLDB {
private $host; //主机地址
private $port; //端口号
private $user; //用户名
private $pwd; //密码
private $dbname; //数据接名
private $charset; //字符集
private $link; //连接对象
private static $instance;
private function __construct($param) {
$this->initParam($param);
$this->initConnect();
}
private function __clone() {

}
//获取单例
public static function getInstance($param=array()) {
if(!self::$instance instanceof self)
self::$instance=new self($param);
return self::$instance;
}
//初始化参数
private function initParam($param) {
$this->host=$param['host']??'127.0.0.1';
$this->port=$param['port']??'3306';
$this->user=$param['user']??'';
$this->pwd=$param['pwd']??'';
$this->dbname=$param['dbname']??'';
$this->charset=$param['charset']??'utf8';
}
//连接数据库
private function initConnect() {
$this->link=@mysqli_connect($this->host,$this->user,$this->pwd,$this->dbname);
if(mysqli_connect_error()){
echo '数据库连接失败<br>';
echo '错误信息:'.mysqli_connect_error(),'<br>';
echo '错误码:'.mysqli_connect_errno(),'<br>';
exit;
}
mysqli_set_charset($this->link,$this->charset);
}
//执行数据库的增、删、改、查
private function execute($sql) {
if(!$rs=mysqli_query($this->link,$sql)){
echo 'SQL语句执行失败<br>';
echo '错误信息:'.mysqli_error($this->link),'<br>';
echo '错误码:'.mysqli_errno($this->link),'<br>';
echo '错误的SQL语句:'.$sql,'<br>';
exit;
}
return $rs;
}
/**
*执行增、删、改
*@return bool 成功返回true,失败返回false
*/
public function exec($sql) {
$key=substr($sql,0,6);
if(in_array($key,array('insert','update','delete')))
return $this->execute($sql);
else{
echo '非法访问<br>';
exit;
}

}
//获取自动增长的编号
public function getLastInsertId() {
return mysqli_insert_id($this->link);
}
}

//测试
//配置参数
$param=array(
'user' => 'root',
'pwd' => 'root',
'dbname' => 'data'
);
//获取单例
$db=MySQLDB::getInstance($param);
//更新
//$db->exec("update news set title='青草' where id=2");
//插入
if($db->exec("insert into news values (null,'aa','bb',unix_timestamp())"))
echo '编号是:'.$db->getLastInsertId();

2、查询结果

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<?php
//封装MySQL单例
class MySQLDB {
private $host; //主机地址
private $port; //端口号
private $user; //用户名
private $pwd; //密码
private $dbname; //数据接名
private $charset; //字符集
private $link; //连接对象
private static $instance;
private function __construct($param) {
$this->initParam($param);
$this->initConnect();
}
private function __clone() {

}
//获取单例
public static function getInstance($param=array()) {
if(!self::$instance instanceof self)
self::$instance=new self($param);
return self::$instance;
}
//初始化参数
private function initParam($param) {
$this->host=$param['host']??'127.0.0.1';
$this->port=$param['port']??'3306';
$this->user=$param['user']??'';
$this->pwd=$param['pwd']??'';
$this->dbname=$param['dbname']??'';
$this->charset=$param['charset']??'utf8';
}
//连接数据库
private function initConnect() {
$this->link=@mysqli_connect($this->host,$this->user,$this->pwd,$this->dbname);
if(mysqli_connect_error()){
echo '数据库连接失败<br>';
echo '错误信息:'.mysqli_connect_error(),'<br>';
echo '错误码:'.mysqli_connect_errno(),'<br>';
exit;
}
mysqli_set_charset($this->link,$this->charset);
}
//执行数据库的增、删、改、查
private function execute($sql) {
if(!$rs=mysqli_query($this->link,$sql)){
echo 'SQL语句执行失败<br>';
echo '错误信息:'.mysqli_error($this->link),'<br>';
echo '错误码:'.mysqli_errno($this->link),'<br>';
echo '错误的SQL语句:'.$sql,'<br>';
exit;
}
return $rs;
}
/**
*执行增、删、改
*@return bool 成功返回true,失败返回false
*/
public function exec($sql) {
$key=substr($sql,0,6);
if(in_array($key,array('insert','update','delete')))
return $this->execute($sql);
else{
echo '非法访问<br>';
exit;
}

}
//获取自动增长的编号
public function getLastInsertId() {
return mysqli_insert_id($this->link);
}

//执行查询语句
private function query($sql) {
if(substr($sql,0,6)=='select' || substr($sql,0,4)=='show' || substr($sql,0,4)=='desc'){
return $this->execute($sql);
}else{
echo '非法访问<br>';
exit;
}
}
/**
*执行查询语句,返回二维数组
*@$sql string 查询sql语句
*@type string assoc|num|both
*/
public function fetchAll($sql,$type='assoc') {
$rs=$this->query($sql);
$type=$this->getType($type);
return mysqli_fetch_all($rs,$type);
}
//匹配一维数组
public function fetchRow($sql,$type='assoc') {
$list=$this->fetchAll($sql,$type);
if(!empty($list))
return $list[0];
return array();
}
//匹配一行一列
public function fetchColumn($sql) {
$list=$this->fetchRow($sql,'num');
if(!empty($list))
return $list[0];
return null;
}

//获取匹配类型
private function getType($type) {
switch($type){
case 'num':
return  MYSQLI_NUM;
case 'both':
return  MYSQLI_BOTH;
default:
return  MYSQLI_ASSOC;
}
}
}

//测试
//配置参数
$param=array(
'user' => 'root',
'pwd' => 'root',
'dbname' => 'data'
);
//获取单例
$db=MySQLDB::getInstance($param);
//更新
//$db->exec("update news set title='青草' where id=2");
//插入
/*
if($db->exec("insert into news values (null,'aa','bb',unix_timestamp())"))
echo '编号是:'.$db->getLastInsertId();
*/

//查询
//$list=$db->fetchAll('select * from news','aa');
//$list=$db->fetchRow('select * from news where id=1','aa');

$list=$db->fetchColumn('select count(*) from news');

echo '<pre>';
var_dump($list);

小结:

1、instanceof 用来判断对象是否属于某个类

2、参数必须从外部传递到内部,不能写死到类的内部。

3、为了保证代码的可重用性,一个方法只实现一个功能,所以初始化参数和连接数据库分到两个方法中。


本站由 RuntimeBroker 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本"页面"访问 次 | 👀总访问 次 | 总访客