1. 程式人生 > >性狀、生成器、閉包、OPcache【Modern PHP】

性狀、生成器、閉包、OPcache【Modern PHP】

執行 timestamp 保存 fclose start 參考 加載文件 路徑 當前

目錄

  • 性狀 Trait
  • 生成器
  • 閉包
  • Zend OPcache

PHP發展這麽多年,技術、架構都已經革新,了解現代PHP很重要,最近在看Model PHP這本書,系統的了解下PHP相關的概念。

性狀 Trait

是類的部分實現(即常量、屬性和方法),可以混入一個或多個現有的php類中。
性狀有兩個作用:表明類可以做什麽(接口);提供模塊化實現(像是類)。

比如說兩個無關的類需要擁有一個共同的方法,繼承、接口都不太合理(一是屬性不同;二是代碼重復),使用性狀可以共同使用某個方法。

舉例 汽車和快遞員都可能都需要查詢地理位置

trait Geocodable{
    protected $address;

    protected $geocoder

    //獲取經緯度
    public function getLatitude(){

    }

}

使用性狀:

<?php
Class Car{
    use Geocodable;

    //類的實現
    ...
}

生成器

生成器提供了一種更容易的方法來實現簡單的對象叠代,相比較定義類實現 Iterator 接口(正常叠代)的方式,性能開銷和復雜性大大降低。

生成器允許你在 foreach 代碼塊中寫代碼來叠代一組數據而不需要在內存中創建一個數組, 那會使你的內存達到上限,或者會占據可觀的處理時間。相反,你可以寫一個生成器函數,就像一個普通的自定義函數一樣, 和普通函數只返回一次不同的是, 生成器可以根據需要 yield 多次,以便生成需要叠代的值。

生成器根據需求計算出要叠代的值;及時計算並產出後續值,不占用寶貴的內存資源。

一個簡單的例子就是使用生成器來重新實現 range() 函數。 標準的 range() 函數需要在內存中生成一個數組包含每一個在它範圍內的值,然後返回該數組, 結果就是會產生多個很大的數組。 比如,調用 range(0, 1000000) 將導致內存占用超過 100 MB。

做為一種替代方法, 我們可以實現一個 xrange() 生成器, 只需要足夠的內存來創建 Iterator 對象並在內部跟蹤生成器的當前狀態,這樣只需要不到1K字節的內存。

Example #1 將 range() 實現為生成器

<?php
function xrange($start, $limit, $step = 1) {
    if ($start < $limit) {
        if ($step <= 0) {
            throw new LogicException('Step must be +ve');
        }

        for ($i = $start; $i <= $limit; $i += $step) {
            yield $i;
        }
    } else {
        if ($step >= 0) {
            throw new LogicException('Step must be -ve');
        }

        for ($i = $start; $i >= $limit; $i += $step) {
            yield $i;
        }
    }
}

/*
 * 註意下面range()和xrange()輸出的結果是一樣的。
 */

echo 'Single digit odd numbers from range():  ';
foreach (range(1, 9, 2) as $number) {
    echo "$number ";
}
echo "\n";

echo 'Single digit odd numbers from xrange(): ';
foreach (xrange(1, 9, 2) as $number) {
    echo "$number ";
}
?>

以上例程會輸出:

Single digit odd numbers from range():  1 3 5 7 9
Single digit odd numbers from xrange(): 1 3 5 7 9

想象下處理斐波那契數列或者叠代流資源,假設處理一個4GB的csv文件,而虛擬服務器只允許使用1GB內存,如果整個加載到內存中,服務器就崩了,而用生成器一次只會為csv一行分配內存。

<?php
    function getRows( $file ){
        $handle = fopen($file,'rb');
        if( $handle === false ){
            throw new Exception();
        }

        while(feof($handle) === false){
            yield fgetcsv($handle);
        }
        fclose($handle);
    }

    foreach($getRows('data.csv') as $row){
        print_r($row);
    }

生成器是功能多樣性和簡潔性之間的這種方案。生成器是只能前進的叠代器,無法執行快進,後退,查找操作。

參考鳥哥的文章:http://www.laruence.com/2015/05/28/3038.html

閉包

是指在創建時封裝周圍狀態的函數。
匿名函數其實就是沒有名稱的函數,特別適合作為函數或方法的調用。

  • _invoke 嘗試以函數方式調用一個對象時,該方法自動被調用。

創建簡單的閉包

$closure = function($name){
    return sprintf ('hello %s',$name);
}

echo $closure("Josh");
//輸出 --> hello Josh

作為回調

//匿名回調
$numbersPlusOne = array_map(function( $number ){
    return $number+1;
},[1,2,3]);
print_r($numbersPlusOne);
//輸出  --> [2,3,4]
//具名回調
function incrementNumber($number){
    return $number+1;
}

$numbersPlusone = array_map('incrementNumber',[1,2,3]);
print_r($numbersPlusOne);
//輸出  --> [2,3,4]
  • 附加狀態

PHP閉包附加並封裝狀態,手動調用閉包對象的bindTo()方法或者使用use關鍵字,把狀態附加到PHP閉包上。

實例:

<?php
function enclosePersion( $name ){
    return function($doCommand use $name){
        return sprintf('%s,%s',$name,$doCommand);
    }
}

//將字符串“sun”封裝到閉包中
$sun = enclosePersion( 'sun' );

//傳入參數,調用閉包
echo $sun('get me sweet tea!');
//輸出  --> "sun,get me sweet tea!"

PHP閉包是對象,與任何其他PHP對象類似,每個閉包實例都可以使用$this關鍵字獲取閉包的內部狀態。閉包有__invoke()、bindTo()方法;

  • [ ] 參考下手冊,手冊有詳細說明bindTo、bind;

<?php

 class  A  {
    function  __construct ( $val ) {
         $this -> val  =  $val ;
    }
    function  getClosure () {
         //returns closure bound to this object and scope
         return function() { return  $this -> val ; };
    }
}

 $ob1  = new  A ( 1 );
 $ob2  = new  A ( 2 );

 $cl  =  $ob1 -> getClosure ();
echo  $cl (),  "\n" ;
 $cl  =  $cl -> bindTo ( $ob2 );
echo  $cl (),  "\n" ;
 ?>


以上例程的輸出類似於:


1
2

Zend OPcache

從php 5.5.0開始,內置了字節碼緩存功能。
PHP執行過程:

graph LR
解析腳本代碼-->編譯成一系列Zend操作碼
編譯成一系列Zend操作碼-->執行字節碼

字節碼緩存 存儲好預先編譯好的PHP字節碼,PHP解釋器跳過讀取、解析、編譯PHP代碼,直接從內存中讀取預先編譯好的字節碼,然後執行。優點節省了時間,極大地提升了應用的性能。

  • 啟用Zend OPcache

編譯PHP時,明確指定啟用Zend OPcache。
執行./configure 命令時必須包含以下選項:

--enable-opcache

編譯成功會顯示Zend OPcache擴展的文件路徑。也可通過下述命令查找擴展的安裝路徑

php-config --extension-dir

php.ini文件中指定Zend OPcache擴展路徑,如:

zend_extension=/path/to/opcache.so

更新php.ini文件,重啟php進程。

<?php
    phpinfo()

查看Zend OPcache擴展開啟情況

技術分享圖片

  • 配置Zend OPcache
[opcache]
zend_extension=/usr/local/php5/lib/php/extensions/no-debug-non-zts-20131226/opcache.so

; Zend Optimizer + 的開關, 關閉時代碼不再優化.
opcache.enable=1

; Determines if Zend OPCache is enabled for the CLI version of PHP
opcache.enable_cli=1

; Zend Optimizer + 共享內存的大小, 總共能夠存儲多少預編譯的 PHP 代碼(單位:MB)
;根據內存來定
opcache.memory_consumption=256

; Zend Optimizer + 暫存池中字符串的占內存總量.(單位:MB)
; 推薦 8
opcache.interned_strings_buffer=4

; 最大緩存的文件數目 200  到 100000 之間
; 推薦 4000~8000
opcache.max_accelerated_files=8000

; 內存“浪費”達到此值對應的百分比,就會發起一個重啟調度.
opcache.max_wasted_percentage=5

; 開啟這條指令, Zend Optimizer + 會自動將當前工作目錄的名字追加到腳本鍵上,
; 以此消除同名文件間的鍵值命名沖突.關閉這條指令會提升性能,
; 但是會對已存在的應用造成破壞.
opcache.use_cwd=0

; 開啟文件時間戳驗證
opcache.validate_timestamps=1

; 2s檢查一次文件更新 註意:0是一直檢查不是關閉
; 推薦 60
opcache.revalidate_freq=0

; 允許或禁止在 include_path 中進行文件搜索的優化
;opcache.revalidate_path=0


; 是否保存文件/函數的註釋   如果apigen、Doctrine、 ZF2、 PHPUnit需要文件註釋
; 推薦 0
opcache.save_comments=1

; 是否加載文件/函數的註釋
;opcache.load_comments=1


; 打開快速關閉, 打開這個在PHP Request Shutdown的時候會收內存的速度會提高
; 推薦 1
opcache.fast_shutdown=1

;允許覆蓋文件存在(file_exists等)的優化特性。
;opcache.enable_file_override=0


; 定義啟動多少個優化過程
;opcache.optimization_level=0xffffffff


; 啟用此Hack可以暫時性的解決”can’t redeclare class”錯誤.
;opcache.inherited_hack=1

; 啟用此Hack可以暫時性的解決”can’t redeclare class”錯誤.
;opcache.dups_fix=0

; 設置不緩存的黑名單
; 不緩存指定目錄下cache_開頭的PHP文件. /png/www/example.com/public_html/cache/cache_
;opcache.blacklist_filename=


; 通過文件大小屏除大文件的緩存.默認情況下所有的文件都會被緩存.
;opcache.max_file_size=0

; 每 N 次請求檢查一次緩存校驗.默認值0表示檢查被禁用了.
; 由於計算校驗值有損性能,這個指令應當緊緊在開發調試的時候開啟.
;opcache.consistency_checks=0

; 從緩存不被訪問後,等待多久後(單位為秒)調度重啟
;opcache.force_restart_timeout=180

; 錯誤日誌文件名.留空表示使用標準錯誤輸出(stderr).
;opcache.error_log=/tmp/ckl.log

; 將錯誤信息寫入到服務器(Apache等)日誌
;opcache.log_verbosity_level=1

; 內存共享的首選後臺.留空則是讓系統選擇.
;opcache.preferred_memory_model=

; 防止共享內存在腳本執行期間被意外寫入, 僅用於內部調試.
;opcache.protect_memory=0

參考下大神的配置

opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=4
opcache.max_accelerated_files=8000
opcache.max_wasted_percentage=5
opcache.use_cwd=1
opcache.validate_timestamps=1
opcache.revalidate_freq=0
opcache.revalidate_path=0
opcache.save_comments=0
opcache.load_comments=0
opcache.force_restart_timeout=3600

性狀、生成器、閉包、OPcache【Modern PHP】