首頁  >  文章  >  後端開發  >  PHP使用Closure建立匿名函數的方法介紹

PHP使用Closure建立匿名函數的方法介紹

青灯夜游
青灯夜游轉載
2020-07-28 17:49:231844瀏覽

PHP使用Closure建立匿名函數的方法介紹

Closure 類別

#用來代表匿名函數的類別。

匿名函數(在 PHP 5.3 中被引入)會產生這個類型的物件。在過去,這個類別被認為是實作細節,但現在可以依賴它做一些事情。自 PHP 5.4 起,這個類別帶有一些方法,允許在匿名函數創建後對其進行更多的控制。

這個類別不能實例化,裡面主要有兩個方法,都用來複製閉包,一個靜態一個動態,下面分別詳細講解下這兩個不好理解的方法。

Closure::bind

public static Closure Closure::bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] )

参数说明:
closure
需要绑定的匿名函数。

newthis
需要绑定到匿名函数的对象,或者 NULL 创建未绑定的闭包。

newscope
想要绑定给闭包的类作用域,或者 'static' 表示不改变。如果传入一个对象,则使用这个对象的类型名。 类作用域用来决定在闭包中 $this 对象的 
私有、保护方法 的可见性。 The class scope to which associate the closure is to be associated, or 'static' to keep the 
current one. If an object is given, the type of the object will be used instead. This determines the visibility of 
protected and private methods of the bound object.

參數說明:

  • closure需要綁定的匿名函數。

  • newthis需要綁定到匿名函數的對象,或 NULL 建立未綁定的閉包。

  • newscope想要綁定給閉包的類別作用域,或是 'static' 表示不改變。如果傳入一個對象,則使用這個對象的型別名稱。類別作用域用來決定在閉包中 $this 物件的 私有、保護方法 的可見性。

The class scope to which associate the closure is to be associated, or 'static' to keep the 

current one. If an object is given, the type of the object will be used instead. This determines the visibility of 

protected and private methods of the bound object.

上面是該方法的定義,

第一個參數很好理解,就是一個閉包函數;

第二個參數就不太好理解,如果要複製的閉包中包含$this,這個物件就表示這個$this,閉包函數裡面對這個物件的修改在呼叫結束之後也會保持一致,例如修改了一個屬性;

第三個參數就不太好理解了,看官方的說明也是雲裡霧裡的,預設參數情況下,呼叫$this->訪問object $newthis中的屬性函數的時候,會有限制,只能存取public屬性的函數,如果想存取protected/private屬性,就要設定為對應的類別名稱/類別實例,就要像在類別裡面一樣,要存取那個類別的保護/私有屬性函數。

範例

<?php
class T {
    private function show()
    {
        echo "我是T里面的私有函数:show\n";
    }

    protected  function who()
    {
        echo "我是T里面的保护函数:who\n";
    }

    public function name()
    {
        echo "我是T里面的公共函数:name\n";
    }
}

$test = new T();

$func = Closure::bind(function(){
    $this->who();
    $this->name();
    $this->show();
}, $test);

$func();

上面的程式碼會報錯Fatal error: Uncaught Error: Call to protected method T::who() from  context 'Closure'。加上bind第三個參數為t::classnew T(),會正常輸出每一個結果。

我是T里面的保护函数:who
我是T里面的公共函数:name
我是T里面的私有函数:show

當然了,閉包也可以傳遞參數

$test = new StdClass();
var_dump($test);

$func = Closure::bind(function($obj){
    $obj->name = "燕睿涛";
}, null);

$func($test);
var_dump($test);

上面的程式跟匿名函數一樣,啥物件也沒有依賴,上面的程式會輸出:

object(stdClass)#1 (0) {
}
object(stdClass)#1 (1) {
  ["name"]=>
  string(9) "燕睿涛"
}

另外還有個特別要說明的例子

<?php
class T {
    private function show()
    {
        echo "我是T里面的私有函数:show\n";
    }

    protected  function who()
    {
        echo "我是T里面的保护函数:who\n";
    }

    public function name()
    {
        echo "我是T里面的公共函数:name\n";
    }
}

$func = Closure::bind(function ($obj) {
    $obj->show();
}, null);

$test = new T();

$func($test);

上面的情況會輸出什麼呢,沒錯,會報錯,提示訪問不了私有屬性show,這個時候,加上第三個參數就可以了,看了第三個參數不光影響$this的作用域,
也可以影響參數的作用域。

Closure::bindTo

#bindTobind功能類似,這裡只是另外一種形式,都是複製目前閉包對象,綁定指定的$this對象和類別作用域。 ,參數比bind少了第一個,
後面兩個一樣,當然還有一個差異就是bindTo不是靜態方法,是閉包才會存在的一個屬性方法。

範例

<?php
class T {
    private function show()
    {
        echo "我是T里面的私有函数:show\n";
    }

    protected  function who()
    {
        echo "我是T里面的保护函数:who\n";
    }

    public function name()
    {
        echo "我是T里面的公共函数:name\n";
    }
}

$func = function () {
    $this->show();
    $this->who();
    $this->name();
};

$funcNew = $func->bindTo(new T(), T::class);

$funcNew();

上面函數的輸出和bind的類似

我是T里面的私有函数:show
我是T里面的保护函数:who
我是T里面的公共函数:name

一個trick

這個函數是在看composer產生的自動載入原始碼的時候碰到的,在composer中用的比較特別,下面是截取部分composer中的程式碼

// 文件autoload_real.php
call_user_func(\Composer\Autoload\ComposerStaticInit898ad46cb49e20577400c63254121bac::getInitializer($loader));

// 文件autoload_static.php
public static function getInitializer(ClassLoader $loader)
{
    return \Closure::bind(function () use ($loader) {
        $loader->prefixLengthsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixLengthsPsr4;
        $loader->prefixDirsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixDirsPsr4;
        $loader->prefixesPsr0 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixesPsr0;
        $loader->classMap = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$classMap;

    }, null, ClassLoader::class);
}

上面的程式碼比較奇特,在call_user_func中,第一個感覺是傳錯參數了,其實不然,這裡呼叫了一個函數,這個函數會回傳一個Closure對象,
也就是一個匿名函數,最後傳入的參數還是一個callable型別。再看看這個回傳的閉包,裡面使用了use,這是連接閉包和外部變數的橋樑。
至於這裡為什麼普通傳參數就可以,是因為php5裡面,對象形參和實參數指向相同的對象,函數裡面對對象的修改會反映到對像外面。

所以,上面這麼做是沒問題的,還有另外一種形式也可以

call_user_func(\Composer\Autoload\ComposerStaticInit898ad46cb49e20577400c63254121bac::getInitializer(), $loader);

public static function getInitializer()
{
    return \Closure::bind(function ($loader) {
        $loader->prefixLengthsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixLengthsPsr4;
        $loader->prefixDirsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixDirsPsr4;
        $loader->prefixesPsr0 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixesPsr0;
        $loader->classMap = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$classMap;

    }, null, ClassLoader::class);
}

#總結

很久沒寫blog了,有時候太煩躁,靜不下心來,有時又有沒有找到想寫的東西。還是得靜下心來,好好做好每一件事,遇事情不要煩躁,心放大,心平氣和的處理每一件事。

相關教學推薦:《PHP教學

以上是PHP使用Closure建立匿名函數的方法介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除