摘要:框架源码一常用的四种数据结构简介是的一个标准库。五其他常见的种设计模式适配器模式将一个类的接口转换成客户希望的另一个接口适配器模式使得原本的由于接口不兼容而不能一起工作的那些类可以一起工作。场景一个事件发生后,要执行一连串更新操作。
phpms框架源码https://github.com/wuxiumu/ms一、PHP常用的四种数据结构
简介:spl是php的一个标准库。
官方文档:http://php.net/manual/zh/book...
push("data1");//入栈(先进后出) $stack->push("data2");//入栈 $stack->push("data3");//入栈 echo $stack->pop().PHP_EOL;//出栈 echo $stack->pop().PHP_EOL;//出栈 echo $stack->pop().PHP_EOL;//出栈 } /** *队列(先进先出) */ function duilie(){ $queue = new SplQueue(); $queue->enqueue("data4");//入队列 $queue->enqueue("data5");//入队列 $queue->enqueue("data6");//入队列 echo $queue->dequeue().PHP_EOL;//出队列 echo $queue->dequeue().PHP_EOL;//出队列 echo $queue->dequeue().PHP_EOL;//出队列 } /** * 堆 */ function dui(){ $heap = new SplMinHeap(); $heap->insert("data8");//入堆 $heap->insert("data9");//入堆 $heap->insert("data10");//入堆 echo $heap->extract().PHP_EOL;//从堆中提取数据 echo $heap->extract().PHP_EOL;//从堆中提取数据 echo $heap->extract().PHP_EOL;//从堆中提取数据 } /** * 固定数组(不论使不使用,都会分配相应的内存空间) */ $array = new SplFixedArray(15); $array["0"] = 54; $array["6"] = 69; $array["10"] = 32; var_dump($array);二、PHP链式操作的实现(原理) 1、入口文件
index.php
header("content-type:text/html;charset=utf-8"); define("PHPMSFRAME",__DIR__); define("CORE",PHPMSFRAME."/core"); define("APP",PHPMSFRAME."/app"); define("MODULE","app"); define("DEBUG",true); include "vendor/autoload.php"; if(DEBUG){ $whoops = new WhoopsRun; $whoops->pushHandler(new WhoopsHandlerPrettyPageHandler); $whoops->register(); ini_set("display_error", "On"); }else{ ini_set("display_error", "Off"); } include CORE."/common/function.php"; include CORE."/phpmsframe.php"; spl_autoload_register("corephpmsframe::load"); corephpmsframe::run();2、自动加载类
corephpmsframe.php
ctrl; $action = $route->action; $ctrlfile = APP."/ctrl/".$ctrlClass."Ctrl.php"; $ctrlClass = "".MODULE."ctrl".$ctrlClass."Ctrl"; if(is_file($ctrlfile)){ include $ctrlfile; $ctrl = new $ctrlClass(); $ctrl->$action(); }else{ $msg = "控制器 $ctrlClass 不存在 "; self::reportingDog($msg); } } static public function load($class) { if(isset($classMap[$class])){ return true; }else{ $class = str_replace("", "/", $class); $file = PHPMSFRAME."/".$class.".php"; if(is_file($file)){ include $file; self::$classMap[$class] = $class; }else{ return false; } } } public function assign($name,$value){ $this->assign[$name]=$value; } public function display($file){ $file_path = APP."/views/".$file; if(is_file($file_path)){ /***********twig模板***********/ $loader = new Twig_Loader_Filesystem(APP."/views"); $twig = new Twig_Environment($loader, array( "cache" => PHPMSFRAME."/cache", "debug"=>DEBUG, )); $template = $twig->load($file); $template->display($this->assign?$this->assign:""); /***********twig模板end***********/ /***********原生模板***********/ //extract($this->assign); //include $file_path; /***********原生模板end***********/ } } static private function reportingDog($msg){ echo $msg." "; include "smile/havefun.php"; $num = str_pad(rand(00,32),2,"0",STR_PAD_LEFT); $num = "str_".$num; $Parsedown = new Parsedown(); echo $Parsedown->text($$num); $num = "str_".rand(50,84); echo $Parsedown->text($$num); exit; } }3、数据库类
注:只是原理,并没有对方法进行具体的封装,具体的封装还是看个人喜好去定链式查询的风格。
corelibmodel.php
corelibdrivedatabasemedooModel.php
三、PHP魔术方法的使用在php设计模式中,会涉及到很多魔术方法的使用,这里也对经常会用到的魔术方法进行简单总结。
1、框架入口文件corephpmsframe.php
/** * 魔术方法的使用 */ /* 这是一个魔术方法,当一个对象或者类获取其不存在的属性的值时, * 如:$obj = new BaseController ; * $a = $obj -> a ; * 该方法会被自动调用,这样做很友好,可以避免系统报错 */ public function __get($property_name){ $msg = "属性 $property_name 不存在 "; self::reportingDog($msg); } /* 这是一个魔术方法,当一个对象或者类给其不存在的属性赋值时, * 如:$obj = new BaseController ; * $obj -> a = 12 ; * 该方法(__set(属性名,属性值))会被自动调用,这样做很友好,可以避免系统报错 */ public function __set($property_name,$value){ $msg = "属性 $property_name 不存在 "; self::reportingDog($msg); } /* 这是一个魔术方法,当一个对象或者类的不存在属性进行isset()时, * 注意:isset 用于检查一个量是否被赋值 如果为NULL会返回false * 如:$obj = new BaseController ; * isset($obj -> a) ; * 该方法会被自动调用,这样做很友好,可以避免系统报错 */ public function __isset($property_name){ $msg = "属性 $property_name 不存在 "; self::reportingDog($msg); } /* 这是一个魔术方法,当一个对象或者类的不存在属性进行unset()时, * 注意:unset 用于释放一个变量所分配的内存空间 * 如:$obj = new BaseController ; * unset($obj -> a) ; * 该方法会被自动调用,这样做很友好,可以避免系统报错 */ public function __unset($property_name){ $msg = "属性 $property_name 不存在 "; self::reportingDog($msg); } /* 当对这个类的对象的不存在的实例方法进行“调用”时,会自动调用该方法, * 这个方法有2个参数(必须带有的): * $methodName 表示要调用的不存在的方法名; * $argument 是一个数组,表示要调用该不存在的方法时,所使用的实参数据, */ public function __call($methodName,$argument){ $msg = "实例方法 $methodName 不存在 "; self::reportingDog($msg); }四、三种基础设计模式 1、工厂模式通过传入参数的不同,来实例化不同的类。
$cache =CoreextendCacheFactory::getCacheObj("redis",array( "host" => "127.0.0.1", "pass" => "myRedis&&&" )); var_dump($cache);coreextendCacheFactory.php
2、单例模式保证一个类只实例化一个类对象,进而减少系统开销和资源的浪费
//单例模式创建对象 $obj = ExtendSingleObject::getInstance(); $obj2 = ExtendSingleObject::getInstance(); var_dump($obj,$obj2);//从结果可以看出,两个实例化的对象其实是一个对象coreextendSingleObject.php
3、注册树模式将我们用到的对象注册到注册树上,然后在之后要用到这个对象的时候,直接从注册树上取下来就好。(就和我们用全局变量一样方便)
coreextendRegisterTree,php
关于注册树模式,这里推荐一篇文章 ,可以方便理解。 https://segmentfault.com/a/11...
五、其他常见的8种PHP设计模式 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();这篇文章介绍了类适配器的使用,感兴趣的可以了解一下 https://segmentfault.com/a/11...
2、策略模式将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,这种模式就是策略模式,策略模式可以实现依赖倒置以及控制反转。
实例举例:假如一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示展示不同的广告。
userType->showAd(); echo "Category:"; $this->userType->showCategory(); } /** * 策略模式 * 根据传递的用户性别展示不同类别数据 * @param ExtendUserType $userType */ function setUserType(ExtendUserType $userType) { $this->userType = $userType; } } $obj = new Home(); if ($_GET["userType"] == "female"){ $userType = new ExtendFemaleUserType(); } else { $userType = new ExtendMaleUserType(); } $obj->setUserType($userType); $obj->index();Extend/userType.php(定义的接口)
MaleUserType.php、FemaleUserType.php(具体实现的类 )
3、数据对象映射模式将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作。
下面在代码中实现数据对象映射模式,我们将实现一个ORM类,将复杂的sql语句映射成对象属性的操作。并结合使用数据对象映射模式、工厂模式、注册模式。
(1)数据库映射模式简单实例实现
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)数据库映射模式复杂案例实现
name = "小卜丢饭团子"; $user->salary = "20000"; $user->city = "浙江省"; } function test() { $user = ExtendFactory::getUserObj(25); $user->city = "广东省"; } } $ex = new EX(); $ex->index();Extend/Factory.php
Extend/Register.php
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 "在用户下单之后进行的一系列操作4.2、观察者模式典型实现方式:
"; } } $event = new Event(); $event->firmOrder();(1)定义2个接口:观察者(通知)接口、被观察者(主题)接口
(2)定义2个类,观察者类实现观察者接口、被观察者类实现被观察者接口
(3)被观察者注册自己需要通知的观察者
(4)被观察者类某个业务逻辑发生时,通知观察者对象,进而每个观察者执行自己的业务逻辑。
代码示例:
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" ) . " 短信日志记录:购票成功:$ticket5、原型模式
"); } } /** * 购票成功后,记录日志 * 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号");原型模式与工厂模式的作用类似,都是用来创建对象的。但是实现方式是不同的。原型模式是先创建好一个原型对象,然后通过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 "
";关于原型模式文章:https://www.imooc.com/article...
6、装饰器模式可以动态的添加或修改类的功能
一个类实现一个功能,如果要再修改或添加额外的功能,传统的编程模式需要写一个子类继承它,并重新实现类的方法。
使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性。
color = $color; } public function beforeEcho() { echo ""; } public function afterEcho() { echo "
在不需要了解内部实现的前提下,遍历一个聚合对象的内部元素而又不暴露该对象的内部表示,这就是PHP迭代器模式的定义。
相对于传统编程模式,迭代器模式可以隐藏遍历元素的所需的操作。
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); } }关于php迭代器文章 http://www.php.cn/php-weiziji...
8、代理模式
在客户端与实体之间建立一个代理对象(proxy),客户端对实体进行操作全部委派给代理对象,隐藏实体的具体实现细节。
典型的应用就是mysql的主从结构,读写分离。在mysql中,对所有读的操作请求从库,所有写的操作请求主库。
声明一个代理类,前台使用时只需创建一个代理类,调用对应方法即可。代码实例:
query("select * from user where id = 1 limit 1"); #增删改操作使用主库 //$db_master = ExtendFactory::getDatabase("master"); //$db_master->query("update user name = "xiaobudiu" where id = 29 limit 1"); // 2、使用代理模式 $db_proxy = new ExtendProxy(); $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 DatabaseMySQLi(); $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;关于php代理模式文章 https://segmentfault.com/a/11...
五、其余设计模式以及总结文章接:
https://segmentfault.com/a/11...
https://segmentfault.com/a/11...
六、面向对象编程的基本原则
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)读取配置
到此,就可以在程序中随心所欲的加载配置文件了。
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 DatabaseMySQLi(); $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]); } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/29977.html
摘要:下面来看主要内容爆款面试宝典目录总览面试前的准备工作面试就是双方互相试探的过程,抛去技术面不谈,面试前简历自我介绍,以及跟对线细节一定要提前准备好基础技术基础集合多线程框架横向技术数据库消息中间件网络设计模式 为了帮大家解决这一问题,LZ今天就把今年金三银四帮我斩获了8家大厂...
摘要:假如我们先告诉网关或服务注册中心我们要下线,等对方完成服务摘除操作再中止进程,那不会有任何流量受到影响这是优雅停止,将单个组件的启停对整个系统影响最小化。与此同时,会将从对应的上摘除。 作者:吴叶磊 一直以来我对优雅地停止 Pod 这件事理解得很单纯:不就利用是 PreStop hook 做优雅退出吗?但最近发现很多场景下 PreStop Hook 并不能很好地完成需求,这篇文章就简单...
摘要:假如我们先告诉网关或服务注册中心我们要下线,等对方完成服务摘除操作再中止进程,那不会有任何流量受到影响这是优雅停止,将单个组件的启停对整个系统影响最小化。与此同时,会将从对应的上摘除。 作者:吴叶磊 一直以来我对优雅地停止 Pod 这件事理解得很单纯:不就利用是 PreStop hook 做优雅退出吗?但最近发现很多场景下 PreStop Hook 并不能很好地完成需求,这篇文章就简单...
阅读 1438·2021-11-11 16:54
阅读 9316·2021-11-02 14:44
阅读 2371·2021-10-22 09:53
阅读 3259·2019-08-30 11:18
阅读 1950·2019-08-29 13:29
阅读 2002·2019-08-27 10:58
阅读 1622·2019-08-26 11:38
阅读 3517·2019-08-26 10:31