成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

讀 PHP - Pimple 源碼筆記(上)

cfanr / 910人閱讀

摘要:也就是閑時(shí)為了寫文章而寫的一篇關(guān)于源碼的閱讀筆記。是標(biāo)準(zhǔn)庫的縮寫,一組旨在解決標(biāo)準(zhǔn)問題的接口和類的集合。提供了一套標(biāo)準(zhǔn)的數(shù)據(jù)結(jié)構(gòu),一組遍歷對象的迭代器,一組接口,一組標(biāo)準(zhǔn)的異常,一系列用于處理文件的類,提供了一組函數(shù),具體可以查看文檔。

也就是閑時(shí)為了寫文章而寫的一篇關(guān)于 Pimple 源碼的閱讀筆記。
Pimple 代碼有兩種編碼方式,一種是以 PHP 編寫的,另一種是以 C 擴(kuò)展編寫的方式,當(dāng)然個(gè)人能力有限呀,也就看看第一種了。

Pimple 鏈接  
官網(wǎng) WebSite
GitHub - Pimple
Pimple 中文版文檔
前提知識(shí) ArrayAccess(數(shù)組式訪問)接口

提供像訪問數(shù)組一樣訪問對象的能力的接口。

http://php.net/manual/zh/clas...

一個(gè) Class 只要實(shí)現(xiàn)以下規(guī)定的 4 個(gè)接口,就可以是像操作數(shù)組一樣操作 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 {
    // 實(shí)現(xiàn)了 4 個(gè)接口
}

$a = new A();

// 可以這么操作
$a["x"] = "x"; // 對應(yīng) offsetSet  
echo $a["x"]; // 對應(yīng) offsetGet  
var_dump(isset($a["x"])); // 對應(yīng) offsetExists  
unset($a["x"]); // 對應(yīng) offsetUnset  

特別說明,只支持上面四種操作,千萬別以為實(shí)現(xiàn)了 ArrayAccess,就可以使用 foreach 了,要實(shí)現(xiàn)循環(huán) = 迭代,要實(shí)現(xiàn) Iterator(迭代器)接口,其實(shí) PHP 定義了很多 預(yù)定義接口 有空可以看看。

SPL - SplObjectStorage SPL

SPL 是 Standard PHP Library(PHP標(biāo)準(zhǔn)庫)的縮寫,一組旨在解決標(biāo)準(zhǔn)問題的接口和類的集合。SPL 提供了一套標(biāo)準(zhǔn)的數(shù)據(jù)結(jié)構(gòu),一組遍歷對象的迭代器,一組接口,一組標(biāo)準(zhǔn)的異常,一系列用于處理文件的類,提供了一組函數(shù),具體可以查看文檔。

SplObjectStorage

SplObjectStorage 是 SPL 標(biāo)準(zhǔn)庫中的數(shù)據(jù)結(jié)構(gòu)對象容器,用來存儲(chǔ)一組對象,特別是當(dāng)你需要唯一標(biāo)識(shí)對象的時(shí)候 。

SplObjectStorage implements Countable , Iterator , Serializable , ArrayAccess {

    /* 
     * 向 SplObjectStorage 添加一個(gè) object,$data 是可選參數(shù)
     * 因?yàn)?SplObjectStorage 實(shí)現(xiàn)了 ArrayAccess 的接口,所以可以通過數(shù)組的形式訪問,這里相當(dāng)于設(shè)置 object 為數(shù)組的 key ,data 是對應(yīng)的 value,默認(rèn) data 是 null
     */
    public void attach ( object $object [, mixed $data = NULL ] )
    
    /* 
     * 檢查 SplObjectStorage 是否包含 object ,相當(dāng)于 isset 判斷
     */
    public bool contains ( object $object )
    
    /* 
     * 從 SplObjectStorage 移除 object ,相當(dāng)于 unset 
     */
    public void detach ( object $object )
    // 其他接口定義可以自行查看文檔
    
}

SplObjectStorage 實(shí)現(xiàn)了 Countable、Iterator、Serializable、ArrayAccess 四個(gè)接口,可實(shí)現(xiàn)統(tǒng)計(jì)、迭代、序列化、數(shù)組式訪問等功能,其中 Iterator 和 ArrayAccess 在上面已經(jīng)介紹過了。

魔術(shù)方法 __invoke()

__invoke() 當(dāng)嘗試以調(diào)用函數(shù)的方式調(diào)用一個(gè)對象時(shí),__invoke() 方法會(huì)被自動(dòng)調(diào)用。

看一個(gè)例子吧,一目了然。


讀源碼
目錄接口
pimple
├── CHANGELOG
├── LICENSE
├── README.rst
├── composer.json
├── ext // C 擴(kuò)展,不展開
│?? └── pimple
├── phpunit.xml.dist
└── src
    └── Pimple
        ├── Container.php
        ├── Exception // 異常類定義,不展開
        ├── Psr11
        │?? ├── Container.php
        │?? └── ServiceLocator.php
        ├── ServiceIterator.php
        ├── ServiceProviderInterface.php
        └── Tests // 測試文件,不展開

PS, Markdown 寫目錄格式真是麻煩,后來找了一個(gè)工具 tree 可以直接生成結(jié)構(gòu)。

Container.php
class Container implements ArrayAccess
{
    private $values = array(); // 存儲(chǔ) value 的數(shù)組
    private $factories; // 存儲(chǔ)工廠方法的對象,是 SplObjectStorage 的實(shí)例
    private $protected; // 存儲(chǔ)保護(hù)方法的對象,是 SplObjectStorage 的實(shí)例
    
    // 存儲(chǔ)被凍結(jié)的服務(wù),新設(shè)置一個(gè) service 的時(shí)候,可以在還沒有調(diào)用這個(gè) service 的時(shí)候,覆蓋原先設(shè)置,這時(shí)不算凍結(jié)
    // 一旦調(diào)用了這個(gè) service 之后,就會(huì)存入 $frozen 數(shù)組,如果這時(shí)還想重新覆蓋這個(gè) service 會(huì)報(bào)錯(cuò),判斷邏輯在 offsetSet 實(shí)現(xiàn)。
    private $frozen = array(); 
    
    private $raw = array(); // 存儲(chǔ) service 原始設(shè)置內(nèi)容,用于 ::raw() 方法讀取 
    private $keys = array(); // 存儲(chǔ) 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 實(shí)現(xiàn)了 ArrayAccess 接口,這就可以理解為什么可以通過數(shù)組的方式定義服務(wù)了。

重要的 function 分析

1、offsetSet、offsetExists、offsetUnset 主要實(shí)現(xiàn) ArrayAccess 的接口很容易看懂
2、factory、protect 主要邏輯是判斷傳入的 $callable 是否有 __invoke ,如果有的話,通過 SplObjectStorage::attach,存儲(chǔ) object 中
3、raw 獲取設(shè)置的原始內(nèi)容
4、key 獲取所有的 key
5、register() 注冊一些通用的 service

6、offsetGet()

    public function offsetGet($id)
    {
        if (!isset($this->keys[$id])) {    // 如果沒有設(shè)置過,報(bào)錯(cuò)
            throw new UnknownIdentifierException($id);
        }
        
        if (
            isset($this->raw[$id])  // raw 里已經(jīng)有值,一般來說就是之前已經(jīng)獲取過一次實(shí)例,再次獲取的時(shí)候,就返回相同的值
            || !is_object($this->values[$id]) // 對應(yīng)的 value 不是 object ,而是一個(gè)普通的值
            || isset($this->protected[$this->values[$id]]) // 存在于 protected 中
            || !method_exists($this->values[$id], "__invoke") // 對應(yīng)的 value 不是閉包
        ) {
            return $this->values[$id]; // 返回 values 數(shù)組里的值
        }

        if (isset($this->factories[$this->values[$id]])) { // 如果工廠方法里面設(shè)置了相關(guān)方法
            return $this->values[$id]($this); // 直接調(diào)用這個(gè)方法,傳入?yún)?shù)($this),也就是匿名函數(shù)中可以訪問當(dāng)前實(shí)例的其他服務(wù)
        }

        $raw = $this->values[$id];
        $val = $this->values[$id] = $raw($this); // 初始化一般的 service ,傳入($this) ,以后再調(diào)用都獲取相同的實(shí)例
        $this->raw[$id] = $raw; // 把原始內(nèi)容存入 raw 數(shù)組

        $this->frozen[$id] = true; // 在初始化之后凍結(jié)這個(gè) key ,不能被覆蓋

        return $val;
    }

7、extend()

擴(kuò)展一個(gè) service,如果已經(jīng)被凍結(jié)了,也不能被擴(kuò)展。
與上文說的直接覆蓋還是有區(qū)別的,直接覆蓋就是完全不管之前定義的 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;
    }
未完待續(xù)。

還有一篇,主要關(guān)于 PSR11 兼容性的。

原創(chuàng)文章,歡迎轉(zhuǎn)載。轉(zhuǎn)載請注明出處,謝謝。
原文鏈接地址:http://dryyun.com/2018/04/18/...
作者: dryyun
發(fā)表日期: 2018-04-18 14:36:40

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/28653.html

相關(guān)文章

  • PHP - Pimple 源碼筆記(下)

    摘要:服務(wù)容器接口是的簡寫,由組織制定的規(guī)范,是開發(fā)的實(shí)踐標(biāo)準(zhǔn)。實(shí)現(xiàn)的容器類源碼很簡單,主要是傳入變量,然后設(shè)置這個(gè)兩個(gè)方法。原創(chuàng)文章,歡迎轉(zhuǎn)載。原文鏈接地址作者發(fā)表日期 接著上篇 還有一些內(nèi)容沒有寫,上篇已經(jīng)把關(guān)于 Pimple 最主要的代碼分析了一下,這篇主要是關(guān)于 PSR-11 兼容性的分析。 PSR-11 服務(wù)容器接口 PSR PSR 是 PHP Standard Recommend...

    KunMinX 評論0 收藏0
  • Pimple相關(guān)的源碼

    摘要:已經(jīng)有了非常好的的相關(guān)解析,建議先看下一個(gè)簡單的依賴注入容器讀源碼筆記上讀源碼筆記下這里通過例子補(bǔ)充下核心方法的說明相關(guān)的類型服務(wù)類似單例工廠服務(wù)多個(gè)實(shí)例參數(shù)僅僅是保存一些變量保護(hù)參數(shù)匿名函數(shù)都會(huì)被認(rèn)為服務(wù),但是如果僅僅是想作為一個(gè) 已經(jīng)有了非常好的Pimple的相關(guān)解析,建議先看下:Pimple - 一個(gè)簡單的 PHP 依賴注入容器讀 PHP - Pimple 源碼筆記(上)讀 PH...

    MSchumi 評論0 收藏0
  • Pimple - 一個(gè)簡單的 PHP 依賴注入容器

    摘要:服務(wù)通過匿名函數(shù)定義,返回一個(gè)對象的實(shí)例定義一些服務(wù)請注意,匿名函數(shù)可以訪問當(dāng)前容器實(shí)例,從而允許引用其他服務(wù)或參數(shù)。如果要為所有調(diào)用返回不同的實(shí)例,請使用方法包裝你的匿名函數(shù)。 鏈接 官網(wǎng) WebSite GitHub - Pimple 這是 Pimple 3.x 的文檔。如果你正在使用 Pimple 1.x ,請查看 Pimple 1.x 文檔。閱讀 Pimple 1.x ...

    wemall 評論0 收藏0
  • PHP容器--Pimple運(yùn)行流程淺析

    摘要:實(shí)際上,閉包和匿名函數(shù)是偽裝成函數(shù)的對象。容器流程淺析是社區(qū)中比較流行的容器。服務(wù)提供者服務(wù)提供者是連接容器與具體功能實(shí)現(xiàn)類的橋梁。服務(wù)提供者需要實(shí)現(xiàn)接口所有服務(wù)提供者必須實(shí)現(xiàn)接口方法。但已經(jīng)完成了服務(wù)提供者的注冊工作。 需要具備的知識(shí)點(diǎn) 閉包 閉包和匿名函數(shù)在PHP5.3.0中引入的。 閉包是指:創(chuàng)建時(shí)封裝周圍狀態(tài)的函數(shù)。即使閉包所處的環(huán)境不存在了,閉包中封裝的狀態(tài)依然存在。 理論上...

    RobinTang 評論0 收藏0
  • Pimple Containter 容器使用實(shí)例代碼

    摘要:安裝代碼加載并實(shí)例化參數(shù)存儲(chǔ)單例存儲(chǔ)非單例存儲(chǔ)存儲(chǔ)匿名函數(shù)獲取匿名函數(shù)服務(wù)提供者文檔官網(wǎng)文檔 Pimple/Container 安裝 composer require pimple/pimple: ^3.0 代碼 加載并實(shí)例化 require __DIR__ . /vendor/autoload.php; $pc = new PimpleContainer(); 參數(shù)存儲(chǔ) $pc[ap...

    maochunguang 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<