性狀、生成器、閉包、OPcache【Modern PHP】
目錄
- 性狀 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】