今天学习了自动加载类 抽象类 接口 接口案例 静态延迟绑定 接口连接数据库 命名空间。内容比较多,但多数为概念性内容,所以以代码展示为主:
一、自动加载类
这里用到了spl_autoload_register 里面调用匿名函数,可以让调用没有加载的类自动加载
<?php //当前脚本所在的目录 echo __DIR__ .'<br>'; ////分别引入inc下的三个类文件 //include __DIR__ . '/inc/Test1.php'; //include __DIR__ . '/inc/Test2.php'; //include __DIR__ . '/inc/Test3.php'; //这种方法比较初级 如果要成千上百的引入文件 那就很困难了 可以用如下函数 spl_autoload_register(function ($name) { return include __DIR__ . '/inc/' . $name . '.php'; }); //实例化三个类 分别显示出他的方法名 $test = new Test1(); echo $test->get().'<br>'; $test = new Test2(); echo $test->get().'<br>'; $test = new Test3(); echo $test->get().'<br>';
点击 "运行实例" 按钮查看在线实例
二、抽象类
在实际开发中,通常父类只是声明功能不需要实例化实现,这里需要引入抽象类,声明时在前面加上abstract
具有以下特点:
1、抽象类不能实例化
2、抽象类中必须有抽象方法,他的子类必须实现抽象方法
3、子类中的可见性必须高于父类抽象类
<?php //通常的类 class Person1 { protected $name; public function __construct($name = 'ji') { $this->name = $name; } public function getName () { return $this->name; } public function setName ($name) { return $this->name = $name; } } $person = new Person1(); echo $person->getName(),'<br>'; $person->setName('doudou'); echo $person->getName(), "<hr>"; //抽象类 //通常在实际开发中我们不会直接实例化一个父类 而是在父类中学一些方法声明 //这时候就需要引入抽象类 这些方法声明要确保在子类中重写 //抽象类就像项目经理 有些方法实现了 有些只是给出设计要求 让下面的成员来实现 //抽象类特点: //1不能实例化 //2 父类中的抽象方法 子类中必要实现 //3自类中的成员可见性 必须要高过父类 //4类中由抽象方法 其必须是抽象类 abstract class Person2 { protected $name; public function __construct($name = 'ji') { $this->name = $name; } public function getName () { return $this->name; } abstract protected function setName ($value); } class Stu extends Person2 { public function __construct(string $name = 'ji') { parent::__construct($name); } public function setName($value) { return $this->name = $value; } } $stu = new Stu(); $stu->setName('mingzi'); echo $stu->getName();
点击 "运行实例" 按钮查看在线实例
三、接口
接口相当于老板,他给项目经理(抽象类)提出客户的需求,他不需要亲自实现。不需要声明抽象类,它本身就是抽象的。
接口声明 前面加上interface 继承接口用implements
<?php //接口 它相当于老板 把客户需求告诉给项目经理(抽象类) 接口不用声明抽象类 因为它本身就是抽象 //里面的方法也是抽象的 interface iVehecle { //驱动方式 public function setFuel($fuel); //用途 public function setPurpose($purpose); } //用implements来继承接口 class Car implements iVehecle { public $fuel; public $purpose; //构造方法 public function __construct($fuel='汽油', $purpose='家用') { $this->fuel = $fuel; $this->purpose = $purpose; } //实现接口中的方法 public function setFuel($fuel) { return $this->fuel = $fuel; } public function setPurpose($purpose) { return $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(), '<hr>'; //如果暂时只能实现部分功能 可以先用抽象类来实现一部分 abstract class Auto implements iVehecle { public $fuel; public function setFuel($fuel) { $this->fuel = $fuel; } } //然后在通过子类继承抽象类来实现其余方法 class Car1 extends Auto { public $purpose; public function __construct($fuel='汽油', $purpose='家用') { $this->fuel = $fuel; $this->purpose = $purpose; } public function setPurpose($purpose) { return $this->purpose = $purpose; } public function getInfo1 () { return $this->fuel . $this->purpose .'车 <br>'; } } $car1 = new Car1(); echo $car1->getInfo1();
点击 "运行实例" 按钮查看在线实例
四、接口的案例
通过接口实现数据库的CURD操作,接口只是声明需要实现的功能,子类具体实现
<?php //接口的案例 数据库的CURD interface iCurd { //增加数据 public function create($date); //修改数据 public function update($date, $where); //查询数据 public function read(); //删除数据 public function delete($where); } //创建Db类实现curd方法 class Db implements iCurd { protected $pdo = null; protected $table; public function __construct($host, $username, $password, $table = 'staff') { $this->pdo = new PDO($host, $username, $password); $this->table = $table; } public function create($date) { $fields = '(name, age, sex, position, mobile, hiredate)'; $values = '(:name, :age, :sex, :position, :mobile, :hiredate)'; $sql = "INSERT INTO " . $this->table . $fields . ' VALUES ' .$values; $stmt = $this->pdo->prepare($sql); $stmt->execute($date); //返回新增数据的id 和 条数组成的数组 return [ 'count'=>$stmt->rowCount(), 'id'=>$this->pdo->lastInsertId() ]; } public function read($fields='*', $where='', $limit='0,5') { //where 设置筛选条件 $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 update($date, $where) { //将更新数据的关键字组成数组 $keyArr = array_keys($date); $set = ''; //遍历 然后组成说起来语句需要的格式 foreach ($keyArr as $value) { $set .= $value . ' = :' . $value . ', '; } $set = rtrim($set, ', '); $sql = "UPDATE " . $this->table . " SET " . $set . " WHERE " . $where; // echo $sql; $stmt = $this->pdo->prepare($sql); $stmt->execute($date); 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:dbname=php'; $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' => 51, 'position' => '抗金英雄' ]; $where = 'id = 29'; echo '成功更新了' . $db->update($data, $where) . '条记录'; echo '<hr>'; //删除数据 $where = 'id = 29'; echo '成功删除了' . $db->delete($where) . '条记录';
点击 "运行实例" 按钮查看在线实例
五、静态延迟绑定
简单来说用static就可以实现子类调用父类中的一个方法,返回的是调用的子类而不是父类。
<?php //下面这个例子 我期望在子类b调用test方法是返回的是类名b 但是如果在a的方法中是self::who的话 //那么就是A 为了实现我的期望就用static代替self 这就是静态延迟绑定技术 // class A { public static function who () { // 返回调用者类名 echo __CLASS__ ; } public static function test () { // self::who(); //换成static就可以实现静态延迟绑定技术 static::who(); } } class B extends A { public static function who() { echo __CLASS__; } } B::test(); echo "<br>"; /********************** 用简单的连接数据库,展示静态延迟绑定技术 *********************/ class Connection { public static function connect () { return static::config(); } public static function config () { return new PDO('mysql:dbname=php', '123', '123'); } } class Link extends Connection { public static function config() { return new PDO('mysql:dbname=php', 'root', 'root'); } } $pdo = Link::connect(); var_dump($pdo instanceof PDO); //$staffs = $pdo-> $staffs = $pdo->query('select * from staff limit 5'); foreach ($staffs as $staff) { print_r($staff); echo '<br>'; }
点击 "运行实例" 按钮查看在线实例
六、接口常量案例
用接口常量来实现数据库连接操作
<?php // 接口常量的应用 namespace Test; //echo __NAMESPACE__; if (!interface_exists(__NAMESPACE__ . '\iDbParam')) { interface iDbParam { const TYPE = 'mysql'; const HOST = 'localhost'; const USER = 'root'; const PSW = 'root'; const DBNAME = 'php'; public static function Connection (); } } class Connection implements iDbParam { private static $type = iDbParam::TYPE; private static $host = iDbParam::HOST; private static $user = iDbParam::USER; private static $psw = iDbParam::PSW; private static $dbname = iDbParam::DBNAME; public static function Connection() { $dsn = self::$type.':host='.self::$host.';dbname='.self::$dbname; $user = self::$user; $password = self::$psw; return new \PDO($dsn, $user, $password); } } $pdo = Connection::Connection(); $stmt = $pdo->prepare('SELECT * FROM staff LIMIT 5'); $stmt->execute(); $staffs = $stmt->fetchAll(\PDO::FETCH_ASSOC); foreach ($staffs as $staff) { echo '<pre>' . print_r($staff, true); }
点击 "运行实例" 按钮查看在线实例
分隔符
下面是命名空间的知识
一、命名空间解决了什么
解决了全局成员命名冲突的问题 命名空间的关键字namespace
<?php require "inc/function.php"; function func1 ($a, $b) { return $a .'+'. $b .'='. ($a + $b); } echo \func1(10, 20); echo '<hr>'; echo \my\func1(10, 20); //命名空间解决了什么问题 //命名空间可以解决全局命名冲突的问题 //全局成员是 类 函数 常量 他们不像变量一样受访问限制符的限制 全局都可用 //命名空间可以区别相同名称的全局成员 方便调用
点击 "运行实例" 按钮查看在线实例
二、命名空间的定义与访问
namespace关键字定义命名空间,如果访问其他命名空间的话就像访问文件目录那样:\根空间,访问其他空间就是\one\pig
<?php //定义命名空间 namespace one; class pig { static function hello () { return 'Hello 朱老师'; } const SITE = 'php中文网'; } echo pig::hello() . '<br>'; echo pig::SITE . '<hr>'; namespace two; class pig { static function hello () { return 'Hello 猪哥'; } const SITE = 'php.cn'; } echo pig::hello() . '<br>'; echo pig::SITE . '<hr>'; //如果想在当前命名空间访问其他命名空间的话就像文件目录那样访问 // \是根空间 先从根空间访问 然后在访问想要访问的空间 // \one\pig echo \one\pig::hello() . '<br>'; echo \one\pig::SITE . '<hr>';
点击 "运行实例" 按钮查看在线实例
三、同时定义多个命名空间
可以在命名空间后加{},像函数一样声明空间作用域,根空间用namespace{}来定义
<?php namespace one { class pig { static function hello () {return 'Hello 朱老师';} const SITE = 'php.cn'; } echo pig::class . '<br>'; //返回完整类名 echo pig::hello() . '<br>'; echo pig::SITE . '<hr>'; } namespace two { class pig { static function hello () {return 'Hello 猪哥';} const SITE = 'php中文网'; } echo pig::class . '<br>'; //返回完整类名 echo pig::hello() . '<br>'; echo pig::SITE . '<hr>'; // 访问one命名空间 echo \one\pig::class .'<br>'; echo \one\pig::hello() . '<br>'; echo \one\pig::SITE . '<hr>'; } //根空间 namespace 全局空间 namespace { class pig { static function hello () { return 'hello 灭绝师太'; } const SITE = '学习交流的平台'; } } //在其他空间访问全局空间里的成员 namespace three { echo \pig::class . '<br>'; echo \pig::hello() . '<br>'; echo \pig::SITE . '<hr>'; }
点击 "运行实例" 按钮查看在线实例
四、子命名空间
命名空间而可以像文件目录一样由子命名空间,如:\父\子
<?php //自命名空间 //命名空间是可以分层管理的 namespace think; class pig {}; echo pig::class . '<br>'; //魔术常量 之所以是魔术常量 就是说可以随着作用域的改变而改变 echo __NAMESPACE__ . '<hr>'; namespace think\admin; echo __NAMESPACE__ .'<br>'; class pig {}; echo pig::class . '<hr>'; //如果想要访问空间think\admin\model 下的pig类 //可以在当前空间(think\admin)用namespace代替 echo namespace\model\pig::class .'<hr>'; namespace think\admin\model; echo __NAMESPACE__ .'<br>'; class pig {}; echo pig::class . '<hr>'; // \是命名空间分隔符 将空间分层有什么用? /*在php框架中 将命名空间同文件的相对路径名一致的话可以实现自动加载 且不会出现命名重复的情况 因为都有命名空间*/
点击 "运行实例" 按钮查看在线实例
五、命名空间下的类文件自动加载技术
<?php /* include 'inc/Class1.php'; include 'inc/Class2.php'; $obj1 = new code1\inc\Class1(); $obj2 = new code1\inc\Class2(); echo get_class($obj1) . '<br>'; echo get_class($obj2) . '<br>'; echo '<hr>';*/ //由于类空间名和文件路径名一样 我们可以解析命名空间 然后实现自动加载文件 // echo __DIR__; $path = str_replace('\\', '/', 'code1\inc\Class1'); $path = __DIR__ . "/../" . $path . '.php'; echo $path; echo '<hr>'; spl_autoload_register(function ($class) { $path = str_replace('\\', DIRECTORY_SEPARATOR, $class); $path = __DIR__ . '/../' . $path . '.php'; //如果不是文件 或者 文件不存在 抛出异常 if (!is_file($path) && file_exists($path)) { throw new \Exception('不是文件或者文件不存在'); } require $path; }); $obj1 = new code1\inc\Class1(); $obj2 = new code1\inc\Class2(); echo get_class($obj1) . '<br>'; echo get_class($obj2) . '<br>'; echo '<hr>';
点击 "运行实例" 按钮查看在线实例
六、空间别名
空间别名解决空间分层过长问题,use关键字引入空间别名 as给空间起自定义的别名
空间别名可以不用根分隔符\,同时也可以声明时不用as 默认use 后的最后类名就是别名
<?php namespace current; include 'inc/Class1.php'; use \code1\inc\Class1 as C1; // $obj1 = new \code1\inc\Class1(); $obj1 = new C1(); echo get_class($obj1) . '<br>'; echo '<hr>'; //添加了命名空间的类名比较长 可以通过添加别名的形式来缩短 //可以使用 use \code1\inc\Class1 as C1 代替长长的名字 //系统默认就是从根空间开始所以开头的\可以去掉 //可以不用as 系统默认Class1就是别名 use code1\inc\Class1; $obj2 = new Class1(); echo get_class($obj2); echo '<hr>';
点击 "运行实例" 按钮查看在线实例
<?php namespace current; include 'inc/Class1.php'; use \code1\inc\Class1 as C1; // $obj1 = new \code1\inc\Class1(); $obj1 = new C1(); echo get_class($obj1) . '<br>'; echo '<hr>'; //添加了命名空间的类名比较长 可以通过添加别名的形式来缩短 //可以使用 use \code1\inc\Class1 as C1 代替长长的名字 //系统默认就是从根空间开始所以开头的\可以去掉 //可以不用as 系统默认Class1就是别名 use code1\inc\Class1; $obj2 = new Class1(); echo get_class($obj2); echo '<hr>';
点击 "运行实例" 按钮查看在线实例
七、命名空间案例
<?php namespace db; $db = new \PDO('mysql:dbname=php', 'root', 'root'); $stmt = $db->prepare('SELECT `id`, `name`, `position` FROM `staff` LIMIT :offset, :num'); $stmt->bindValue('offset', 0, \PDO::PARAM_INT); $stmt->bindValue('num', 5, \PDO::PARAM_INT); $stmt->execute(); $stmt->bindColumn('id',$id); $stmt->bindColumn('name',$name); $stmt->bindColumn('position',$position); while ($stmt->fetch(\PDO::FETCH_BOUND)) { echo "<li>{$id}.{$name}:{$position}</li>"; }
点击 "运行实例" 按钮查看在线实例
最后总结:
1、接口就是老板自带安排属性,就是说他不会亲自实现功能,只提需求,那么他本身就不需要声明抽象类及方法,里面的方法全部是抽象的;
2、抽象类就是项目经理,他会实现一部分功能,提出一部分需求,当然也有可能全部提需求,所以声明抽象类后,里面必须由抽象方法,他的子类就必须实现这个抽象方法
3、自动加载类技术,结合命名空间,(命名空间原则是和文件目录一致这样方便自动加载类),用spl_autoload_register 注册组建等方法实现
4、命名空间是下一步方法的基础,他避免了在框架中众多相同的名称的类 方法 的命名冲突 同时还可以起别名缩短长度