PHP閉包函數(shù)

一. 什么是閉包?

先看看百度百科的介紹:

閉包包含自由(未綁定到特定對象)變量,這些變量不是在這個代碼塊內或者任何全局上下文中定義的,而是在定義代碼塊的環(huán)境中定義(局部變量)柑营。
“閉包” 一詞來源于以下兩者的結合:要執(zhí)行的代碼塊(由于自由變量被包含在代碼塊中,這些自由變量以及它們引用的對象沒有被釋放)和為自由變量提供綁定的計算環(huán)境(作用域)村视。
在PHP官套、Scala、Scheme蚁孔、Common Lisp奶赔、Smalltalk、Groovy杠氢、JavaScript站刑、Ruby、 Python鼻百、Go绞旅、Lua摆尝、objective c、swift 以及Java(Java8及以上)等語言中都能找到對閉包不同程度的支持因悲。

說實話堕汞,這個介紹雖然專業(yè),但是有點僵硬不太容易理解晃琳,閉包是一種設計思想讯检,而不是一種語法特性,在PHP語言里面卫旱,匿名函數(shù)就是閉包的一種實現(xiàn)人灼。

二. 匿名函數(shù)

這個我相信大家都多多少少用過,看一下代碼:

$f = function () {
    $a = 1;
    $b = 2;
    return $a + $b;
};
var_dump($f);

輸出結果是:

class Closure#1 (0) {
}

可見顾翼,PHP中匿名函數(shù)就是閉包投放,也可以理解為閉包就是把這個函數(shù)賦值給一個變量,這時候這個變量保存的就是這個函數(shù)的內存地址适贸。

如何去調用這個閉包函數(shù)呢跪呈?很簡單,在這個例子里面只要 $f() 就可以了

var_dump($f()); #結果是:3

當然這個匿名函數(shù)也是可以傳參的取逾,你可以這樣寫:

$f = function ($c) {
    $a = 1;
    $b = 2;
    return $a + $b + $c;
};

這樣你在調用的時候就可以傳入?yún)?shù)耗绿,類似 $f(3), 但是有一點需要注意,如果這時候你想在定義閉包函數(shù)的時候使用外部變量砾隅,舉個例子

$out = 100;
$f = function ($c) {
    $a = 1;
    $b = 2;
    return $out - ($a + $b + $c); #報錯误阻,無法引用外部變量out
};

這時候就體現(xiàn)了閉包封閉的特性,但是PHP提供了一個 use 關鍵字晴埂,可以使用下面這個寫法:

$out = 100;
$f = function ($c) use ($out) {
    $a = 1;
    $b = 2;
    return $out - ($a + $b + $c);
};

三. 閉包到底有啥用究反?

一般來說還是在框架以及一些架構設計里面會用到,這里先舉2個小例子:

$arr = [1, 2, 3, 4, 5];

//使用array_reduce求和

function sum($arr)
{
    return array_reduce($arr, function ($x, $y) {
        return $x + $y;
    });
}

var_dump(sum($arr));

代碼里面使用了 array_reduce 這個函數(shù)求一個數(shù)組的和儒洛,但是精耐,如果不需要立刻求和,而是在后面的代碼中琅锻,根據(jù)需要再計算怎么辦卦停?可以不返回求和的結果,而是返回求和的函數(shù)恼蓬!

function lazySum($arr)
{
    return function () use ($arr) {
        return array_reduce($arr, function ($x, $y) {
            return $x + $y;
        });
    };
}
$sum = lazySum($arr);
var_dump($sum());

結果是一樣的惊完,雖然這種寫法有點奇怪


有一道面試題就涉及到了閉包的特性:

function plus()
{
    $funcArr = [];
    for ($i = 1; $i <= 3; $i++) {
        $funcArr[] = function () use (&$i) {
            return $i * $i;
        };
    }
    return $funcArr;
}

$res = plus();

var_dump($res[0]());
var_dump($res[1]());
var_dump($res[2]());

plus 函數(shù)會返回3個閉包函數(shù),然后依次調用這個幾個函數(shù), 有人以為結果可能是1,4,9处硬,其實結果都是16小槐,需要注意的是use 那里使用的是引用傳遞,這就意味著在生成這3個閉包函數(shù)的時候i的值并不是循環(huán)的時候的1,2,3荷辕,而且到最后 $i++ 之后的值也就是 4凿跳,這說明閉包函數(shù)是調用的時候才會執(zhí)行件豌。

四. 閉包在PHP框架里面使用

1.一個是IOC容器

<?php

/**
 * 閉包的使用IOC
 * Class Container
 */
class Container
{
    protected static $bindings;

    public static function bind(string $abstract, Closure $concrete)
    {
        static::$bindings[$abstract] = $concrete;
    }

    public static function make(string $abstract)
    {
        return call_user_func(static::$bindings[$abstract]);
    }
}


class talk
{
    public function greet($target)
    {
        echo "Hello " . $target->getName();
    }
}

class say
{
    public function getName()
    {
        return "World\n";
    }
}

$talk = new talk();

Container::bind('foo', function () {
    return new say();
});

$talk->greet(Container::make('foo'));

接觸過laravel框架的應該都見過這種寫法,laravel框架稱之為服務容器控嗜,其設計思想基本上就是這樣苟径,也就是在框架初始化的時候注冊綁定一堆服務,然后框架里面隨時就可以調用這些服務了躬审。

2.閉包路由

/**
 * 演示閉包的使用,路由
 * Class App
 */
class App
{
    protected $routes = [];
    protected $responseStatus = '200 OK';
    protected $responseContentType = 'text/html';
    protected $responseBody = 'Hello World';

    public function addRoute(string $path, Closure $callback)
    {
        $this->routes[$path] = $callback->bindTo($this, __CLASS__);
    }

    public function dispatch(string $path)
    {
        foreach ($this->routes as $routePath => $callback) {
            if ($routePath === $path) {
                $callback();
            }
        }
        header('HTTP/1.1 ' . $this->responseStatus);
        header('Content-Type: ' . $this->responseContentType);
        header('Content-Length: ' . mb_strlen($this->responseBody));
        echo $this->responseBody;
    }

}
require 'App.php';

$app = new App();

$app->addRoute("/", function () {
    $this->responseBody = "Hello Closure!\n";
});

$app->dispatch("/");
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蟆盐,隨后出現(xiàn)的幾起案子承边,更是在濱河造成了極大的恐慌,老刑警劉巖石挂,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件博助,死亡現(xiàn)場離奇詭異,居然都是意外死亡痹愚,警方通過查閱死者的電腦和手機富岳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拯腮,“玉大人窖式,你說我怎么就攤上這事《溃” “怎么了萝喘?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長琼懊。 經(jīng)常有香客問我阁簸,道長,這世上最難降的妖魔是什么哼丈? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任启妹,我火速辦了婚禮,結果婚禮上醉旦,老公的妹妹穿的比我還像新娘饶米。我一直安慰自己,他們只是感情好车胡,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布咙崎。 她就那樣靜靜地躺著,像睡著了一般吨拍。 火紅的嫁衣襯著肌膚如雪褪猛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天羹饰,我揣著相機與錄音伊滋,去河邊找鬼碳却。 笑死,一個胖子當著我的面吹牛笑旺,可吹牛的內容都是我干的昼浦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼筒主,長吁一口氣:“原來是場噩夢啊……” “哼关噪!你這毒婦竟也來了?” 一聲冷哼從身側響起乌妙,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤使兔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后藤韵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虐沥,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年泽艘,在試婚紗的時候發(fā)現(xiàn)自己被綠了欲险。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡匹涮,死狀恐怖天试,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情然低,我是刑警寧澤秋秤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站脚翘,受9級特大地震影響灼卢,放射性物質發(fā)生泄漏。R本人自食惡果不足惜来农,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一鞋真、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沃于,春花似錦涩咖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至咨演,卻和暖如春闸昨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工饵较, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留拍嵌,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓循诉,卻偏偏與公主長得像横辆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子茄猫,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內容