一、PHP常用的四种数据结构
简介:spl是php的一个标准库。
官方文档:http://php.net/manual/zh/book.spl.php
push('data1');//入栈(先进后出) $stack->push('data2');//入栈 $stack->push('data3');//入栈 echo $stack->pop();//出栈 echo $stack->pop();//出栈 echo $stack->pop();//出栈 /** *队列(先进先出) */ $queue = new SplQueue(); $queue->enqueue('data4');//入队列 $queue->enqueue('data5');//入队列 $queue->enqueue('data6');//入队列 echo $queue->dequeue();//出队列 echo $queue->dequeue();//出队列 echo $queue->dequeue();//出队列 echo $queue->dequeue();//出队列 /** * 堆 */ $heap = new SplMinHeap(); $heap->insert('data8');//入堆 $heap->insert('data9');//入堆 $heap->insert('data10');//入堆 echo $heap->extract();//从堆中提取数据 echo $heap->extract();//从堆中提取数据 echo $heap->extract();//从堆中提取数据 /** * 固定数组(不论使不使用,都会分配相应的内存空间) */ $array = new SplFixedArray(15); $array['0'] = 54; $array['6'] = 69; $array['10'] = 32; var_dump($array);
1、入口文件 index.php
where('uid < 100000')->->order('uid desc')->limit(100);
2、自动加载类 Loader.php
Nach dem Login kopieren
3、数据库类Database.php
注:只是原理,并没有对方法进行具体的封装,具体的封装还是看个人喜好去定链式查询的风格。
Nach dem Login kopieren
其实就是对传过来的条件进行重新的底层封装,然后再把当前对象返回,使得可以不断的链式查询。
在php设计模式中,会涉及到很多魔术方法的使用,这里也对经常会用到的魔术方法进行简单总结。
1、框架入口文件 index.php
title = 'xiaobudiu'; //当对一个不存在的类属性赋值时,会自动调用类中定义的__set() echo $obj->title; //当调用一个不存在的类属性时,会自动调用类中定义的__get() # __call 和 __callStatic 对不存在或者权限不够的类方法进行接管 $obj->getUserInfo('1000068'); //当调用一个不存在的类方法时,会调用__call(),并自动将当前方法名和参数传到__call方法中 \Extend\Object::getOpenId('1000068'); //当调用一个不存在的类静态方法时,会调用__callStatic(),并自动将当前方法名和参数传递到__callStatic方法中 # echo或print对象时,由__toString 接管 echo $obj; //当echo或print一个对象时,会自动调用类中定义的__toString方法 # 在php中,如果我们把一个对象当成函数用,则由__invoke()接管 $obj('xiaobudiu');//当我们将一个对象当成函数用的时候,会自动调用当前类中定义的__invoke()方法
2、 Extend/Object.php
this is __invoke func"; } }
1、工厂模式
通过传入参数的不同,来实例化不同的类。
index.php
'127.0.0.1', 'pass' => 'myRedis&&&' ); //工厂模式创建cache对象 $cache = Extend\CacheFactory::getCacheObj('redis',$config); var_dump($cache);
Extend/CacheFactory.php
Nach dem Login kopieren
2、单例模式
保证一个类只实例化一个类对象,进而减少系统开销和资源的浪费
index.php
Nach dem Login kopieren
Extend/SingleObject.php
Nach dem Login kopieren
3、注册树模式
将我们用到的对象注册到注册树上,然后在之后要用到这个对象的时候,直接从注册树上取下来就好。(就和我们用全局变量一样方便)
Extend/RegisterTree,php
Nach dem Login kopieren
1、适配器模式
将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本的由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库。
常见的有两种适配器,分别是类适配器和对象适配器,这里拿更看好的对象适配器举例:
"; } } /** * 类适配器角色(新定义接口的具体实现) * Class Adapter * @package Extend */ class Adapter implements Target { private $adaptee; function __construct() { //适配器初始化直接new 原功能类,以方便之后委派 $adaptee = new Adaptee(); $this->adaptee = $adaptee; } //委派调用Adaptee的sampleMethod1方法 public function simpleMethod1() { echo $this->adaptee->simpleMethod1(); } public function simpleMethod2() { echo 'Adapter simpleMethod2'."
"; } } /** * 客户端调用 */ $adapter = new Adapter(); $adapter->simpleMethod1(); $adapter->simpleMethod2();
2、策略模式
将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,这种模式就是策略模式,策略模式可以实现依赖倒置以及控制反转。
实例举例:假如一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示展示不同的广告。
index.php
userType->showAd(); echo "Category:"; $this->userType->showCategory(); } /** * 策略模式 * 根据传递的用户性别展示不同类别数据 * @param \Extend\UserType $userType */ function setUserType(\Extend\UserType $userType) { $this->userType = $userType; } } $obj = new Home(); if ($_GET['userType'] == 'female'){ $userType = new \Extend\FemaleUserType(); } else { $userType = new \Extend\MaleUserType(); } $obj->setUserType($userType); $obj->index();
Extend/userType.php(定义的接口)
Nach dem Login kopieren
MaleUserType.php、FemaleUserType.php(具体实现的类 )
Nach dem Login kopieren
Nach dem Login kopieren
显示效果:
3、数据对象映射模式
将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作。
下面在代码中实现数据对象映射模式,我们将实现一个ORM类,将复杂的sql语句映射成对象属性的操作。并结合使用数据对象映射模式、工厂模式、注册模式。
-----(1)数据库映射模式简单实例实现
index.php
name = '小卜丢饭团子'; $user->salary = '20000'; $user->city = '浙江省';
Extend/User.php
id = $id; $this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456'); } function __destruct() { $this->pdo->query("update user set name = '{$this->name}',salary = '{$this->salary}',city = '{$this->city}' where id='{$this->id}'"); } }
这样,执行index.php文件,数据库就会发生相应的操作,也就实现了基本的数据对象映射。
-------(2)数据库映射模式复杂案例实现
index.php
name = '小卜丢饭团子'; $user->salary = '20000'; $user->city = '浙江省'; } function test() { $user = Extend\Factory::getUserObj(25); $user->city = '广东省'; } } $ex = new EX(); $ex->index();
Extend/Factory.php
Nach dem Login kopieren
Extend/Register.php
Nach dem Login kopieren
Extend/User.php
id = $id; $this->pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456'); } function __destruct() { $this->pdo->query("update user set name = '{$this->name}',salary = '{$this->salary}',city = '{$this->city}' where id='{$this->id}'"); } }
这样,就实现了稍复杂的数据对象映射模式和工厂模式、注册树模式相结合的案例。
4、观察者模式
当一个对象状态发生改变时,依赖它的对象会全部收到通知,并自动更新。
场景:一个事件发生后,要执行一连串更新操作。传统的编程方式就是在事件的代码之后直接加入处理逻辑,当更新的逻辑增多之后,代码会变的难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码。观察者模式实现了低耦合,非侵入式的通知与更新机制。
4.1、传统模式举例:
"; //传统方式是在发生一个事件之后直接进行一系列的相关处理,耦合度比较高,比如写入日志,给用户发邮件等等 echo "在用户下单之后进行的一系列操作
"; } } $event = new Event(); $event->firmOrder();
4.2、观察者模式典型实现方式:
(1)定义2个接口:观察者(通知)接口、被观察者(主题)接口
(2)定义2个类,观察者类实现观察者接口、被观察者类实现被观察者接口
(3)被观察者注册自己需要通知的观察者
(4)被观察者类某个业务逻辑发生时,通知观察者对象,进而每个观察者执行自己的业务逻辑。
代码示例:
test.php
observers[] = $observer; } /** * 购票主体方法 * BuyTicket constructor. * @param $ticket 购票排号 */ public function buyTicket($ticket) { //1、根据需求写购票逻辑 //.............. //2、购票成功之后,循环通知观察者,并调用其buyTicketOver实现不同业务逻辑 foreach ($this->observers as $observe) { $observe->buyTicketOver($this, $ticket); //$this 可用来获取主题类句柄,在通知中使用 } } } /** * 购票成功后,发送短信通知 * Class buyTicketMSN */ class buyTicketMSN implements TicketObserver { public function buyTicketOver($sender, $ticket) { echo (date ( 'Y-m-d H:i:s' ) . " 短信日志记录:购票成功:$ticket
"); } } /** * 购票成功后,记录日志 * Class buyTicketLog */ class buyTicketLog implements TicketObserver { public function buyTicketOver($sender, $ticket) { echo (date ( 'Y-m-d H:i:s' ) . " 文本日志记录:购票成功:$ticket
"); } } /** * 购票成功后,赠送优惠券 * Class buyTicketCoupon */ class buyTicketCoupon implements TicketObserver { public function buyTicketOver($sender, $ticket) { echo (date ( 'Y-m-d H:i:s' ) . " 赠送优惠券:购票成功:$ticket 赠送10元优惠券1张。
"); } } //实例化购票类 $buy = new BuyTicket(); //添加多个观察者 $buy->addObserver(new buyTicketMSN()); $buy->addObserver(new buyTicketLog()); $buy->addObserver(new buyTicketCoupon()); //开始购票 $buy->buyTicket ("7排8号");
浏览器显示结果:
5、原型模式
原型模式与工厂模式的作用类似,都是用来创建对象的。但是实现方式是不同的。原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。这样,就免去了类创建时重复的初始化操作。
原型模式适用于大对象的创建,创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需内存拷贝即可。
代码实例:
_name = $name; } public function setName($name) { $this->_name = $name; } public function getName() { return $this->_name; } public function copy() { //深拷贝实现 //$serialize_obj = serialize($this); // 序列化 //$clone_obj = unserialize($serialize_obj); // 反序列化 //return $clone_obj; // 浅拷贝实现 return clone $this; } } /** * 测试深拷贝用的引用类 */ class Demo { public $array; } //测试 $demo = new Demo(); $demo->array = array(1, 2); $object1 = new ConcretePrototype($demo); $object2 = $object1->copy(); var_dump($object1->getName()); echo '
'; var_dump($object2->getName()); echo '
'; $demo->array = array(3, 4); var_dump($object1->getName()); echo '
'; var_dump($object2->getName()); echo '
';
浏览器显示结果:
6、装饰器模式
可以动态的添加或修改类的功能
一个类实现一个功能,如果要再修改或添加额外的功能,传统的编程模式需要写一个子类继承它,并重新实现类的方法。
使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性。
color = $color; } public function beforeEcho() { echo "color}'>"; } public function afterEcho() { echo ""; } } /** * 字体大小装饰器实现 * Class SizeDecorator */ class SizeDecorator implements Decorator { protected $size; public function __construct($size) { $this->size = $size; } public function beforeEcho() { echo " size}px'>"; } public function afterEcho() { echo ""; } } /** * 被装饰者 * 输出一个字符串 * 装饰器动态添加功能 * Class EchoText */ class EchoText { protected $decorators = array();//存放装饰器 //装饰方法 public function Index() { //调用装饰器前置操作 $this->beforeEcho(); echo "你好,我是装饰器。"; //调用装饰器后置操作 $this->afterEcho(); } //添加装饰器 public function addDecorator(Decorator $decorator) { $this->decorators[] = $decorator; } //执行装饰器前置操作 先进先出原则 protected function beforeEcho() { foreach ($this->decorators as $decorator) $decorator->beforeEcho(); } //执行装饰器后置操作 先进后出原则 protected function afterEcho() { $tmp = array_reverse($this->decorators); foreach ($tmp as $decorator) $decorator->afterEcho(); } } //实例化输出类 $echo = new EchoText(); //增加装饰器 $echo->addDecorator(new ColorDecorator('yellow')); //增加装饰器 $echo->addDecorator(new SizeDecorator('22')); //输出 $echo->Index();
7、迭代器模式
在不需要了解内部实现的前提下,遍历一个聚合对象的内部元素而又不暴露该对象的内部表示,这就是PHP迭代器模式的定义。
相对于传统编程模式,迭代器模式可以隐藏遍历元素的所需的操作。
index.php
Nach dem Login kopieren
Extend/AllUser.php
pdo = new \PDO('mysql:host=127.0.0.1;dbname=test','root','123456'); //获取所有用户的id $this->ids = $this->pdo->query("select id from user")->fetchAll(2); } /** * 实现接口方法,重置迭代器,回到集合开头 */ public function rewind() { $this->index = 0; } /** * 实现接口方法,获取当前元素 * @return mixed|void */ public function current() { $id = $this->ids[$this->index]['id']; //获取当前用户的数据 $user_data = $this->pdo->query("select * from user where id='{$id}'")->fetch(2); return $user_data; } /** * 实现接口方法,获取当前元素键值 * @return mixed|void */ public function key() { return $this->index; } /** * 实现接口方法,获取下一个元素 */ public function next() { $this->index++; } /** * 实现接口方法,验证是否还有下一个元素 * @return bool|void */ public function valid() { return $this->index < count($this->ids); } }
8、代理模式
在客户端与实体之间建立一个代理对象(proxy),客户端对实体进行操作全部委派给代理对象,隐藏实体的具体实现细节。
典型的应用就是mysql的主从结构,读写分离。在mysql中,对所有读的操作请求从库,所有写的操作请求主库。
声明一个代理类,前台使用时只需创建一个代理类,调用对应方法即可。代码实例:
index.php
query("select * from user where id = 1 limit 1"); #增删改操作使用主库 //$db_master = Extend\Factory::getDatabase('master'); //$db_master->query("update user name = 'xiaobudiu' where id = 29 limit 1"); // 2、使用代理模式 $db_proxy = new Extend\Proxy(); $db_proxy->getUserName(1); $db_proxy->setUserName(29,'xiaobudiu');
Extend/Proxy.php
query("select name from user where id =$id limit 1"); } function setUserName($id, $name) { $db = Factory::getDatabase('master'); $db->query("update user set name = $name where id =$id limit 1"); } }
Extend/Factory.php
config['database']['slave']; $db_conf = $slaves[array_rand($slaves)]; } else { $db_conf = Application::getInstance()->config['database'][$id]; } //注册树模式存储及获取对象 $db = Register::get($key); if (!$db) { $db = new Database\MySQLi(); $db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']); Register::set($key, $db); } return $db; } }
Extend/Application.php
base_dir = $base_dir; $this->config = new Config($base_dir.'/configs'); } static function getInstance($base_dir = '') { if (empty(self::$instance)) { self::$instance = new self($base_dir); } return self::$instance; } }
Extend/Config.php
path = $path; } function offsetGet($key) { if (empty($this->configs[$key])) { $file_path = $this->path.'/'.$key.'.php'; $config = require $file_path; $this->configs[$key] = $config; } return $this->configs[$key]; } function offsetSet($key, $value) { throw new \Exception("cannot write config file."); } function offsetExists($key) { return isset($this->configs[$key]); } function offsetUnset($key) { unset($this->configs[$key]); } }
configs/database.php
array( 'type' => 'MySQL', 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'dbname' => 'test', ), 'slave' => array( 'slave1' => array( 'type' => 'MySQL', 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'dbname' => 'test', ), 'slave2' => array( 'type' => 'MySQL', 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'dbname' => 'test', ), ), ); return $config;
1、单一职责原则:一个类只需要做好一件事情。不要使用一个类完成很多功能,而应该拆分成更多更小的类。
2、开放封闭原则:一个类写好之后,应该是可扩展而不可修改的。
3、依赖倒置原则:一个类不应该强依赖另外一个类,每个类对于另外一个类都是可替换的。
4、配置化原则:尽量使用配置,而不是硬编码。
5、面向接口编程原则:只需要关心某个类提供了哪些接口,而不需要关心他的实现。
1、php中使用ArrayAccess实现配置文件的加载(使得程序可以以数组的方式进行读取配置)
(1)定义Config.php,继承php自带的ArrayAccess接口,并实现相应的方法,用于读取和设置配置
Extend/Config.php
path = $path; } function offsetGet($key) { if (empty($this->configs[$key])) { $file_path = $this->path.'/'.$key.'.php'; $config = require $file_path; $this->configs[$key] = $config; } return $this->configs[$key]; } function offsetSet($key, $value) { throw new \Exception("cannot write config file."); } function offsetExists($key) { return isset($this->configs[$key]); } function offsetUnset($key) { unset($this->configs[$key]); } }
(2)configs/database.php
array( 'type' => 'MySQL', 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'dbname' => 'test', ), 'slave' => array( 'slave1' => array( 'type' => 'MySQL', 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'dbname' => 'test', ), 'slave2' => array( 'type' => 'MySQL', 'host' => '127.0.0.1', 'user' => 'root', 'password' => '123456', 'dbname' => 'test', ), ), ); return $config;
(3)读取配置
index.php
Nach dem Login kopieren
(4)浏览器显示:
到此,就可以在程序中随心所欲的加载配置文件了。
2、在工厂方法中读取配置,生成可配置化的对象
Extend/Factory.php
config['database']['slave']; $db_conf = $slaves[array_rand($slaves)]; } else { $db_conf = Application::getInstance()->config['database'][$id]; } //注册树模式存储及获取对象 $db = Register::get($key); if (!$db) { $db = new Database\MySQLi(); $db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']); Register::set($key, $db); } return $db; } }
Extend/Application.php
base_dir = $base_dir; $this->config = new Config($base_dir.'/configs'); } static function getInstance($base_dir = '') { if (empty(self::$instance)) { self::$instance = new self($base_dir); } return self::$instance; } }
Extend/Config.php
path = $path; } function offsetGet($key) { if (empty($this->configs[$key])) { $file_path = $this->path.'/'.$key.'.php'; $config = require $file_path; $this->configs[$key] = $config; } return $this->configs[$key]; } function offsetSet($key, $value) { throw new \Exception("cannot write config file."); } function offsetExists($key) { return isset($this->configs[$key]); } function offsetUnset($key) { unset($this->configs[$key]); } }
Das obige ist der detaillierte Inhalt vonPHP开发自己的框架,你必须知道这些知识点!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!