摘要:也就是閑時為了寫文章而寫的一篇關于源碼的閱讀筆記。是標準庫的縮寫,一組旨在解決標準問題的接口和類的集合。提供了一套標準的數據結構,一組遍歷對象的迭代器,一組接口,一組標準的異常,一系列用于處理文件的類,提供了一組函數,具體可以查看文檔。
也就是閑時為了寫文章而寫的一篇關于 Pimple 源碼的閱讀筆記。
Pimple 代碼有兩種編碼方式,一種是以 PHP 編寫的,另一種是以 C 擴展編寫的方式,當然個人能力有限呀,也就看看第一種了。
Pimple 鏈接前提知識 ArrayAccess(數組式訪問)接口
官網 WebSite
GitHub - Pimple
Pimple 中文版文檔
提供像訪問數組一樣訪問對象的能力的接口。
http://php.net/manual/zh/clas...
一個 Class 只要實現以下規定的 4 個接口,就可以是像操作數組一樣操作 Object 了。
ArrayAccess { /* 方法 */ abstract public boolean offsetExists ( mixed $offset ) abstract public mixed offsetGet ( mixed $offset ) abstract public void offsetSet ( mixed $offset , mixed $value ) abstract public void offsetUnset ( mixed $offset ) }
偽代碼如下
class A implements ArrayAccess { // 實現了 4 個接口 } $a = new A(); // 可以這么操作 $a["x"] = "x"; // 對應 offsetSet echo $a["x"]; // 對應 offsetGet var_dump(isset($a["x"])); // 對應 offsetExists unset($a["x"]); // 對應 offsetUnset
特別說明,只支持上面四種操作,千萬別以為實現了 ArrayAccess,就可以使用 foreach 了,要實現循環 = 迭代,要實現 Iterator(迭代器)接口,其實 PHP 定義了很多 預定義接口 有空可以看看。
SPL - SplObjectStorage SPLSPL 是 Standard PHP Library(PHP標準庫)的縮寫,一組旨在解決標準問題的接口和類的集合。SPL 提供了一套標準的數據結構,一組遍歷對象的迭代器,一組接口,一組標準的異常,一系列用于處理文件的類,提供了一組函數,具體可以查看文檔。
SplObjectStorageSplObjectStorage 是 SPL 標準庫中的數據結構對象容器,用來存儲一組對象,特別是當你需要唯一標識對象的時候 。
SplObjectStorage implements Countable , Iterator , Serializable , ArrayAccess { /* * 向 SplObjectStorage 添加一個 object,$data 是可選參數 * 因為 SplObjectStorage 實現了 ArrayAccess 的接口,所以可以通過數組的形式訪問,這里相當于設置 object 為數組的 key ,data 是對應的 value,默認 data 是 null */ public void attach ( object $object [, mixed $data = NULL ] ) /* * 檢查 SplObjectStorage 是否包含 object ,相當于 isset 判斷 */ public bool contains ( object $object ) /* * 從 SplObjectStorage 移除 object ,相當于 unset */ public void detach ( object $object ) // 其他接口定義可以自行查看文檔 }
SplObjectStorage 實現了 Countable、Iterator、Serializable、ArrayAccess 四個接口,可實現統計、迭代、序列化、數組式訪問等功能,其中 Iterator 和 ArrayAccess 在上面已經介紹過了。
魔術方法 __invoke()__invoke() 當嘗試以調用函數的方式調用一個對象時,__invoke() 方法會被自動調用。
看一個例子吧,一目了然。
讀源碼 目錄接口pimple ├── CHANGELOG ├── LICENSE ├── README.rst ├── composer.json ├── ext // C 擴展,不展開 │?? └── pimple ├── phpunit.xml.dist └── src └── Pimple ├── Container.php ├── Exception // 異常類定義,不展開 ├── Psr11 │?? ├── Container.php │?? └── ServiceLocator.php ├── ServiceIterator.php ├── ServiceProviderInterface.php └── Tests // 測試文件,不展開PS, Markdown 寫目錄格式真是麻煩,后來找了一個工具 tree 可以直接生成結構。
Container.phpclass Container implements ArrayAccess { private $values = array(); // 存儲 value 的數組 private $factories; // 存儲工廠方法的對象,是 SplObjectStorage 的實例 private $protected; // 存儲保護方法的對象,是 SplObjectStorage 的實例 // 存儲被凍結的服務,新設置一個 service 的時候,可以在還沒有調用這個 service 的時候,覆蓋原先設置,這時不算凍結 // 一旦調用了這個 service 之后,就會存入 $frozen 數組,如果這時還想重新覆蓋這個 service 會報錯,判斷邏輯在 offsetSet 實現。 private $frozen = array(); private $raw = array(); // 存儲 service 原始設置內容,用于 ::raw() 方法讀取 private $keys = array(); // 存儲 key public function __construct(array $values = array()) { $this->factories = new SplObjectStorage(); $this->protected = new SplObjectStorage(); foreach ($values as $key => $value) { $this->offsetSet($key, $value); } } public function offsetSet($id, $value){} public function offsetGet($id){} public function offsetExists($id){} public function offsetUnset($id){} public function factory($callable){} public function protect($callable){} public function raw($id){} public function extend($id, $callable){} public function keys(){} public function register(ServiceProviderInterface $provider, array $values = array()){} }Container 實現了 ArrayAccess 接口,這就可以理解為什么可以通過數組的方式定義服務了。
重要的 function 分析1、offsetSet、offsetExists、offsetUnset 主要實現 ArrayAccess 的接口很容易看懂
2、factory、protect 主要邏輯是判斷傳入的 $callable 是否有 __invoke ,如果有的話,通過 SplObjectStorage::attach,存儲 object 中
3、raw 獲取設置的原始內容
4、key 獲取所有的 key
5、register() 注冊一些通用的 service6、offsetGet()
public function offsetGet($id) { if (!isset($this->keys[$id])) { // 如果沒有設置過,報錯 throw new UnknownIdentifierException($id); } if ( isset($this->raw[$id]) // raw 里已經有值,一般來說就是之前已經獲取過一次實例,再次獲取的時候,就返回相同的值 || !is_object($this->values[$id]) // 對應的 value 不是 object ,而是一個普通的值 || isset($this->protected[$this->values[$id]]) // 存在于 protected 中 || !method_exists($this->values[$id], "__invoke") // 對應的 value 不是閉包 ) { return $this->values[$id]; // 返回 values 數組里的值 } if (isset($this->factories[$this->values[$id]])) { // 如果工廠方法里面設置了相關方法 return $this->values[$id]($this); // 直接調用這個方法,傳入參數($this),也就是匿名函數中可以訪問當前實例的其他服務 } $raw = $this->values[$id]; $val = $this->values[$id] = $raw($this); // 初始化一般的 service ,傳入($this) ,以后再調用都獲取相同的實例 $this->raw[$id] = $raw; // 把原始內容存入 raw 數組 $this->frozen[$id] = true; // 在初始化之后凍結這個 key ,不能被覆蓋 return $val; }7、extend()
擴展一個 service,如果已經被凍結了,也不能被擴展。
與上文說的直接覆蓋還是有區別的,直接覆蓋就是完全不管之前定義的 service ,使用 extend 是可以在原始定義上做出修改public function extend($id, $callable) { // ... 一些判斷邏輯省略 // 如果是 protected 的 service 還不被支持 extend if (isset($this->protected[$this->values[$id]])) { @ rigger_error(sprintf("How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "%s" should be protected?", $id), E_USER_DEPRECATED); } if (!is_object($callable) || !method_exists($callable, "__invoke")) { throw new ExpectedInvokableException("Extension service definition is not a Closure or invokable object."); } $factory = $this->values[$id]; // 主要是這兩行代碼 $extended = function ($c) use ($callable, $factory) { return $callable($factory($c), $c); }; if (isset($this->factories[$factory])) { $this->factories->detach($factory); $this->factories->attach($extended); } return $this[$id] = $extended; }未完待續。還有一篇,主要關于 PSR11 兼容性的。
原創文章,歡迎轉載。轉載請注明出處,謝謝。
原文鏈接地址:http://dryyun.com/2018/04/18/...
作者: dryyun
發表日期: 2018-04-18 14:36:40
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28653.html
摘要:服務容器接口是的簡寫,由組織制定的規范,是開發的實踐標準。實現的容器類源碼很簡單,主要是傳入變量,然后設置這個兩個方法。原創文章,歡迎轉載。原文鏈接地址作者發表日期 接著上篇 還有一些內容沒有寫,上篇已經把關于 Pimple 最主要的代碼分析了一下,這篇主要是關于 PSR-11 兼容性的分析。 PSR-11 服務容器接口 PSR PSR 是 PHP Standard Recommend...
摘要:已經有了非常好的的相關解析,建議先看下一個簡單的依賴注入容器讀源碼筆記上讀源碼筆記下這里通過例子補充下核心方法的說明相關的類型服務類似單例工廠服務多個實例參數僅僅是保存一些變量保護參數匿名函數都會被認為服務,但是如果僅僅是想作為一個 已經有了非常好的Pimple的相關解析,建議先看下:Pimple - 一個簡單的 PHP 依賴注入容器讀 PHP - Pimple 源碼筆記(上)讀 PH...
摘要:服務通過匿名函數定義,返回一個對象的實例定義一些服務請注意,匿名函數可以訪問當前容器實例,從而允許引用其他服務或參數。如果要為所有調用返回不同的實例,請使用方法包裝你的匿名函數。 鏈接 官網 WebSite GitHub - Pimple 這是 Pimple 3.x 的文檔。如果你正在使用 Pimple 1.x ,請查看 Pimple 1.x 文檔。閱讀 Pimple 1.x ...
摘要:實際上,閉包和匿名函數是偽裝成函數的對象。容器流程淺析是社區中比較流行的容器。服務提供者服務提供者是連接容器與具體功能實現類的橋梁。服務提供者需要實現接口所有服務提供者必須實現接口方法。但已經完成了服務提供者的注冊工作。 需要具備的知識點 閉包 閉包和匿名函數在PHP5.3.0中引入的。 閉包是指:創建時封裝周圍狀態的函數。即使閉包所處的環境不存在了,閉包中封裝的狀態依然存在。 理論上...
摘要:安裝代碼加載并實例化參數存儲單例存儲非單例存儲存儲匿名函數獲取匿名函數服務提供者文檔官網文檔 Pimple/Container 安裝 composer require pimple/pimple: ^3.0 代碼 加載并實例化 require __DIR__ . /vendor/autoload.php; $pc = new PimpleContainer(); 參數存儲 $pc[ap...
閱讀 1993·2021-11-24 10:45
閱讀 1849·2021-10-09 09:43
閱讀 1291·2021-09-22 15:38
閱讀 1219·2021-08-18 10:19
閱讀 2837·2019-08-30 15:55
閱讀 3057·2019-08-30 12:45
閱讀 2960·2019-08-30 11:25
閱讀 356·2019-08-29 11:30