SPL函數 (standard php librarys)php
類自動載入,儘管 __autoload()
函數也能自動加載類和接口,但更建議使用 spl_autoload_register('函數名')
函數。 spl_autoload_register('函數名')
提供了一種更加靈活的方式來實現類的自動加載(同一個應用中,能夠支持任意數量的加載器,好比第三方庫中的)。所以,再也不建議使用 __autoload()
函數,在之後的版本中它可能被棄用。mysql
spl_autoload_register('函數名')
自動加載類,能夠重複使用,不會報函數名相同的錯誤!能夠實現咱們自定義函數的激活,它的返回值是bool類型:true or false。redis
若是不寫參數,那麼它會去調用 spl_autoload()方法,這個方法默認會執行下面的語句:require_once 類名.php 或 類名.inc。算法
spl_autoload_register('函數名') function 函數名($class){<br> require __DIR__.'/'.$class.'.php';<br> }
咱們能夠在入口文件中,使用spl_autoload_register()來完成類的自動加載sql
1. 命名空間必須與絕對路徑一致(文件裏寫命名空間從根目錄下它所在文件夾開始到它的上一層文件夾名)
2. 類名首字母必須大寫
3. 除入口文件外,其它的".php"文件中只能存在一個類,不能有外部可執行的代碼數據庫
鏈式操做最重要的是return $this
要求每一個方法體內必須return $this
編程
class Database { static private $db; private function __construct() { } static function getInstance() { if (empty(self::$db)) { self::$db = new self; return self::$db; } else { return self::$db; } } function where($where) { return $this; } function order($order) { return $this; } function limit($limit) { return $this; } function query($sql) { echo "SQL: $sql\n"; } }
鏈式操做能簡化代碼,好比:設計模式
$db=new DataBase(); $db->where("id>10")->order(2)->limit(10);
__FILE__
文件的完整路徑和文件名,若是用在被包含的文件中,則返回被包含文件路徑名。
__DIR__
文件的所在目錄,不包括文件名。 等價於dirname(__FILE__) 除了根目錄,不包括末尾的反斜槓
,basename(__FILE__)返回的是文件名
__FUNCTION__
返回的是函數名稱
__METHOD__
返回的是類的方法名
__CLASS__
返回的是類的名稱
__NAMESPACE__
返回的是當前命名空間的名稱數組
__get/ __set # 訪問不存在的屬性 __call/ __callStatic # 調用不存在的方法 __toString # 對象做爲字符串使用 __invoke # 對象做爲方法使用
咱們定義一個專門用來建立其它對象的類。 這樣在須要調用某個類的時候,咱們就不須要去使用new
關鍵字實例化這個類,而是經過咱們的工廠類調用某個方法獲得類的實例。
好處:當咱們對象所對應的類的類名發生變化的時候,只須要在工廠類裏面修改便可,而不用一個一個的去修改緩存
class A { //不容許類直接實例化 或克隆 private function __construct(){} private function __clone(){} } class B { //不容許類直接實例化 或克隆 private function __construct(){} private function __clone(){} } class Factory { public static function getInstance($class) { //類對象的獲取方式經過工廠類 產生 return new $class(); } } //使用 $a = Factory::getInstance('A'); $b = Factory::getInstance('B');
單例模式的最大好處就是減小資源的浪費,保證整個環境中只存在一個實例化的對象,特別適合資源鏈接類的編寫。
只實例化一次,內部實例化,對外只有一個開放方法。
// 單例模式(口訣:三私一公) class Singleton{ //私有化構造方法,禁止外部實例化對象 private function __construct(){} //私有化__clone,防止對象被克隆 private function __clone(){} //私有化內部實例化的對象 private static $instance = null; // 公有靜態實例方法 public static function getInstance(){ if(self::$instance == null){ //內部實例化對象 self::$instance = new self(); } return self::$instance; } }
單例模式解決的是如何在整個項目中建立惟一對象實例的問題,工廠模式解決的是如何不經過new創建實例對象的方法。 那麼註冊樹模式想解決什麼問題呢? 在考慮這個問題前,咱們仍是有必要考慮下前兩種模式目前面臨的侷限。 首先,單例模式建立惟一對象的過程自己還有一種判斷,即判斷對象是否存在。存在則返回對象,不存在則建立對象並返回。 每次建立實例對象都要存在這麼一層判斷。 工廠模式更多考慮的是擴展維護的問題。 總的來講,單例模式和工廠模式能夠產生更加合理的對象。怎麼方便調用這些對象呢?並且在項目內創建的對象好像散兵遊勇同樣,不便統籌管理安排啊。於是,註冊樹模式應運而生。無論你是經過單例模式仍是工廠模式仍是兩者結合生成的對象,都通通給我「插到」註冊樹上。我用某個對象的時候,直接從註冊樹上取一下就好。這和咱們使用全局變量同樣的方便實用。 並且註冊樹模式還爲其餘模式提供了一種很是好的想法
註冊器解決對象屢次調用場景, 減小new新的對象的次數,防止重複建立對象。看對象是不是同一個對象方法,var_dump
後看id
,#1
,#2
等字樣id
,它是php
內部對象惟一標識
註冊器模式:Register.php
,用來將一些對象註冊到全局的註冊樹上,能夠在任何地方訪問。set()
:將對象映射到全局樹上,_unset()
:從樹上移除,get()
:去註冊到樹上的對象。
class Register { protected static $objects; //註冊 static function set($alias, $object) { self::$objects[$alias] = $object; } //獲取 static function get($key) { if (!isset(self::$objects[$key])) { return false; } return self::$objects[$key]; } //註銷 function _unset($alias) { unset(self::$objects[$alias]); } }
一、工廠模式的特徵有一個統一輩子成對象的入口,使用工廠方式生成對象,而不是在代碼直接new
。爲了後期更好的擴展和修改
二、單例模式的特徵是對象不可外部實例而且只能實例化一次,當對象已存在就直接返回該對象
三、註冊樹模式的特徵是對象不用在經過類建立,具備全局對象樹類。解決對象屢次調用場景, 減小new
新的對象的次數
PHP
的數據庫操做有mysql/mysqli/pdo
三種,能夠用適配器模式統一成一致。相似的場景還有cache
適配器,能夠將memcache/redis/file/apc
等不一樣的緩存函數統一成一致的接口。使用適配器策略是爲了更好的兼容。相似於手機電源適配器,若是能用一個充電器對全部手機充電固然是最方便的。不管什麼手機,都只須要拿一個充電器。不然,不一樣手機不一樣充電器,太麻煩。
新建一個接口 IDatabase
而後在這個接口裏面申明統一的方法體,再讓不一樣的類去實現這個接口,和重寫其抽象方法。當咱們在入口文件使用到不一樣的類的時候,就只是實例化的類名不一樣,其它調用方法體的地方都一致。
/** * 一、新建一個接口 IDatabase 而後在這個接口裏面申明統一的方法體 */ interface IDatabase { //鏈接 function connect($host, $user, $passwd, $dbname); //查詢 function query($sql); //關閉鏈接 function close(); }
/** * 二、讓不一樣的類去實現這個接口,和重寫其抽象方法。 */ class MySQLi implements IDatabase { protected $conn; function connect($host, $user, $passwd, $dbname) { $conn = mysqli_connect($host, $user, $passwd, $dbname); $this->conn = $conn; } function query($sql) { return mysqli_query($this->conn, $sql); } function close() { mysqli_close($this->conn); } }
策略模式:
if...else...
判斷。若是新增長一種用戶類型,只須要新增長一種策略便可依賴倒置,控制反轉
,面向對象很重要的一個思想是解耦策略模式實現:
// 策略的接口文件:約定策略的全部行爲 interface UserStrategy { function showAd(); function showCategory(); } // 實現接口的全部方法 class FemaleUserStrategy implements UserStrategy { function showAd() { echo "2018新款女裝"; } function showCategory() { echo "女裝"; } } // 實現接口的全部方法 class MaleUserStrategy implements UserStrategy { function showAd() { echo "iPhone X"; } function showCategory() { echo "電子產品"; } }
class Page { /** * @var \IMooc\UserStrategy */ protected $strategy; function index() { echo "AD:"; $this->strategy->showAd(); echo "<br/>"; echo "Category:"; $this->strategy->showCategory(); echo "<br/>"; } function setStrategy(\IMooc\UserStrategy $strategy) { $this->strategy = $strategy; } } $page = new Page; if (isset($_GET['female'])) { $strategy = new \IMooc\FemaleUserStrategy(); } else { $strategy = new \IMooc\MaleUserStrategy(); } $page->setStrategy($strategy); $page->index();
不須要在page
類中判斷業務邏輯,若是在index
裏面寫邏輯判斷 if男else女
就會存在‘依賴’,這個是很差的,存在很大耦合,因此把邏輯寫在外部,而且在page
裏面增長一個set
的方法,這個方法的做用就是‘注入’一個對象。只有再使用時才綁定,這樣之後更方便的替換修改MaleUserStratey
類,實現了兩個類的解耦,這就是策略模式的依賴倒置,實現了硬編碼到解耦。
依賴倒置原則:
A. 高層次的模塊不該該依賴於低層次的模塊,他們都應該依賴於抽象
B. 抽象不該該依賴於具體實現,具體實現應該依賴於抽象
在這裏無論是Page,仍是低層次的MaleUserStratey和FemaleUserStrategy都依賴於抽象userStrategy這個抽象,而UserStrategy不依賴於具體實現,具體實現Female和male都依賴於UserStrategy這個抽象。有點繞,應該是這個關係。
數據對象映射模式,是將對象和數據存儲映射起來,對一個對象的操做會映射爲對數據存儲的操做,比咱們在代碼中new一個對象,那麼使用該模式就能夠將對對象的一些操做,好比說咱們設置的一些屬性,它就會自動保存到數據庫,跟數據庫中表的一條記錄對應起來,數據對象映射模式就是將sql的操做轉化爲對象的操做。
對象關係映射(英語:Object Relation Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程序技術,用於實現面向對象編程語言裏不一樣類型系統的數據之間的轉換。從效果上說,它實際上是建立了一個可在編程語言裏使用的--「虛擬對象數據庫」。
面向對象是從軟件工程基本原則(如耦合、聚合、封裝)的基礎上發展起來的,而關係數據庫則是從數學理論發展而來的,兩套理論存在顯著的區別。爲了解決這個不匹配的現象,對象關係映射技術應運而生。簡單的說:ORM
至關於中繼數據
實例,在代碼中實現數據對象映射模式,咱們將寫一個ORM
類,將複雜的SQL
語句映射成對象屬性的操做。結合使用數據對象映射模式,工廠模式,註冊模式混合使用
class User { protected $id; protected $data; protected $db; protected $change = false; function __construct($id) { $this->db = Factory::getDatabase(); $res = $this->db->query("select * from user where id = $id limit 1"); $this->data = $res->fetch_assoc(); $this->id = $id; } function __get($key) { if (isset($this->data[$key])) { return $this->data[$key]; } } function __set($key, $value) { $this->data[$key] = $value; $this->change = true; } /** * 析構方法 */ function __destruct() { if ($this->change) { foreach ($this->data as $k => $v) { $fields[] = "$k = '{$v}'"; } $this->db->query("update user set " . implode(', ', $fields) . "where id = {$this->id} limit 1"); } } }
class Factory { /** 註冊$user * @param $id * @return User */ static function getUser($id) { $key = 'user_'.$id; $user = Register::get($key); if (!$user) { $user = new User($id); Register::set($key, $user); } return $user; } }
1. 觀察者模式( `Observer` ),當一個對象狀態發生改變時,依賴它的對象所有會收到通知,並自動更新 2. 場景:一個事件發生後,要執行一連串更新操做。傳統的編程方式,就是在事件的代碼以後直接加入處理邏輯。當更新的邏輯增多後,代碼會變得難以維護。這種方式是耦合的,入侵式的,增長新的邏輯須要修改事件主體的代碼 3. 觀察者模式實現了低耦合,非入侵式的通知與更新機制
觀察者模式實現:
Add
觀察者()方法Foreach
(觀察者句柄數組 as
某一個觀察者)//事件基類 abstract class EventGenerator { private $observers = array(); function addObserver(Observer $observer) { $this->observers[] = $observer; } function notify() { foreach($this->observers as $observer) { $observer->update(); } } }
//觀察者接口 interface Observer { function update($event_info = null); }
class Event extends EventGenerator { /** * 觸發事件 */ function trigger() { echo "Event<br/>\n"; $this->notify(); } } class Observer1 implements Observer { function update($event_info = null) { echo "邏輯1<br />\n"; } } class Observer2 implements Observer { function update($event_info = null) { echo "邏輯2<br />\n"; } } $event = new Event; $event->addObserver(new Observer1); $event->addObserver(new Observer2); $event->trigger();
clone
原型對象來建立新的對象。這樣就免去了類建立時的重複初始化操做 new
就會消耗很大,原型模式僅需內存拷貝便可
Decorator
),能夠動態地添加修改類的功能 class Canvas { public $data; protected $decorators = array(); //添加裝飾器 function addDecorator(DrawDecorator $decorator) { $this->decorators[] = $decorator; } //執行裝飾器前置操做 先進先出原則 function beforeDraw() { foreach($this->decorators as $decorator) { $decorator->beforeDraw(); } } //執行裝飾器後置操做 先進後出原則 function afterDraw() { //注意,反轉 $decorators = array_reverse($this->decorators); foreach($decorators as $decorator) { $decorator->afterDraw(); } } function draw() { //調用裝飾器前置操做 $this->beforeDraw(); foreach($this->data as $line) { foreach($line as $char) { echo $char; } echo "<br />\n"; } //調用裝飾器後置操做 $this->afterDraw(); }
//裝飾器接口 interface DrawDecorator { function beforeDraw(); function afterDraw(); }
//實現顏色裝飾器實現接口 class ColorDrawDecorator implements DrawDecorator { protected $color; function __construct($color = 'red') { $this->color = $color; } function beforeDraw() { echo "<div style='color: {$this->color};'>"; } function afterDraw() { echo "</div>"; } }
應用場景:遍歷數據庫表,拿到全部的user對象,而後用佛 foreach
循環,在循環的過程當中修改某些字段的
class AllUser implements \Iterator { protected $index = 0; protected $data = []; public function __construct() { $link = mysqli_connect('192.168.0.91', 'root', '123', 'xxx'); $rec = mysqli_query($link, 'select id from doc_admin'); $this->data = mysqli_fetch_all($rec, MYSQLI_ASSOC); } //1 重置迭代器 public function rewind() { $this->index = 0; } //2 驗證迭代器是否有數據 public function valid() { return $this->index < count($this->data); } //3 獲取當前內容 public function current() { $id = $this->data[$this->index]; return User::find($id); } //4 移動key到下一個 public function next() { return $this->index++; } //5 迭代器位置key public function key() { return $this->index; } } //實現迭代遍歷用戶表 $users = new AllUser(); //可實時修改 foreach ($users as $user){ $user->add_time = time(); $user->save(); }
proxy
),客戶端對實體進行的操做所有委派給代理對象,隱藏實體的具體實現細節。Proxy
還能夠與業務代碼分離,部署到另外的服務器,業務代碼中經過RPC
來委派任務。代理模式:數據庫主從,經過代理設置主從讀寫設置
傳統方式:
須要手動的去選擇主庫和從庫。
代理模式:
//作約束接口 interface IUserProxy { function getUserName($id); function setUserName($id, $name); }
class Proxy implements IUserProxy { function getUserName($id) { $db = Factory::getDatabase('slave'); $db->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"); } }
$id = 1; $proxy = new \IMooc\Proxy(); $proxy->getUser($id); $proxy->setUser($id, array('name' => 'wang'));
一個類,只需作好一件事情
。不要使用一個類來完成很複雜的功能,而是拆分設計成更小更具體的類。一個類,應該能夠擴展,而不可修改
。一個類在實現以後,應該是對擴展開放,對修是改封閉的,不該該使用修改來增長功能,而是經過擴展來增長功能。依賴注入
的方式,經過使用注入,將A類依賴的B類的對象注入給A類,B類對於A類來講就是能夠替換的。若是C類實現了和B類同樣的接口,那對於A類,B和C也是能夠隨意替換的。儘量的使用配置,而不是使用硬編碼
。數據參數和常量應該放在配置文件中。像類的關係的定義,也應該是能夠配置的。 若是實現ArrayAcess
接口,則能使一個對象屬性的訪問,能夠以數組的方式進行
class Config implements \ArrayAccess { protected $path; protected $configs = array(); //配置文件目錄 function __construct($path) { $this->path = $path; } //獲取數組的key 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]; } //設置數組的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]); } }
controller.php
$config = array( 'home' => array( 'decorator' => array( //'App\Decorator\Login', //'App\Decorator\Template', //'App\Decorator\Json', ), ), 'default' => 'hello world', ); return $config;
完!