标签搜索

PHP面向对象(3)

sunshine
2022-10-11 / 0 评论 / 10 阅读
温馨提示:
本文最后更新于2022年10月11日,已超过742天没有更新,若内容或图片失效,请留言反馈。

1.1 目标

  1. 掌握类的自动加载原理和实际应用;
  2. 了解对象的克隆意义;
  3. 掌握PHP重载的意义以及具体重载的应用;
  4. 了解对象的序列化和反序列化操作以及注意事项
  5. 掌握foreach对对象进行遍历;
  6. 掌握Mysqli的类封装以及应用;

1.2 自动加载类

在项目开发中,因为一个文件中只能写一个类,并且在执行过程中会有很多的类参与,如果一个一个的加载很麻烦,所以,就需要一个机制实现在PHP执行过程中自动加载需要的类。

1.2.1 类的规则

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

1.2.2 手动加载类

1、创建Goods.class.php页面

<?php
//商品类
abstract class Goods {
    protected $name;
    final public function setName($name) {
        $this->name=$name;    
    }
    public abstract function getName();
}

2、创建Book.class.php页面

<?php
//图书类
class Book extends Goods {
    public function getName() {
        echo "《{$this->name}》<br>";
    }
}

3、创建Phone.class.php页面

<?php
//电话类
class Phone extends Goods {
    public function getName() {
        echo $this->name,'<br>';
    }
}

4、在PHP页面上加载类文件

<?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();

运行结果

1560824628303

1.2.3 自动加载类

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

<?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.2.4 注册加载类

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

<?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()可以注册多个自动加载函数

<?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.2.5 类文件存储不规则的加载方法

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

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

代码如下

<?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();

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

1.3 clone和__clone()

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

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

例题

<?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()方法

1.4 设计模式

1.4.1 单例模式

一个类只能有一个对象

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

实现:三私一公

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

代码

<?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)  

1.4.2 工厂模式

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

<?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)  

1.4.3 策略模式

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

<?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());    //坐车去

1.5 序列化与反序列化

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

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

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

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

<?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 ) 

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

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

1560841768999

代码

<?php
class Student {
    public $name;
    protected $sex;
    private $add;
    public function __construct($name,$sex,$add) {
        $this->name=$name;
        $this->sex=$sex;
        $this->add=$add;
    }
}
/*
//测试
$stu=new Student('tom','男','北京');
//序列化
$str=serialize($stu);
file_put_contents('./stu.txt',$str);
*/

//反序列化,类的反序列化必须要有类的参与
$str=file_get_contents('./stu.txt');
$stu=unserialize($str);
echo '<pre>';
var_dump($stu);

运行结果

1560841835640

1.6 魔术方法

已经学习的魔术方法

__construct()
__destruct()
__clone()

1.6.1 __tostring()、__invoke()

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

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

<?php
class Student {
    //把对象当成字符串使用的时候自动执行
    public function __tostring() {
        return '这是一个对象,不是字符串<br>';
    }
    //把对象当成函数使用的时候自动执行
    public function __invoke() {
        echo '这是一个对象,不是函数<br>';
    }
}
$stu=new Student;
echo $stu;    //当成字符串使用
$stu();        //当成函数使用

1.6.2 __set()、__get()、__isset()、__unset()

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

例题

<?php
class Student {
    private $name;
    private $sex;
    private $age;
    //给无法访问的属性赋值的时候自动执行
    public function __set($k,$v) {
        $this->$k=$v;
    }
    //获取无法访问的属性值的时候自动调用
    public function __get($k) {
        return $this->$k;
    }
    //判断无法访问的属性是否存在自动调用
    public function __isset($k) {
        return isset($this->$k);
    }
    //销毁无法访问的属性的时候自动执行
    public function __unset($k) {
        unset($this->$k);
    }
}
//测试
$stu=new Student;
//1、给私有属性赋值
$stu->name='tom';
$stu->sex='男';
$stu->age=22;
//2、获取私有属性的值
//echo $stu->name;
//3、判断私有属性是否存在
//var_dump(isset($stu->name));
//4、销毁私有属性
unset($stu->age);
print_r($stu);

应用:设置读写属性

<?php
class Student {
    private $name;            //读写属性
    private $add='中国';    //只读属性
    private $age;            //只写属性
    
    public function __set($k,$v) {
        if(in_array($k,array('name','age')))
            $this->$k=$v;
        else
            echo "{$k}属性是只读属性<br>";
    }
    public function __get($k) {
        if(in_array($k,array('name','add')))
            return $this->$k;
        else
            echo "{$k}是只写属性<br>";
    }
}
//测试
$stu=new Student;
$stu->name='tom';
$stu->age=22;
echo '姓名:'.$stu->name,'<br>';
echo '地址:'.$stu->add,'<br>';

1.6.3 __call()、__callstatic()

__call():调用无法访问的方法时自动执行
__callstatic():调用无法访问的静态方法时自动执行

例题:

<?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();

1.6.4 __sleep()、__wakeup()

__sleep():当序列化的时候自动调用
__wakeup():当反序列化的时候自动调用

例题

<?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);

1.7 模拟方法重载

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

<?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);

1.8 遍历对象

通过foreach遍历对象

<?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();

1560846141700

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

1.9 封装MySQL的单例

1.8.1 分析

1、实现单例

2、连接数据库

3、对数据进行操作

1.8.2 步骤

第一步:实现单例

第二步:初始化参数

第三步:连接数据库

第四步:操作数据

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

2、执行数据查询语句

​ a) 返回二维数组

​ b) 返回一维数组

​ c)返回一行一列

1.8.3 代码实现

第一步:实现单例

<?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值

第二步:初始化参数

<?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);

第三步:连接数据库

<?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、执行增、删、改操作

<?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、查询结果

<?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、为了保证代码的可重用性,一个方法只实现一个功能,所以初始化参数和连接数据库分到两个方法中。

感觉很棒,欢迎点赞 OR 打赏~
0
分享到:

评论 (0)

取消