面向对象开发的规范
定义:面向对象规范是指我们在开发时,在技术没有强制规定的情况下,应该采用什么样的方式来协助我们进行代码的书写。
- 属性的初始化:属性是类对于同类事务所抽离出来的共性
数据,本身在类结构中没有价值,是当具体对象产生之后,属于对象本身的。因此在进行类中定义属性的时候,通常不会对属性进行初始化,除非属性本身的值也具有共性
<?phpclass Saler{//属性public $count;//某个卖家拥有的商品数量,每位具体卖家对象拥有的不可能一样,所以没必要初始化protected $discount;//某个卖家针对销售的折扣,同样没有统一的价值private $money = 0;//某个卖家的账户余额,任何一位卖家一开始做生意的时候,账户余额都为0,所以可以初始化?>
- 属性的初始化:属性在类中初始化的基本判定就是数据是否初始化的时候统一,如果数据统一(即初始化),那么所有对象在生成之后,就拥有对应的值;如果不初始化,意味着所有对象在生成后,应该对属性进行赋值。
<?phpclass Saler{//属性public $count;private $money = 0;}//实例化$s1 = new Saler();$s2 = new Saler(); //所有对象都有两个属性,其中$count都没有数据,而$money都为0//生成对象后,对象对自己的属性进行初始化$s1->count = 100;$s2->count = 1000;?>
- 访问修饰限定符选择:访问修饰限定符是用来限制类成员被对象访问时对象所处位置的。访问的权限从public、protected到private依次变小。使用规则如下:
- 设定好的类成员本身不会被外部用到,那么应该使用private或者protected
- 设定好的类成员一定会给外部访问,使用public
- 属性通常private居多,如果需要外部访问属性,通常会定义相关方法来实现属性的查看和修改,因为可以在方法内对数据逻辑进行代码控制,安全
- 总之一句话:尽可能增加类对成员的控制(尽可能多使用private,少使用public)
<?phpclass Saler{//属性public $count;private $money = 0;//增加方法操作私有属性moneypublic function getMoney(){return $this->money;}public function setMoney($money){//可以对逻辑进行修改,对数据进行安全判定,保证数据的安全性$this->money = $money;//$this->money是属性,$money是外部传入的参数,二者同名但是性质完全不同}}
总结
- 属性在类中定义的时候,通常不会初始化值,除非所有类实例化得到的对象的某个属性需要是统一值
- 应该尽可能增加类对成员的控制,即使用范围较小的访问修饰限定符优先
- 属性通常是私有化的,一般是通过设定方法来实现属性的访问和修改
构造方法
定义:构造方法__construct(),是一种类结构特有的特殊方法,该方法由系统规定好,开发人员在定义的时候只需要写抄一遍,有了构造方法的类在实例化对象之后,对象就会自动调用。
- 构造方法实现:在类中增加一个方法__construct()即可
<?phpclass Saler{//属性public $count;private $money;//构造方法public function __construct(){echo __CLASS__;}}
- 构造方法也是一个普通方法,不普通的地方在于,类实例化得到的对象会马上自动调用
<?php//接上述代码new Saler(); //输出Saler
- 构造方法的意义:构造方法是对象实例化的时候用来初始化对象的资源的,所以通常是用来初始化对象的属性或者其他资源初始化
<?phpclass Saler{//属性public $count;private $money;//构造方法:初始化属性public function __construct(){$this->count = 100;$this->money = 100;}}
- 如果属性的数据在构造方法中初始化是固定写死的,那么与直接在定义类的时候初始化属性一样。意味着数据没有任何价值(所有对象都相同),因此通常是通过构造方法的参数来实现数据的外部传入
<?phpclass Saler{//属性public $count;private $money;//构造方法:初始化属性public function __construct($count,$money){$this->count = $count;$this->money = $money;}}
- 一旦构造方法拥有了形参,那么对象在调用该方法的时候就需要传入对应的实参,而构造方法又是自动调用的,所以需要在实例化对象的时候使用new 类名(构造方法对应的实参列表)来实现
<?php//接上述代码$s1 = new Saler(100,100);$s2 = new Saler(1000,1000);
注意:之前所说的new 类名 和new 类名()没有区别是因为没有构造方法,或者构造方法没有参数限定,一旦构造方法有了参数,那么new 类名 就不能直接使用了。
- 构造方法不管再怎么特殊,也是用户定义的方法,言外之意除了在实例化对象时对象会自动调用之外,我们也可以手动调用构造方法(但是一般没有价值,因为对象实例化时会自动调用)
<?phpclass Saler{//属性public $count;private $money;//构造方法:初始化属性public function __construct($count,$money){$this->count = $count;$this->money = $money;}}//实例化$s = new Saler(100,100); //系统在new Saler(100,100)好之后,会自动调用一次$s->__construct(1000,1000); //允许手动调用
总结
- 构造方法__construct()是一种系统内置的方法,该方法的特性是会在对象实例化之后,对象立即自动调用
- 构造方法的目的就是为了初始化资源,包含对象属性和其他资源
- 一旦构造方法定义好之后,且构造方法自带参数,那么就只能使用
new 类名(参数列表)方式才能正确实例化 - 构造方法可以当做普通方法由对象调用(不建议)
析构方法
定义:析构方法__destruct(),也是一种类结构中特殊的方法,与构造方法一样,也是系统规定好,只需要开发人员一遍即可,对象在被销毁时会自动调用。
- 析构方法实现:类中增加一个__destruct()方法
<?phpclass Saler{//析构方法public function __destruct(){echo __FUNCTION__;}}
- 析构方法调用:析构方法是在对象被销毁时自动,对象的“垂死挣扎”
<?php//接上述代码//实例化$s = new Saler();unset($s); //删除对象
- 析构方法是对象用来调用释放对象中的资源,不是用来删除对象的
<?phpclass Saler{//析构方法public function __destruct(){//销毁对象所占用资源的代码}}
- 析构方法也是普通方法,可以由对象直接调用
<?php//接析构方法实现代码$s = new Saler();$s->__destruct(); //思考:此时对象是否被销毁?
- 对象销毁情形
- 保存对象的变量被用来保存其他数据,导致对象内存没有任何变量引用
- 删除对象变量
- 脚本执行结束:释放所有变量
总结
- 析构方法是一种对象销毁时自动调用的方法
- 析构方法是用来对象销毁自身所占用的资源
- PHP中脚本执行结束,系统会自动回收所有资源,因此一般PHP中很少使用析构方法
对象传值
定义:对象传值,其实就是将保存对象的变量赋值给另外一个变量,在PHP中,对象的传值是引用传递的:即一个对象变量赋值给另外一个变量,两个变量指向同一个对象的内存地址,即只有一个对象。
- 对象传值就是保存对象的变量赋值给另外一个变量
<?phpclass Saler{}$s1 = new Saler();$s2 = $s1;
- 对象传值是引用传递,不管对象赋值给多少个变量,内存中只有一个对象
<?phpclass Saler{}$s1 = new Saler();$s2 = $s1;//证明var_dump($s1,$s2); //同一个对象$s1->name = 'Saler'; //更改一个变量所保存对象的属性echo $s2->name; //输出Saler
总结:对象传值是引用传值,一般情况下不会在项目中用到。
范围解析操作符
定义:范围解析操作符,由两个冒号组成“::”,是专门用于类实现类成员操作的,可以实现类直接访问类成员。
- 类常量的普通访问尝试:尝试使用对象进行访问
<?phpclass Saler{//类常量const PI = 3.14;}$s1 = new Saler();echo $s1->PI; //错误,$s1->PI最终转换的访问方式为:$PI,这个在类中并不存在
- 以上案例可以看出,对象无法访问类常量,那是因为类常量的定义本身就是用来给类访问的,对象是用来访问属性和方法的,类常量的访问方式为:类名::常量名
<?phpclass Saler{//类常量const PI = 3.14;}echo Saler::PI; //输出3.14
- 分析:类常量是固定的,而对象的属性是不同对象而不同的,成员方法简单的理解也是为属性本身进行加工的。因此有一些东西是专属于类的,而有部分内容是专门为对象提供的,所以就会有不同的成员拥有不同的访问方式
总结
- 类访问成员的方式是使用范围解析操作符“::”访问,由类名直接访问:类名::类常量
- 类本身是通过对同类对象的抽象而形成,所以属性和方法本身都是由对象来访问
- 类也需要有一些自身的数据和操作,这些就由类来进行访问
静态成员
定义:静态成员,使用static关键字修饰的类成员,表示该成员属于类访问。PHP静态成员有两种,静态属性和静态方法。
- 静态属性:在类中定义属性的时候使用static关键字修饰,访问的时候只能使用类+范围解析操作符+静态属性访问
<?phpclass Saler{//属性public $money = 0;public static $count = 0; //静态属性}//静态成员可以直接使用类访问,而不需要先实例化对象echo Saler::$count;
- 静态方法:在勒种定义方法的时候使用static关键字修饰,访问的时候使用类+范围解析操作符+静态方法名字()访问
<?phpclass Saler{//方法public static function showClass(){echo __CLASS__;}}//类直接访问Saler::showClass();
- 在类的内部也可以访问静态成员,同样是使用类名+范围解析操作符+静态属性/静态方法()
<?phpclass Saler{//属性private static $count = 0; //私有,不允许外部直接访问//方法public static function showClass(){echo Saler::$count;}}//类直接访问Saler::showClass();
- 静态方法本质也是类中定义的方法,因此也可以使用对象进行访问,但是不建议
<?phpclass Saler{//属性private static $count = 0; //私有,不允许外部直接访问//方法public static function showClass(){echo Saler::$count;}}//对象访问静态方法$s = new Saler();$s->showClass(); //输出0
- 同理,方法也是在类内部,在编译时就存在,因此可以通过类来进行访问,使用范围解析操作符,但是非常不建议(会报错:因为类只允许访问静态成员和类常量)
<?phpclass Saler{public function testStatic(){echo __FUNCTION__;}}//类访问普通成员方法Saler::testStatic(); //输出testStatic,但是报错,当前访问的不是静态方法
- 静态方法本质是给类访问,所以不允许在静态方法内部使用$this对象
<?phpclass Saler{public static function testStaticThis(){var_dump($this); //致命错误:$this放到了不该放的位置}}
总结
- 为了保障类能直接访问数据和操作数据,可以在属性和方法前增加static关键字变成静态属性和静态方法
- 类通过类名+范围解析操作符+静态成员的方式进行访问
- 静态成员也收访问修饰限定符的限定,访问权限与普通属性和方法的限制一样
- 对象可以无条件访问静态方法,而类只能访问不带$this的普通方法(不建议)
- 静态成员是给类访问的,非静态成员是给对象访问的
- 静态成员的访问效率比非静态成员高,因此有种说法是能用静态的时候就不用非静态
self关键字
定义:self关键字是一种在类的内部(方法里面)使用,代替类名的写法。能够保障用户方便修改类名字。
- self是用来代替类名的,与范围解析操作符
::一起使用的
<?phpclass Saler{//属性private static $count = 0; //私有,不允许外部直接访问//方法public static function showClass(){echo Saler::$count;echo self::$count; //代替类名}}
- self也可以在类的内部方便实例化对象:比如构造方法被私有化之后,就没有办法在类外部实例化对象,此时可以在类内部进行对象实例化
<?phpclass Saler{//属性private static $count = 0; //私有,不允许外部直接访问private function __construct(){} //私有,不允许外部实例化(因为对象不能外部调用)//方法public static function getInstance(){return new Saler(); //使用类名实例化return new self(); //使用self关键字实例化}}$s = Saler::getInstance();
总结
- self是一种在类内部用来代替类名的关键字
- self可以用来在类内部访问静态成员(类常量也可以)
- self也可以在类内部用来实例化对象(代替类名:new self())
类的加载
定义:所谓类的加载,本质是因为类的访问必须保证类在内存中已经存在,所以需要在用类之前将类所在的PHP文件加载到内存。
- 手动加载:即要访问某个类之前,使用文件包含将类所在的文件加载进来
类文件:Saler.php<?phpclass Saler{}?>应用文件:useSaler.php<?php//使用Saler类需要先包含Saler类所在的文件include_once 'Saler.php'; //通常使用include_once,因为类不允许重名$s = new Saler();?>
- 加载类文件是一种比较消耗资源的方式,所以有的时候不确定类是否在内存中存在,可以事先使用class_exists()函数来判定是否存在,存在就不用加载,不存在才加载
<?php//使用Saler类,但是不确定内存中是否存在if(!class_exists('Saler')){//不存在:加载include_once 'Saler.php';}//使用$s = new Saler();?>
- 自动加载:PHP没有那么智能的系统自动加载,所谓自动加载只是PHP提供了一种
加载机制:即实现定义一个函数autoload(),然后当系统需要使用类,而内存中又不存在的时候,系统就会自动调用autoload()函数来加载类文件.
<?php//自动加载机制:利用系统提供的__autoload()函数function __autoload($classname){ //参数为类名:即当前需要访问的类的名字//需要人为定义去哪加载,怎么加载include_once $classname . '.php'; //假定为当前目录下,类文件名字为:类名.php}//使用类:内存目前并没有$s = new Saler(); //系统发现内存没有Saler,所以调用__autoload()去加载?>
- 一个系统里,可能类文件会放到不同的路径下,因此一个完整的自动加载函数,应该要进行文件判定以及加载功能
<?php//定义自动加载function __autoload($classname){//组织文件路径:假设当前路径下,有两个文件夹下都有类c和m$c_file = 'c/' . $classname . '.php'; //如c/Saler.phpif(file_exists($c_file)){include_once $c_file;}else{//说明c文件夹没有对应的文件$m_file = 'm/' . $classname . '.php'; //如m/Saler.phpif(file_exists($m_file)){include_once $m_file;}}}?>
注意:自动加载是指按照开发者规定的路径去寻找对应的文件,并实现包含。如果文件不存在,那么系统会在使用类的时候报错,因为这是开发者自己犯的错,系统不能规避。
- 随着PHP版本的提升,在7以后,不怎么建议直接使用autoload()函数,而是采用一种注册机制,将用户自定义的函数,放到系统内部,使用spl_autoload_register(定义好的函数)。本质与autoload()一样
<?php//定义一个函数,用来加载类文件function my_autoload($classname){ //也需要一个参数来接收要加载的类名字//功能与__autoload()一样$c_file = 'c/' . $classname . '.php'; //如c/Saler.phpif(file_exists($c_file)){include_once $c_file;}else{//说明c文件夹没有对应的文件$m_file = 'm/' . $classname . '.php'; //如m/Saler.phpif(file_exists($m_file)){include_once $m_file;}}}//此时,上述函数永远不会自动运行,除非将函数注册到系统内部spl_autoload_register('my_autoload');?>
注意:该方式其实本质就是通过两步完成了__autoload()一步的操作,但是spl_autoload_register()函数可以注册多个自定义的加载函数,更方便管理。
<?phpfunction c_autoload($classname){$c_file = 'c/' . $classname . '.php';if(file_exists($c_file)){include_once $c_file;}}function m_autoload($classname){$m_file = 'm/' . $classname . '.php';if(file_exists($m_file)){include_once $m_file;}}//全部注册spl_autoload_register('c_autoload'); //先尝试调用第一个自定义加载函数:找到了结束;找不到找第二个函数spl_autoload_register('m_autoload');?>
总结
- 类的使用必须先保证内存中该类存在
- 可以使用手动加载来确保类的使用安全:优点是明确,缺点是繁琐(类文件名字可以随意没有规范)
- 可以使用自动加载来让系统按照开发者设定的路径和方式去寻找类,并尝试加载到内存(尽量让类文件名字统一,保证类名和文件名有关联)
- 自动加载可以使用__autoload()函数来实现,也可以使用自定义函数+spl_autoload_register()注册共同实现(后者推荐)
- 基本上所有的框架都在使用自动加载机制
对象克隆
定义:克隆对象clone,即通过已有的对象复制一个新的同样的对象,但是两者之间并非同一个对象。
- 对象克隆是通过clone关键字实现,即:clone 对象;
<?phpclass Saler{//属性public $count;private $money;}//实例化$s1 = new Saler();$s1->count = 1;//克隆$s2 = clone $s1;?>
- 克隆出来的对象与原来对象是两个内存地址,因此是两个不同的对象
<?php//接上述代码$s2->count = 2;echo $s1->count; //1,没有变化?>
- 对象在实例化的时候会自动调用存在的构造方法construct(),同样的,在类的内部,PHP允许定义一个clone()的方法,在对象被克隆后,新克隆出来的对象会自动调用
<?phpclass Saler{//属性public $count;private $money;//克隆方法public function __clone(){var_dump($this); //编号为2,代表是克隆出来的对象$this->count++;}}//实例化$s1 = new Saler();$s1->count = 1;//克隆$s2 = clone $s1;?>
- 如果不允许对象被克隆,可以将__clone()方法私有化(本质是不允许对象在外部被克隆)
<?phpclass Saler{//属性public $count;private $money;//私有化克隆方法private function __clone(){}}//实例化$s1 = new Saler();$s1->count = 1;//克隆$s2 = clone $s1; //致命错误:不允许对象在外部访问一个私有方法?>
总结
- 对象可以通过克隆来得到新的对象(以前只有实例化)
- 克隆出来的对象会自动调用类中对应的__clone()方法(如果有)
- 可以通过私有化克隆方法来实现禁止外部对象克隆
封装数据库操作类
定义:封装数据库操作类,即根据数据库的操作需求,来确认数据库操作类该有什么样的功能,以及这些功能该如何实现。
- 一个类通常就是一个文件,所以要先确定文件的名字:通常类文件命名规范有两种
- 文件名字与类名字一样,如Sql.php
- 为了区分普通PHP文件,增加中间类描述,如Sql.class.php
- 现在PHP几乎都是面向对象编程,所以通常采用第一种方式:因此当前命名数据类的文件为:Sql.php
- 确定类文件名字后其实也就确定了类名字,因此可以创建一个Sql类
<?php//数据库操作类class Sql{}?>
- 类的创建分两种:一是特定使用,即类里面的所有内容只为某次使用;二是通用,即工具类,以后很多地方可以用。
- 特定使用,功能可以不用太灵活
- 通用工具,功能应该大众化,数据的变化会比较多
数据库类以后凡是要操作数据库的地方都可以用得到,很多项目都会用到,所以应该是个通用工具类,因此要考虑其到处可用的特性,让其能够灵活
- 数据库的操作最基本的特性不会改变:即需要连接认证,而连接认证的信息是灵活的,所以可以通过设定属性来控制,这些信息也都是不同使用者不同的,应该可以改变,所以可以通过构造方法来实现数据传入
<?php//数据库操作类class Sql{//设置属性public $host;public $port;public $user;public $pass;public $dbname;public $charset;//构造方法初始化数据:数据较多,应该使用数组来传递数据,关联数组,而且绝大部分的开发者本意是用来测试,所以基本都是本地,因此可以给默认数据/*$info = array('host' => 'localhost','port' => '3306','user' => 'root','pass' => 'root','dbname' => 'test','charset' => 'utf8')*/public function __construct(array $info = array()){//初始化:确保用户传入了数据,否则使用默认值$this->host = isset($info['host']) ? $info['host'] : 'localhost';$this->port = isset($info['port']) ? $info['port'] : '3306';$this->user = isset($info['user']) ? $info['user'] : 'root';$this->pass = isset($info['pass']) ? $info['pass'] : 'root';$this->dbname = isset($info['dbname']) ? $info['dbname'] : 'test';$this->charset = isset($info['charset']) ? $info['charset'] : 'utf8';}}?>
注意:方法设定的原则是一个方法只实现一个简单的功能,不要多个功能堆积到一个方法中。
- 数据库属性会在实例化Sql对象的时候自动初始化
<?php//接上述代码(类外测试)$s1 = new Sql(); //使用默认数据库信息$db = array('host' => 'localhost','user' => 'root','pass' => 'root','dbname' => 'my_database');$s2 = new Sql($db); //使用外部数据库信息?>
- 数据库要操作的第一件事就是连接认证,所以需要一个连接认证的功能。这里可以使用mysqli面向对象的方法。但是需要建立一个方法来实现连接认证:连接是否成功?
//在上述类中增加一个方法:实现连接认证功能public function sql_connect(){//利用属性可以跨方法访问:5个参数分别为:主机、用户名、密码、数据库、端口//利用错误抑制符抑制可能出现的错误:如找不到数据库之类$link = @new mysqli($this->host,$this->user,$this->pass,$this->dbname,$this->port);//判定连接是否成功if($link->connect_error){//mysqli对象有两个属性:connect_errno表示错误编号,connect_error表示错误信息:发生错误后终止脚本执行die( 'Connect Error (' . $link -> connect_errno . ') ' . $link -> connect_error );}}
- 用户调用Sql类的目的一定是为了操作数据库,那么用户在实例化之后就需要调用连接认证的方法。为了方便用户操作,可以帮助用户省去调用这一步骤:在构造方法中调用该方法
//在上述构造方法中调用连接认证方法public function __construct(array $info = array()){//在属性初始化之后调用连接认证方法$this->sql_connnect();}
- 至此,一旦实例化Sql类对象,就可以实现数据库的连接,但是此时还存在一个细节问题:字符集,为了保证数据库连接的正常操作,需要新增一个方法设定字符集
//在Sql类中增加设定字符集的方法public function sql_charset(){$sql = "set names {$this->charset}"; //组织SQL指令//此时需要调用mysqli连接数据库得到的对象:发现$link在sql_connect方法中是一个局部变量,不能跨方法}
- 由于sql_connect方法中得到的连接对象mysqli的
link提升为整个类的内部可用:提升为属性
//在Sql类中增加属性,保存对象public $link;//修改sql_connect方法,使用属性保存连接对象public function sql_connect(){$this->link = @new mysqli($this->host,$this->user,$this->pass,$this->dbname,$this->port);//判定连接是否成功if($link->connect_error){//注意:连接资源保存在$this->link中,所以访问mysqli对象成员时需要$this->link->属性/方法()die('Connect Error (' . $this->link->connect_errno . ') ' . $this->link->connect_error);}}
- 继续完成设定字符集的功能:使用属性保存的mysqli连接对象
//在Sql类中完善设定字符集的方法public function sql_charset(){$sql = "set names {$this->charset}"; //组织SQL指令//调用mysqli::query$this->link->query($sql);}
- 设置字符集的SQL指令虽然简单,但是因为有数据来源于外部,所以存在出错的风险,因此需要进行SQL错误判定
//在Sql类中完善设定字符集的方法public function sql_charset(){$sql = "set names {$this->charset}"; //组织SQL指令$res = $this->link->query($sql);if(!$res){//失败发挥falsedie('Charset Error(' . $this->link->errno . ') ' . $this->link->error);}}
- 同样的,字符集的设置也是为了操作数据库的前置条件,属于初始化的一部分。因此需要字符集设置的方法在构造方法中调用,方便用户操作
//在Sql类的构造方法中调用字符集设置方法public function __construct(array $info = array()){//在连接数据库方法调用之后调用设置字符集方法$this->sql_charset();}
- 至此:数据库的初始化操作已经完成,此时要考虑的事情是用户调用数据库类是为了干什么?为了执行SQL指令,也就是增删改查。在mysqli中所有的SQL执行都是通过mysqli::query()方法执行,但是我们可以根据需求封装两个函数:写方法和查方法(包含一条和多条查询)
//在Sql类中增加一个写方法:SQL指令来自于调用处提供public function sql_exec($sql){//当前不知道SQL指令是什么样的,只是负责执行,所以由外部提供$res = $this->link->query($sql);//判断SQL有没有语法错误if(!$res){die('Sql Error(' . $this->link->errno . ') ' . $this->link->error);}//返回执行结果return $res; //本质就是一个true}//在Sql类中增加一个读方法:Sql指令来自于调用处提供,同时设定参数供用户选择一条或全部结果public function sql_query($sql,$all = false){//$all代表是否获取多条记录,默认false只获取一条记录$res = $this->link->query($sql);//判断SQL有没有语法错误if(!$res){die('Sql Error(' . $this->link->errno . ') ' . $this->link->error);}//解析其中的结果:根据用户需求获取一条或者多条记录if($all){//获取全部:mysqli_result::fetch_all(MYSQLI_ASSOC)表示返回关联数组(默认是索引)return $res->fetch_all(MYSQLI_ASSOC);}else{//获取一条:mysqli_result::fetch_assoc()return $res->fetch_assoc();}}
- 上述已经完成了数据库类要实现的基本功能:实现SQL指令的执行和结果返回,但是从功能细节的角度出发还需要进行完善:插入操作后要获取自增长id,更新和删除操作受影响的行数,查询操作中记录数量。这种使用可以通过设置方法来实现获取(自增长id),也可以通过增加属性来实现(属性简单)
增加属性:受影响的行数,自增长id,查询记录数
//在Sql类中追加属性public $affected_rows; //受影响行数(上次操作)public $num_rows; //查询结果记录数(上次操作)
在写操作sql_exec中,为受影响行数赋值
//修改sql_exec方法public function sql_exec($sql){$res = $this->link->query($sql);//判断SQL有没有语法错误if(!$res){die('Sql Error(' . $this->link->errno . ') ' . $this->link->error);}//成功保存受影响的行数:数据在mysqli对象中$this->affected_rows = $this->link->affected_rows;//返回执行结果return $res; //本质就是一个true}
在读操作中,为记录数
//修改sql_query方法public function sql_query($sql,$all = false){$res = $this->link->query($sql);//判断SQL有没有语法错误if(!$res){die('Sql Error(' . $this->link->errno . ') ' . $this->link->error);}//获取记录信息:数据在mysqli_result对象中$this->num_rows = $res->num_rows;//解析其中的结果:根据用户需求获取一条或者多条记录if($all){//获取全部:mysqli_result::fetch_all(MYSQLI_ASSOC)表示返回关联数组(默认是索引)return $res->fetch_all(MYSQLI_ASSOC);}else{//获取一条:mysqli_result::fetch_assoc()return $res->fetch_assoc();}}
增加一个方法专门获取上次插入数据的自增长ID(因为这个是人为区分,不方便放到sql_exec中)
//在Sql类中增加一个方法获取上一次自增长操作idpublic function sql_insert_id(){//insertid是在mysqli对象执行query方法时获得return $this->link->insert_id;}
- 至此:数据库类的功能已经实现,接下来要考虑类的定义规范:类对成员的控制性
- 属性如果不需要给外部访问,私有
- 方法如果只是内部调用,私有
- 测试:利用数据库类实现数据库的写操作和读操作
总结
- 类的封装是以功能驱动为前提,相关操作存放到一个类中
- 一个类通常是一个独立的文件,文件名与类名相同(方便后期维护和自动加载)
- 类中如果有数据需要管理,设定属性(固定数据可以使用类常量)
- 类中如果有功能需要实现(数据加工),设定方法
- 一个功能通常使用一个方法实现,方法的颗粒度应该尽可能小(方便复用)
- 应该尽可能增加类对成员的控制:即能私有尽可能私有
