<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                **閉包**就是能夠讀取其他函數內部變量的函數是指有權訪問另外一個函數作用域中的變量的函數。可以理解為(能夠讀取另一個函數作用域的變量的函數) 與其他不同的是php中的閉包就是匿名函數 閉包可以從父作用域中繼承變量。 任何此類變量都應該用*use*語言結構傳遞進去。 PHP 7.1 起,不能傳入此類變量:[superglobals](https://www.php.net/manual/zh/language.variables.predefined.php)、$this或者和參數重名。參看下面的例子: 將匿名函數放在普通函數中,也可以將匿名函數返回,這就構成了一個簡單的閉包 注意:理論上講,閉包和匿名函數是不同的概念. 不過,PHP將其視作相同的概念 ``` function demo(){ //父作用域 $name='dash'; //注意閉包函數一定要加;這是將匿名函數當作變量,PHP 會自動把此種表達式轉換成內置類 Closure 的對象實例。把一個 closure 對象賦值給一個變量的方式與普通變量賦值的語法是一樣的 $callback=function($param) use($name){//從父作用域中繼承$name變量 echo $name.$param; }; $callback('你好!'); } demo(); ``` **實現閉包的常見形態** ``` //例一 在函數里定義一個匿名函數,并且調用它 function demo() { $callback= function( $str ) { echo $str; }; $callback( 'some string' ); //直接執行閉包函數 } demo(); //例二 在函數中把匿名函數返回,并且調用它 function demo() { $callback= function( $str ) { echo $str; }; return $callback;//返回閉包函數 } $func= demo();//獲取閉包函數 $func( 'some string' );//執行閉包函數 //例三 把匿名函數當做參數傳遞,并且調用它 function demo( $func ) { $func( 'some string' ); } $callback = function( $str ) { echo $str; }; demo( $callback ); //也可以直接將匿名函數進行傳遞。如果你了解js,這種寫法可能會很熟悉 demo( function( $str ) { echo $str; } ); ``` **連接閉包和外界變量的關鍵字:USE(即繼承父作用域的變量)** ``` function getMoney() { $rmb = 1; $dollar = 6; $func = function() use ( $rmb ) { echo $rmb; echo $dollar; }; $func(); } getMoney();//輸出:1 報錯,找不到dorllar變量 ``` use所引用的也只不過是變量的一個副本 ``` function getMoney() { $rmb = 1; $func = function() use ( $rmb ) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney();//輸出:1 1 function getMoney() { $rmb = 1; $func = function() use ( &$rmb ) { echo $rmb; //把$rmb的值加1 $rmb++; }; $func(); echo $rmb; } getMoney();//輸出:1 2 //如果將匿名函數返回給外界,匿名函數會保存use所引用的變量,而外界則不能得到這些變量,這樣形成‘閉包'這個概念可能會更清晰一些。 function getMoneyFunc() { $rmb = 1; $func = function() use ( &$rmb ) { echo $rmb; //把$rmb的值加1 $rmb++; }; return $func; } $getMoney = getMoneyFunc();//外界則不能得到$rmb變量 $getMoney(); $getMoney(); $getMoney(); //輸出:1 2 3 ``` **經典案例:購物車類統計價格** ``` // 一個基本的購物車,包括一些已經添加的商品和每種商品的數量。 // 其中有一個方法用來計算購物車中所有商品的總價格,該方法使 // 用了一個 closure 作為回調函數。 class Cart { const PRICE_BUTTER = 1.00; const PRICE_MILK = 3.00; const PRICE_EGGS = 6.95; //商品集合 鍵為商品值為數量 protected $products = array(); //添加商品 public function add($product, $quantity) { $this->products[$product] = $quantity; } //獲取商品數量 public function getQuantity($product) { return isset($this->products[$product]) ? $this->products[$product] : FALSE; } //計算總價 public function getTotal($tax) { $total = 0.00; //constant()獲取常量的值 $callback = function ($quantity, $product) use ($tax, &$total) { $pricePerItem = constant(__CLASS__ . "::PRICE_" . strtoupper($product));//獲取當前商品價格 $total += ($pricePerItem * $quantity) * ($tax + 1.0); }; //array_walk($arr ,function($arr_value,$arr_key){});//對arr中每個array_value進行回調 array_walk($this->products, $callback); return round($total, 2); } } $my_cart = new Cart; // 往購物車里添加條目 $my_cart->add('butter', 1); $my_cart->add('milk', 3); $my_cart->add('eggs', 6); // 打出出總價格,其中有 5% 的銷售稅. print $my_cart->getTotal(0.05) . "\n"; // 最后結果是 54.29 ``` >[info]總結 PHP閉包的特性并沒有太大驚喜,其實用CLASS就可以實現類似甚至強大得多的功能,更不能和js的閉包相提并論,只能期待PHP以后對閉 包支持的改進。不過匿名函數`function(){}`還是挺有用的,比如在使用preg_replace_callback等之類的函數可以不用在外部聲明回調函數了。 ## **Closure類**: 匿名函數(在 PHP 5.3 中被引入)會產生這個類型的對象 自 PHP 5.4 起,這個類帶有一些方法,允許在匿名函數創建后對其進行更多的控制 ``` Closure { __construct ( void ) — 用于禁止實例化的構造函數 public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static'] — 復制一個閉包,綁定指定的$this對象和類作用域。 public Closure bindTo (object $newthis [, mixed $newscope = 'static' ]) — 復制當前閉包對象,綁定指定的$this對象和類作用域。 public call ( object $newthis [, mixed $... ] ) : mixed -- 綁定并調用閉包【臨時將對象scope綁定到閉包并調用它的簡寫方法】(php7) public static fromCallable ( callable $callable ) : Closure -- 將一個可調用的包轉換為一個閉包(php7?) } ``` 參數說明: * **closure**: 需要綁定的匿名函數。 * **newthis**: 需要綁定到匿名函數的對象,或者 NULL 創建未綁定的閉包(**給閉包綁定`$this`對象的**)。 參數為object還是null,取決于第一個參數閉包中是否用到了`$this`的上下文環境。(綁定的對象決定了函數中的$this的取值) 若閉包中用到了`$this`,則第2個參數不可為null,只能為object實例對象;若閉包中用到了靜態訪問(::操作符),第2個參數就可以為null,也可以為object **先要搞清楚類屬性的可訪問權限:** * **public**(在子類中可以通過self::var調用public方法或屬性,parent::method調用父類方法,在實例中可以能過$obj->var 來調用 public類型的方法或屬性) * **protect**(子類可訪問,類外不可以訪問; 在子類中可以通過self::var調用protected方法或屬性,parent::method調用父類方法在實例中不能通過$obj->var 來調用 protected類型的方法或屬性) * **private**(子類不可訪問 屬性或方法只能在該類中使用,在該類的實例、子類中、子類的實例中都不能) * 不能通過 $this 訪問靜態變量,靜態方法里面也不可用 $this在類外不能通過 類名::私有靜態變量,只能在類里面通過self,或者static 訪問私有靜態變量 * **newscope**: 想要綁定給閉包的類作用域,或者 'static' 表示不改變(**給閉包綁定作用域的**)。如果傳入一個對象,則使用這個對象的類型名。 類作用域用來決定在閉包中 $this 對象的 私有、保護方法 的可見性。如果閉包中訪問的是 private 屬性,就需要第3個參數提升閉包的訪問權限,若閉包中訪問的是public屬性,第三個參數可以不用。只有需要改變訪問權限時才要(備注:可以傳入類名或類的實例,默認值是 'static', 表示不改變。) 上面的例子的Closure只是全局的的匿名函數,那我們現在想指定一個類有一個匿名函數。也可以理解說,這個匿名函數的訪問范圍不再是全局的了,而是一個類的訪問范圍。 那么我們就需要將“一個匿名函數綁定到一個類中” *bind*是*bindTo*的靜態版本,因此只說*bind*吧 ``` class Person{ private $name = '王力宏'; protected $age = '30'; private static $weight = '70kg'; public $address = '中國'; public static $height = '180cm'; } $obj = new Person(); //echo $obj->name;//報錯 在類外部不能獲取私有屬性 //echo $obj->age;//報錯 在類外部不能獲取受保護的屬性 //echo Person::$weight; //報錯 在類外部不能獲取私有的靜態屬性 echo $obj->address;//中國 公共屬性 echo Person::$height;//180cm 公共靜態屬性 $fun = function(){ $obj = new Person(); //實例對象可以獲得公有屬性address, 但是$obj->name等私有屬性肯定不行 上面例子已列出報錯 return $obj->address; } echo $fun();//中國 $fun2 = function(){ // 類可以直接訪問公有靜態屬性,但Person::$weight肯定不行,因為weight為私有屬性 return Person::$height; } echo $fun2();//正常 輸出 180cm //下面獲取私有屬性試試 $fun = function(){ return $this->name; } //或者 $fun = function(){ return Person::$height; } echo $fun();//會報錯的 因為里面有個$this,程序壓根不知道你這個$this是代表那個對象 或 那個類 ``` 這里我們就需要給閉包綁定$this對象,將Person的對象綁定到閉包的$this ``` class Person{ public $address = '中國'; public static $height = '180cm'; protected $age = '30'; private $name = '王力宏'; private static $weight = '70kg'; } //public $fun1 = function(){ return $this->address; } ; //將閉包的$this綁定到Person $address = Closure::bind($fun1,new Person()); echo $address();//中國 //該函數返回一個全新的 Closure匿名的函數,和原來匿名函數$fun一模一樣,只是其中$this被指向了Person實例對象,這樣就能訪問address屬性了。 //public static $fun2 = function(){ //return static::$height; return Person::$height; } ; //給閉包綁定了Person類的作用域,以便閉包找到Person類 //靜態無this 所以不指定第二個參數 $height = Closure::bind($fun2,null,'Person'); echo $height();//中國 //protected或者private $fun3 = function(){ return $this->name.'<=>'.$this->age; } ; //將閉包的$this綁定到Person, name是私有屬性,需要第3個參數 同理age屬性也需要第三個屬性 $nameAndage = Closure::bind($fun3,new Person(),'Person'); echo $nameAndage();//王力宏<=>30 //private static $fun4 = function(){ //return self::$weight;//第三個參數不指定作用域,是找不到self指向的那個類的 return Person::$weight;//不綁定的話肯定報錯私有和保護的屬性在外部是不能調用的 } ; //給閉包綁定了Person類的作用域 //靜態無this 所以不指定第二個參數 $weight = Closure::bind($fun4,null,'Person'); echo $weight();//70kg ``` ### **總結:** 第二個參數為null則獲取不到閉包的$this **第二個參數可以是類實例或 null(默認)** 第三個參數null只能獲取public權限的屬性或者方法(獲取私有保護或靜態的需要設置第三個參數)**第三個參數可以是類實例,可以是類字符串,或 static(默認)** 綁定的幾種情況 1、只綁定$this對象. (public訪問權限的屬性或方法) 2、只綁定類作用域. (靜態的屬性或方法) 3、同時綁定$this對象和類作用域 (protected,private訪問權限的屬性或方法) 4、都不綁定.(這樣一來只是純粹的復制`Closure::bind($fun4,null)`與`clone $fun4`一樣, 文檔說法是使用cloning代替bind或bindTo) ## **BindTo與Bind差不多** 完全解除綁定使用`bindTo(null, null)` ``` class Person{ public $address = '中國'; public static $height = '180cm'; protected $age = '30'; private $name = '王力宏'; private static $weight = '70kg'; } //閉包返回的是Closure對象所以可以直接用bindTo $func = function ($name, $age) { $this->name = $name; $this->age = $age; return $this->name.'<=>'.$this->age; }; $fun5=$func ->bindTo(new Person,'Person'); echo $fun5('dash',30); //簡寫為 $fun5 = (function ($name, $age) { $this->name = $name; $this->age = $age; return $this->name.'<=>'.$this->age; })->bindTo(new Person,'Person'); echo $fun5('dash',30); //更近一步簡寫 (function ($name, $age) { $this->name = $name; $this->age = $age; return $this->name.'<=>'.$this->age; })->bindTo(new Person,'Person')('dash',30); ``` ``` class A { function __construct ( $val ) { $this -> val = $val ; } function getClosure () { //返回一個匿名函數,即Closure對象 return function() { return $this -> val ; }; } } $ob1 = new A ( 1 ); $ob2 = new A ( 2 ); $cl = $ob1 -> getClosure (); echo $cl ();//1 //綁定obj2對象到閉包 $c2 = $cl -> bindTo ( $ob2 ); echo $c2 ();//2 ``` 實現類似javascript的功能 ``` trait DynamicDefinition { //調用不存在的成員觸發 public function __call($name, $args) { if (is_callable($this->$name)) { return call_user_func_array($this->$name, $args); } else { throw new \RuntimeException("Method {$name} does not exist"); } } //設置不存在的成員觸發 public function __set($name,Closure $value) { $this->$name = is_callable($value)? $value->bindTo($this, $this): $value; } } class Foo { use DynamicDefinition; private $privateValue = 'I am private'; private $name='dash'; } $foo = new Foo; $foo->bar = function() { return $this->privateValue; }; print $foo->bar();//返回:I am private $foo->age = function($age){ return $this->name.'的年齡是'.$age; }; echo $foo->age(18);//dash的年齡是18 ``` 我們可以使用bindTo的概念來編寫一個非常小的模板引擎 ``` //############# //tpl.php //############ <h1><?php echo $this->title;?></h1> //############# //index.php //############ class Article{ private $title = "文章標題"; } class Post{ private $title = "帖子標題"; } class Template{ function render($context, $tpl){ $closure = function($tpl){ ob_start(); include $tpl; return ob_end_flush(); }; $closure = $closure->bindTo($context, $context); $closure($tpl); } } $art = new Article(); $post = new Post(); $template = new Template(); $template->render($art, 'tpl.php');//文章標題 $template->render($post, 'tpl.php');//帖子標題 ``` ## **php7新增call方法** ``` class Person { private $name = 1; } // PHP 7 之前版本的代碼 $getClosure = function() { return $this->name; }; $getName = $getClosure->bindTo(new Person(), 'Person'); // 中間層閉包 echo $getName(); // PHP 7+ 及更高版本的代碼 $getName = function() { return $this->name; }; echo $getName->call(new Person); ``` ## **php7.1新增Closure::fromCallable() 將callables轉為閉包** Closure新增了一個靜態方法,用于將callable快速地 轉為一個Closure 對象。 ``` class Test { public function exposeFunction() { return Closure::fromCallable([$this, 'privateFunction']); } private function privateFunction($param) { var_dump($param); } } //((new Test)->exposeFunction())('some value'); $privFunc = (new Test)->exposeFunction(); $privFunc('some value');//some value ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看