前段時間,公司的項目從PHP5.3升級到PHP7,如今項目裏開始使用PHP7的一些新語法和特性。反觀PHP的5.四、5.五、5.6版本,有點認知缺失的感受。因此,決定看《Modern PHP》補一補裏面的一些概念。php
命名空間用的比較多,不詳細寫了,記錄幾個值得注意的實踐和細節。
多重導入
別這麼作,這樣寫容易讓人困惑。html
<?php use Symfony\Component\HttpFoundation\Request, Symfony\Component\HttpFoundation\Response, Symfony\Component\HttpFoundation\Cookie;
建議一行寫一個use語句:前端
<?php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Cookie;
一個文件中使用多個命名空間
你能夠這麼作,但這違背了「一個文件定義一個類」的良好實踐。nginx
<?php namespace Foo { //code } namespace Bar { //code }
全局命名空間
想要使用PHP原生的Exception類,須要在類名前加 \ 符號。laravel
<?php namespace My\App; class Foo { public function doSomething() { $exception = new \Exception(); } }
若是Exception前不加 \ 符號,會在My\App命名空間下尋找Exception類。web
使用接口編寫的代碼更靈活,能委託其餘人實現細節。使用的人只須要關心有什麼接口,而不須要關心實現。可以很好地解耦代碼,方便擴展,比較經常使用就不說啦。正則表達式
在學習laravel框架以前都沒弄清楚性狀(trait)。這是PHP5.4.0引入的新概念,既像類又像接口。但它兩個都不是。算法
性狀是類的部分實現,能夠混入一個或多個現有PHP類中。相似Ruby的組合模塊活混入(mixin)。sql
爲何使用性狀
舉個具體的例子,好比有兩個類,Car 和 Phone,他們都須要GPS功能。爲了解決這個問題,第一反應建立一個父類,而後讓Car和Phone繼承它。但由於很明顯,這個祖先不屬於各自的繼承層次結構。數據庫
第二反應建立一個GPS的接口,定義好GPS的功能接口,而後讓Car和Phone兩個類都實現這個接口。這樣作能實現功能,同時也能保持天然的繼承層級結構。不過,這就使得在兩個都要實現重複的GPS功能,這不符合DRY(dont repeat yourself)原則。
第三反應建立實現GPS功能的性狀(trait),而後在Car和Phone類中混入這個性狀。能實現功能,不影響繼承結構,不重複實現,完美。
建立與使用性狀
建立trait
<?php trait MyTrait{ //實現 }
使用trait
<?php class MyClass { use MyTrait; // 類的實現 }
PHP生成器(generator)是PHP5.5.0引入的新功能,不少PHP開發者生成器不瞭解。生成器是個簡單的迭代器,但生成器不要求實現Iterator接口。生成器會根據須要計算併產生要迭代的值。若是不查詢,生成器永遠不知道下一個要迭代的值是什麼,在生成器中沒法後退或快進。具體看以下兩個例子:
簡單的生成器
<?php function makeRange($length) { for ($i = 0; $i < $length; $i++) { yield $i; } } foreach (makeRange(1000000) as $i) { echo $i, PHP_EOL; }
具體場景:使用生成器處理CSV文件
<?php function getRows($file) { $handle = fopen($file, 'rb'); if ($handle === false) { throw new Exception(); } while (feof($handle) === false) { yield fgetcsv($handle); } } foreach (getRows('data.csv') as $row) { print_r($row); }
處理這種場景,習慣的處理方法是先讀取文件的全部內容放到數組中,而後再作處理等等。這種的處理存在的問題是:當文件特別大,一次讀取就佔用不少內存資源。而生成器最適合這種場景,由於這樣佔用的系統內存量極少。
理論上,閉包和匿名函數是不一樣的概念。不過,PHP將其視做相同的概念。
簡單閉包
<?php $closure = function ($name) { return sprintf('Hello %s', $name); } echo $closure("Beck"); // 輸出 --> 「Hello Beck」
注意:咱們之因此能調用$closure變量,是由於這個變量的值是個閉包,並且閉包對象實現了__invoke()魔術方法。只要變量名後有(),PHP就會查找並調用__invoke()方法。
附加狀態
使用use關鍵字能夠把多個參數傳入閉包,此時要像PHP函數或方法的參數同樣,使用逗號分隔多個參數。
<?php function enclosePerson($name) { return function ($doCommand) use ($name) { return sprintf('%s, %s', $name, $doCommand); }; } // 把字符串「Clay」封裝在閉包中 $clay = enclosePerson('Clay'); // 傳入參數,調用閉包 echo $clay('get me sweet tea!'); // 輸出 --> "Clay, get me sweet tea!"
使用bindTo()方法附加閉包的狀態
PHP框架常用bindTo()方法把路由URL映射到匿名回調函數上,框架會把匿名函數綁定到應用對象上,這麼作能夠在這個匿名函數中使用$this關鍵字引用重要的應用對象。例子以下:
<?php
class App
{
protected $routes = array();
protected $responseStatus = '200 OK';
protected $responseContentType = 'text/html';
protected $responseBody = 'Hello world';
public function addRoute($routePath, $routeCallback)
{
$this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);//重點
}
public function dispatch($currentPath)
{
foreach ($this->routes as $routePath => $callback) {
if ($routePath === $currentPath) {
$callback();
}
}
header('HTTP/1.1' . $this->responseStatus);
header('Content-type:' . $this->responseContentType);
header('Content-length' . mb_strlen($this->responseBody));
echo $this->responseBody;
}
}
第11行是重點所在,把路由回調綁定到了當前的App實例上。這麼作能在回調函數中處理App實例的狀態:
<?php $app = new App(); $app->addRoute('/users/josh', function () { $this->responseContentType = 'application/json;charset=utf8'; $this->responseBody = '{"name": "Josh"}'; }); $app->dispatch('/users/josh');
字節碼緩存不是PHP的新特性,不少獨立的擴展能夠實現緩存。從PHP5.5.0開始,PHP內置了字節碼緩存功能,名爲Zend OPcache。
字節碼緩存是什麼
PHP是解釋性語言,PHP解釋器執行PHP腳本時會解析PHP腳本代碼,把PHP代碼編譯成一系列Zend操做碼,而後執行字節碼。每次請求PHP文件都是這樣,會消耗不少資源。字節碼緩存能存儲預先編譯好的PHP字節碼。這意味着,請求PHP腳本時,PHP解釋器不用每次都讀取、解析和編譯PHP代碼。這樣能極大地提高應用的性能。
從PHP5.4.0起,PHP內置了Web服務器,這對衆多使用Apache或nginx的php開發者來講,多是個隱藏功能。不過,這個內置的服務器功能並不完善,不該該在生產環境中使用,但對本地開發來講是個便利的工具,能夠用於快速預覽一些框架和應用。
啓動服務器
php -S localhost:4000
配置服務器
php -S localhost:8000 -c app/config/php.ini
路由器腳本
與Apache和nginx不一樣,它不支持.htaccess文件。所以,這個服務器很難使用多數流行的PHP框架中常見的前端控制器。PHP內置的服務器使用路由器腳本彌補了這個遺漏的功能。處理每一個HTTP請求前,會先通過這個路由器腳本,若是結果爲false,返回當前HTTP請求中引用的靜態資源URI。
php -S localhost:8000 route.php
是否爲內置的服務器
<?php if (php_sapi_name() === 'cli-server') { // php 內置的web服務器 }
PHP組件和框架的數量不少,隨之產生的問題就是:單獨開發的框架沒有考慮到與其餘框架的通訊。這樣對開發者和框架自己都是不利的。
打破舊局面的PHP-FIG
多位PHP框架的開發者認識到了這個問題,在2009年的 php|tek(一個受歡迎的PHP會議)上談論了這個問題。通過討論後得出:咱們須要一個標準,用來提升框架的互操做性。因而這幾位在php|tek意外碰頭的PHP框架開發者組織了PHP Framework Interop Group,簡稱PHP-FIG。
PHP-FIG是框架表明自發組織的,其成員不是選舉產生的,任何人均可以申請加入PHP-FIG,而且能對處於提議階段的推薦規範提交反饋。另外,PHP-FIG發佈的是推薦規範,而不是強制規定。
PSR是PHP Standards Recommendation(PHP推薦標準)的簡稱。截至今日,PHP-FIG發佈了五個推薦規範:
你會發現只有四個,沒錯,由於第一份推薦規範PSR-0廢棄了,新發布的PSR-4替代了。
若是想編寫符合社區標準的PHP代碼,首先要遵照PSR-1。遵照這個標準很是簡單,可能你已經再使用了。標準的細節就不寫啦,點連接就能看。
PSR-2是在PSR-1的基礎上更進一步的定義PHP代碼規範。這個標準解決了不少世紀問題哈,好比縮進,大括號等等。細節也很少記錄啦。
另外,如今不少IDE(好比,PHPStorm)會有代碼格式化功能,設置代碼格式化的標準,編寫完代碼,而後所有格式化,能夠幫助你遵循推薦規範,修復一些換行、縮進、大括號等細節。
這個推薦規範與前兩個不一樣,這是一個接口,規定PHP日誌記錄器組件能夠實現的方法。符合PSR-3推薦規範的PHP日誌記錄器組件,必須包含一個實現Psr\Log\LoggerInterface接口的PHP類。PSR-3接口複用了RFC 5424系統日誌協議,規定要實現的九個方法:
<?php namespace Psr\Log; interface LoggerInterface { public function emergency($message, array $context = array()); public function alert($message, array $context = array()); public function critical($message, array $context = array()); public function error($message, array $context = array()); public function warning($message, array $context = array()); public function notice($message, array $context = array()); public function info($message, array $context = array()); public function debug($message, array $context = array()); public function log($level, $message, array $context = array()); }
每一個方法對應RFC 5424協議的一個日誌級別。
使用PRS-3日誌記錄器
若是你正在編寫本身的PSR-3日誌記錄器,能夠停下來了。由於已經有一些十分出色的日誌記錄器組件。好比:monolog/monolog,直接用就能夠了。若是不能知足要求,也建議在此基礎上作擴展。
這個推薦規範描述了一個標準的自動加載器策略。自動加載器策略是指,在運行時按需查找PHP類,接口或性狀,並將其載入PHP解釋器。
爲何自動加載很重要
在PHP文件的頂部你是否是常常看到相似下面的代碼?
<?php include 'path/to/file1.php'; include 'path/to/file2.php'; include 'path/to/file3.php';
若是隻需載入幾個PHP腳本,使用這些函數(include()、include_once()、require()、require_once())能很好的完成工做。但是若是你要引入一千個PHP腳本呢?
在PSR-4推薦規範以前,PHP組件和框架的做者使用__autoload()和spl_autoload_register()函數註冊自定義的自動加載器策略。但是,每一個PHP組件和框架的自動加載器都使用獨特的自動加載器。所以,使用的組件多的時候,也是很麻煩的事情。
推薦使用PSR-4自動加載器規範,就是解決這個問題,促進組件實現互操做性。
PSR-4自動加載器策略
PSR-4推薦規範不要求改變代碼的實現方式,只建議如何使用文件系統目錄結構和PHP命名空間組織代碼。PSR-4的精髓是把命名空間的前綴和文件系統中的目錄對應起來。好比,我能夠告訴PHP,\Oreilly\ModernPHP命名空間中的類、接口和性狀在物理文件系統的src/目錄中,這樣PHP就知道,前綴爲\Oreilly\ModernPHP的命名空間中的類、接口和性狀對應的src/目錄裏的目錄和文件。
如何編寫PSR-4自動加載器
若是你在寫本身的PSR-4自動加載器,請停下來。咱們可使用依賴管理器Composer自動生成的PSR-4自動加載器。
使用htmlentities()函數過濾輸入。
<?php $input = '<p><script>alert("You won the Nigerian lottery!");</script></p>'; echo htmlentities($input, ENT_QUOTES, 'UTF-8');
須要注意的是:默認狀況下,htmlentities()函數不會轉義單引號,並且也檢測不出輸入字符串的字符集。正確的使用方式是:第一個參數輸入字符串;第二個參數設爲ENT_QUOTES常量,轉移單引號;第三個參數設爲輸入字符串的字符集。
更多過濾HTML輸入的方式,可使用HTML Purifier庫。這個庫強健且安全,缺點:慢,且可能難以配置。
構建SQL查詢很差的方式:
$sql = sprintf( 'UPDATE users SET password = "%s" WHERE id = %s', $_POST['password'], $_GET['id'] );
若是 psasword=abc";--
,則致使修改了整個users表的記錄password都未abc。若是須要在SQL查詢中使用輸入數據,要使用PDO預處理語句。
A.過濾用戶資料中的電子郵件地址
這裏會刪除除字符、數字和!#$%&'*+-/=?^_{|}~@.[]`以外的全部其餘符號。
<?php $email = 'beckjiang@meijiabang.cn'; $emailSafe = filter_var($email, FILTER_SANITIZE_EMAIL);
B.過濾用戶資料中的外國字符
<?php $string = "外國字符"; $safeString = filter_var( $string, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW|FILTER_FLAG_ENCODE_HIGH );
驗證數據與過濾不一樣,驗證不會從輸入數據中刪除信息,而是隻確認輸入數據是否符合預期。
驗證電子郵件地址
咱們能夠把某個FILTER_VALIDATE_*標誌傳給filter_var()函數,除了電子郵件地址,還能夠驗證布爾值、浮點數、整數、IP地址、正則表達式和URL。
<?php $input = 'beckjiang@meijiabang.cn'; $isEmail = filter_var($input, FILTER_VALIDAE_EMAIL); if ($isEmail !== false) { echo "Success"; } else { echo "Fail"; }
哈希算法有不少種,例如:MD五、SHA一、bcrypt和scrypt。有些算法的速度很快,用於驗證數據完整性;有些算法速度則很慢,旨在提升安全性。生成密碼和存儲密碼時須要使用速度慢、安全性高的算法。
目前,經同行審查,最安全的哈希算法是bcrypt。與MD5和SHA1不一樣,bcrypt是故意設計的很慢。bcrypt算法會自動加鹽,防止潛在的彩虹表攻擊。bcrypt算法永不過期,若是計算機的運算速度變快了,咱們只需提升工做因子的值。
從新計算密碼的哈希值
下面是登陸用戶的腳本:
<?php session_start(); try { // 從請求主體中獲取電子郵件地址 $email = filter_input(INPUT_POST, 'email'); // 從請求主體中獲取密碼 $password = filter_input(INPUT_POST, 'password'); // 使用電子郵件地址獲取用戶(注意,這是虛構代碼) $user = User::findByEmail($email); // 驗證密碼和帳戶的密碼哈希值是否匹配 if (password_verify($password, $user->password_hash) === false) { throw new Exception('Invalid password'); } // 若是須要,從新計算密碼的哈希值 $currentHashAlgorithm = PASSWORD_DEFAULT; $currentHashOptions = array('cost' => 15); $passwordNeedRehash = password_needs_rehash( $user->password_hash, $currentHashAlgorithm, $currentHashOptions ); if ($passwordNeedsRehash === true) { // 保存新計算得出的密碼哈希值(注意,這是虛構代碼) $user->password_hash = password_hash( $password, $currentHashAlgorithm, $currentHashOptions ); $user->save(); } // 把登陸狀態保存到回話中 ... // 重定向到我的資料頁面 ... } catch (Exception $e) { //異常處理 ... }
值得注意的是:在登陸前,必定要使用password_needs_rehash()函數檢查用戶記錄中現有的密碼哈希值是否過時。若是過時了,要從新計算密碼哈希值。
PHP5.5.0以前的密碼哈希API
若是沒法使用PHP5.5.0或以上版本,可使用安東尼·費拉拉開發的ircmaxell/password-compat組件。這個組件實現了PHP密碼哈希API中的全部函數:
DateTime類提供一個面向對象接口,用於管理日期和時間。
沒有參數,建立的是一個表示當前日期和時間的實例:
<?php $datetime = new DateTime();
傳入參數建立實例:
<?php $datetime = new DateTime('2017-01-28 15:27');
指定格式,靜態構造:
<?php $datetime = DateTime::createFromFormat('M j, Y H:i:s', 'Jan 2, 2017 15:27:30');
DateInterval實例表示長度固定的時間段(好比,「兩天」),或者相對而言的時間段(好比,「昨天」)。DateInterval實例用於修改DateTime實例。
使用DateInterval類:
<?php // 建立DateTime實例 $datetime = new DateTime(); // 建立長度爲兩週的間隔 $interval = new DateInterval('P2W'); // 修改DateTime實例 $datetime->add($interval); echo $datetime->format('Y-m-d H:i:s'); 建立反向的DateInterval實例: <?php // 過去一天 $interval = new DateInterval('-1 day');
若是應用要迎合國際客戶,可能要和時區鬥爭。
建立、使用時區:
<?php $timezone = new DateTimeZone('America/New_York'); $datetime = new DateTime('2017-01-28', $timezone);
實例化以後,也可使用setTimeZone()
函數設置市區:
$datetime->setTimeZone(new DateTimeZone('Asia/Hong_Kong'));
有時咱們須要迭代處理一段時間內反覆出現的一系列日期和時間,DatePeriod類能夠解決這種問題。DatePeriod類的構造方法接受三個參數,並且都必須提供:
DatePeriod實例是迭代器,每次迭代時都會產出一個DateTime實例。
使用DatePeriod類:
<?php $start = new DateTime(); $interval = new DateInterval('P2W'); $period = new DatePeriod($start, $interval, 3); foreach ($period as $nextDateTime) { echo $nextDateTime->format('Y-m-d H:i:s'), PHP_EOL; }
PHP應用能夠在不少種數據庫中持久保存信息,好比:MySQL、SQLite、Oracle等。若是在項目中使用多種數據庫,須要安裝並學習多種PHP數據庫擴展和接口,這增長了認知和技術負擔。
正是基於這個緣由,PHP原生提供了PDO擴展(PHP Data Objects,意思是PHP數據對象),PDO是一系列PHP類,抽象了不一樣數據庫的具體實現。PDO的介紹和使用就不寫了,比較經常使用。
在現代的PHP特性中,流或許是最出色但最少使用的。雖然PHP4.3.0就引入了流,但不少開發者不知道流的存在,由於不多人說起流,並且流的文檔也匱乏。官方的解釋比較難理解,一句話說就是:流的做用是在出發地和目的地之間傳輸數據。
我把流理解爲管道,至關於把水從一個地方引到另外一個地方。在水從出發地流到目的地的過程當中,咱們能夠過濾水,能夠改變水質,能夠添加水,也能夠排出水(提示:水是數據的隱喻)。
流式數據的種類各異,每種類型須要獨特的協議,以便讀寫數據。咱們稱這些協議爲流封裝協議。好比,咱們能夠讀寫文件系統,能夠經過HTTP、HTTPS或SSH與遠程Web服務器通訊,還能夠打開並讀寫ZIP、RAR或PHAR壓縮文件。這些通訊方式都包含下述相同的過程:
雖然過程同樣的,可是讀寫文件系統中文件的方式與手法HTTP消息的方式有所不一樣。流封裝協議的做用是使用通用的幾口封裝這些差別。
每一個流都有一個協議和一個目標。格式以下:
<scheme>://<target>
說這麼多有點懵,先看例子,使用HTTP流封裝協議與Flickr API通訊:
<?php $json = file_get_contents( 'http://api.flickr.com/services/feeds/photos_public.gne?format=json' );
不要誤覺得這是普通的網頁URL,file_get_contents()函數的字符串參數實際上是一個流標識符。http協議會讓PHP使用HTTP流封裝協議。看起來像是普通的網頁URL,是由於HTTP流封裝協議就是這樣規定的:)。其餘流封裝協議可能不是這樣。
咱們使用file_get_contents()
,fopen()
,fwrite()
和fclose()
函數讀寫文件系統。由於PHP默認使用的流封裝協議是file://
,因此咱們不多認爲這些函數使用的是PHP流。
隱式使用file://
流封裝協議:
<?php $handle = fopen('/etc/hosts', 'rb'); while (feof($handle) !== true) { echo fgets($handle); } fclose($handle);
顯式使用file://
流封裝協議:
<?php $handle = fopen('file:///etc/hosts', 'rb'); while (feof($handle) !== true) { echo fgets($handle); } fclose($handle);
有些PHP流能接受一些列可選的參數,這些參數叫流上下文,用於定製流的行爲。流上下文使用stream_context_create()
函數建立。
好比,你知道可使用file_get_contents()
函數發送HTTP POST請求嗎?若是想這麼作,可使用一個流上下文對象:
<?php $requestBody = '{"username": "beck"}'; $context = stream_context_create(array( 'http' => array( 'method' => 'POST', 'header' => "Content-Type: application/json;charset=utf-8;\r\n" . "Content-Length: " . mb_strlen($requestBody), "content" => $requestBody ) )); $response = file_get_contents('https://my-api.com/users', false, $context);
關於PHP的流,其實真正強大的地方在於過濾、轉換、添加或刪除流中傳輸的數據。
注意:PHP內置了幾個流過濾器:string.rot1三、string.toupper、string.tolower和string.strp_tags。這些過濾器沒什麼用,咱們要使用自定義的過濾器。
若想把過濾器附加到現有的流上,要使用stream_filter_append()
函數。好比,想要把文件中的內容轉換成大寫字母,可使用string.toupper過濾器。書中不建議使用這個過濾器,這裏只是演示如何把過濾器附加到流上:
<?php $handle = fopen('data.txt', 'rb'); stream_filter_append($handle, 'string.toupper'); while (feof($handle) !== true) { echo fgets($handle); // <-- 輸出的全是大寫字母 } fclose($handle);
使用php://filter
流封裝協議把過濾器附加到流上:
<?php $handle = fopen('php://filter/read=string.toupper/resource=data.txt', 'rb'); while (feof($handle) !== true) { echo fgets($handle); // <-- 輸出的全是大寫字母 } fclose($handle);
來看個更實際的流過濾器示例,假如咱們nginx訪問日誌保存在rsync.net,一天的訪問狀況保存在一個日誌文件中,並且會使用bzip2壓縮每一個日誌文件,名稱格式爲:YYYY-MM-DD.log.bz2。某天,領導讓我提取過去30天某個域名的訪問數據。使用DateTime類和流過濾器迭代bzip壓縮的日誌文件:
<?php $dateStart = new \DateTime(); $dateInterval = \DateInterval::createFromDateString('-1 day'); $datePeriod = new \DatePeriod($dateStart, $dateInterval, 30);//建立迭代器 foreach ($datePeriod as $date) { $file = 'sftp://USER:PASS@rsync.net/' . $date->format('Y-m-d') . 'log.bz2'; if (file_exists($file)) { $handle = fopen($file, 'rb'); stream_filter_append($handle, 'bzip2.decompress'); while (feof($handle) !== true) { $line = fgets($handle); if (strpos($line, 'www.example.com') !== false) { fwrite(STDOUT, $line); } } fclose($handle); } }
計算日期範圍,肯定日誌文件的名稱,經過FTP鏈接rsync.net,下載文件,解壓縮文件,逐行迭代每一個文件,把相應的行提取出來,而後把訪問數據寫入一個輸出目標。使用PHP流,不到20行代碼就能作完全部這些事情。
其實大多數狀況下都要使用自定義的流過濾器。自定義的流過濾器是個PHP類,繼承內置的php_user_filter類。這個類必須實現filter()
、onCreate()
和onClose()
方法。並且,必須使用stream_filter_register()
函數註冊自定義的流過濾器。
PHP流會把數據分紅按次序排列的桶,一個桶中盛放的流數據量是固定的。必定時間內過濾器接收到的桶叫作桶隊列。桶隊列中的每一個桶對象都有兩個公開屬性:data和datalen,分別是桶中的內容和內容的長度。
下面定義一個處理髒字的流過濾器:
<?php class DirtyWordsFilter extends php_user_filter { /** * @param resource $in 流來的桶隊列 * @param resource $out 流走的桶隊列 * @param resource $consumed 處理的字節數 * @param resource $closing 是流中最後一個桶隊列嗎? */ public function filter() { $words = array('grime', 'dirt', 'grease'); $wordData = array(); foreach ($words as $word) { $replacement = array_fill(0, mb_strlen($word), '*'); $wordData[$word] = implode(' ', $replacement); } $bad = array_keys($wordData); $goods = array_values($wordData); // 迭代流來的桶隊列中的每一個桶 while ($bucket = stream_bucket_make_writeable($in)) { // 審查桶數據中的髒字 $bucket->data = str_replace($bad, $goods, $bucket->data); // 增長已處理的數據量 $consumed += $bucket->datalen; // 把桶放入流向下游的隊列中 stream_bucket_append($out, $bucket); } return PSFS_PASS_ON; } }
filter()
方法的做用是接受、處理再轉運桶中的流數據。這個方法的返回值是PSFS_PASS_ON
常量,表示操做成功。
註冊流過濾器
接着,咱們必須使用stream_filter_register()
函數註冊這個自定義的DirtWordsFilter流過濾器:
<?php stream_filter_register('dirty_words_filter', 'DirtWordsFilter');
第一個參數是用於識別這個自定義過濾器的過濾器名,第二個參數是自定義過濾器的類名。
使用DirtWordsFilter流過濾器
<?php $handle = fopen('data.txt', 'rb'); stream_filter_append($handle, 'dirty_words_filter'); while (feof($handle) !== true) { echo fgets($handle); // <-- 輸出審查後的文本 } fclose($handle);
對錯誤和異常的處理,必定要遵照四個規則:
錯誤與異常在平常使用的比較多,就不記錄啦!