PHP面向对象-3-加载类&设计模式&魔术方法&序列化
1 自动加载类
1.1 类的规则
- 一个文件中只能放一个类(必须)
- 文件名和类名同名(必须)
- 类文件以.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
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
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 3 4 5 6 7 8 9 10
| <?php class Student { public function __clone() { echo '正在克隆对象<br>'; } } $stu1=new Student; $stu2=clone $stu1; var_dump($stu1,$stu2);
|
小结:
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() {
} private function __clone() {
} public static function getInstance() { if(!self::$instance instanceof self) self::$instance=new self(); return self::$instance; } }
$db1=DB::getInstance(); $db2=DB::getInstance(); var_dump($db1,$db2);
|
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);
|
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
$str=file_get_contents('./stu.txt'); $stu=unserialize($str); print_r($stu);
|
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 {
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; }
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
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
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
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; }
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);
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
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; }
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; } }
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);
$list=$db->fetchColumn('select count(*) from news');
echo '<pre>'; var_dump($list);
|
小结:
1、instanceof 用来判断对象是否属于某个类
2、参数必须从外部传递到内部,不能写死到类的内部。
3、为了保证代码的可重用性,一个方法只实现一个功能,所以初始化参数和连接数据库分到两个方法中。