PHP之道

jopen 12年前發布 | 112K 次閱讀 PHP PHP開發

Welcome

目前網絡上充斥著大量的陳舊信息,讓PHP新手誤入歧途,傳播著錯誤的實踐和糟糕的代碼,這必須得到糾正。PHP之道網站的 目標就是搜集PHP最佳實踐、編碼規范和網絡上的權威學習指南,給PHP學習者提供一個易于閱讀,快速查找的入口。

翻譯

PHP之道已經翻譯成多種語言:

免責聲明

PHP沒有規范化的使用方式,本網站只是展示業界的最佳實踐、可用的選項和有用的信息,目的是幫助PHP新手,并對以往的經驗進行反思。

本文檔會隨著相關技術的發展,持續更新其中的信息和示例。

如何參與網站建設

參與我們,讓這個網站成為PHP開發者的最好學習資源!去GitHub參與建設網站

推廣網站

PHP之道 有多個banner宣傳圖片可以放在你的站點上顯示,讓更多開發者知道這個網站,找到權威的學習資料!

查看banner圖片

Back to Top

Getting Started

使用當前穩定版本 (5.4)

如果你剛開始學習PHP,請使用最新穩定版本 PHP 5.4. PHP近年來有了巨大的改進,增加了許多強大的 新特性。不要讓低版本的PHP如5.2的缺陷誤導你,這些新特性是對舊版本的重要改進。如果 你想查找一個函數及其用法,可以去官方PHP手冊php.net查找。

內置的Web服務器

有了它,你可以不用安裝和配置功能齊全的Web服務器,就可以開始學習PHP(要求PHP 5.4版本)。要啟動內置Web服務器,需要從你的命令行終端進入項目的Web根目錄,執行下面的命令:

> php -S localhost:8000

Mac 安裝

OSX系統會預裝PHP,只是版本比最新穩定版低一點。 目前Lion下是PHP 5.3.6,Mountain Lion下是5.3.10.

要更新OSX中的PHP,你可以通過那些Mac包管理器來安裝,推薦使用php-osx by Liip

另外一種方式是自己編譯,不過要確認已經安裝Xcode或“Command Line Tools for Xcode”,它們可以 從Apple的Mac Developer Center下載。

如果想安裝包含了PHP、Apache和MySQL的一鍵安裝包,可以試試MAMP,里面包含了相應的圖形管理工具。

Windows 安裝

Windows下有多種方式來安裝PHP,你可以下載二進制安裝包

若只是本地開發和學習,可以直接使用PHP 5.4內置的Web服務器,還能省去配置服務器的麻煩。如果你喜歡包含PHP、Apache和MySQL的 一鍵安裝包,可以下載Web Platform InstallerZend Server CEXAMPPWAMP,它們可以幫你快速搭建出PHP運行環境。 不過這些工具和你產品的正式運行環境會有一些差別,特別是你在Windows下開發,而代碼最終部署在Linux服務器上的時候。

如果你需要把產品部署在Windows上,那么IIS7將給你最穩定和性能最佳的環境,你可以使用phpmanager(IIS7下的PHP 管理插件)來配置和管理PHP。IIS7已經內置FastCGI,你只需把PHP配置為它的處理器即可。更多詳情可以參考dedicated area on iis.net

通常情況下,開發環境和部署環境不同的話,容易在產品上線后產生各種奇怪的Bug。如果你是在Windows下開發,但是代碼部署在Linux 或其它平臺時,可以考慮使用虛擬機來解決。雖然這聽起來很Tricky,不過用Vagrant建立簡單的Wrapper,你就可以通過PuppetChef,把這些虛擬機提供給同事使用,保證大家使用系統的環境進行開發。

Back to Top

代碼風格指南

PHP社區百花齊放,擁有大量的函數庫、框架和組件。PHP開發者通常會在自己的項目中使用若干個外部庫,因而PHP代碼遵循或盡量接近 同一個代碼風格就非常重要,可以讓開發者方便地把多個代碼庫集成在自己的項目中。

框架互操作組(即PHP標準組)發布了一系列代碼風格推薦標準,即PSR-0PSR-1PSR-2。 不要讓這些名稱所混淆,這些推薦僅是一些被其它項目所遵循的規則,如Drupal, Zend, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium等,你可以把這些規則用在自己的項目中,或者繼續使用你自己的風格。

通常情況下,你的PHP代碼應該遵循其中一項或多項標準,從而其他開發者可以方便地閱讀和使用你的代碼。這些標準都是在前一個標準 上附加新的規則,所以使用PSR-1就同時要求遵循PSR-0,但可以不遵循PSR-2。

Back to Top

Language Highlights

編程范式

PHP是一個靈活的動態語言,支持多種編程范式。這些年來一直在不斷的進化,重要的里程碑包括PHP 5.0 (2004)增加完善的 面向對象模型、PHP 5.3 (2009)增加匿名函數和命名空間和PHP 5.4 (2012)增加traits.

面向對象編程

PHP具有完整的面向對象編程特性,如類、抽象類、接口、繼承、構造函數、克隆和異常等。

函數式編程

PHP支持第一類函數(first-class function),即函數可以賦值給變量,包括用戶自定義的函數和內置函數,然后動態調用它。 函數可以作為參數傳遞給其他函數(即高階函數),也可以作為函數返回值返回。

PHP支持函數遞歸調用,即函數自己調用自己,不過在實際的PHP代碼中,我們更喜歡用迭代來代替遞歸。

2009年發布的PHP 5.3開始引入支持閉包的匿名函數。

PHP 5.4支持把閉包綁定到對象作用域,并改善其可調用性,從而可以在大部分場景中使用匿名函數替代普通函數。

元編程

PHP通過反射API和魔術方法機制,支持多種方式的元編程。開發者通過魔術方法,如__get(), __set(), __clone(), __toString(), __invoke()等,可以改變類的行為。Ruby開發者經常說PHP沒有method_missing方法,實際上通過__call()__callStatic()就可以 完成同樣的功能。

命名空間

如前所述,PHP社區的眾多開發者已經開發了大量的代碼。這意味著一個函數庫中的PHP代碼可能使用了另外一個庫中相同的類名,如果它們共享一個命名空間,則會產生沖突導致異常。

命名空間解決了這個問題。如PHP手冊里描述的那樣,命名空間類似于操作系統中的目錄,兩個同名文件可以共存于不同的目錄。同理,同名的PHP類可以在不同的PHP命名空間下共存,就這么簡單。

因而把代碼放在自己的命名空間下就顯得非常必要,這樣其他人就可以放心的使用這些代碼,而無需擔心與其他函數庫的命名沖突。

PSR-0 里提供了命名空間的推薦使用方式, 它試圖提供一個標準的文件、類和命名空間的使用慣例,從而讓代碼做到即插即用。

標準PHP庫

標準PHP庫(SPL)和PHP一起發布,提供了一組類和接口,包括了常用的數據結構如棧,隊列和堆等,以及遍歷這些數據結構的迭代器, 或者你還可以自己實現SPL接口。

命令行接口

PHP的主要目的是開發Web應用,不過它的命令行腳本接口(CLI)也非常有用。PHP命令行編程可以幫你完成自動化的任務,如測試,部署和 應用管理。

CLI PHP編程非常強大,可以直接調用你自己的app代碼而無需創建Web圖像界面,需要注意的是不要把CLI PHP腳本放在公開的web目錄下!

在命令行下運行PHP:

> php -i

選項-i將會打印PHP配置,類似于phpinfo函數。

選項-a提供交互式shell,和ruby的IRB或python的交互式shell相似,此外還有很多其他有用的命令行選項

接下來寫一個簡單的”Hello, $name” CLI程序,先創建名為hello.php的腳本:

<?php if($argc != 2) {     echo "Usage: php hello.php [name].\n";     exit(1); } $name = $argv[1]; echo "Hello, $name\n"; 

PHP會在腳本運行時根據參數創建兩個特殊的變量,$argc是一個整數,表示參數個數$argv是一個數組變量,包含每個參數的, 它的第一個元素一直是PHP腳本的名字,如本例中為hello.php

命令運行失敗時,可以通過exit()表達式返回一個非0整數來通知shell,常用的exit返回碼可以查看列表

運行上面的腳本,在命令行輸入:

> php hello.php
Usage: php hello.php [name] > php hello.php world
Hello, world

Back to Top

依賴管理

如今有大量的PHP函數庫、框架和組件可供選擇,一個項目中可能會使用其中的若干——這就是項目的依賴。到目前為止,PHP還沒有有效的 項目依賴管理方案。即使你手工的管理它們,你還不得不處理它們的自動加載問題。

目前主要有兩個PHP包管理系統:Composer和PEAR,哪個適合你呢?答案是兩個都需要。

  • 管理單個項目的依賴時使用Composer
  • 管理整個系統的PHP依賴時使用PEAR

通常情況下,Composer包只在你項目中明確指定時才可用,而PEAR包在所有的PHP項目中可用。盡管PEAR聽起來似乎更簡單,但是根據每個 項目制定方案可能更合適。

Composer and Packagist

Composer是一個出色的PHP依賴管理器,把項目的依賴列在composer.json文件中,然后通過一些簡單的命令,Composer就會 自動的幫你下載這些依賴,并配置好自動加載路徑。

現在已經有很多PHP庫支持Composer,可以在項目中使用它們,具體列表可以點擊查看,這是官方支持的Composer兼容的PHP庫。

如何安裝Composer

Composer可以安裝在本地(在當前工作目錄,不推薦這種方式),也可以安裝在系統中(如/usr/local/bin)。假設你要在本地安裝,在 項目的根目錄執行:

curl -s http://getcomposer.org/installer | php

它會下載composer.phar(PHP二進制文檔),然后你就可以用php運行它來完成項目依賴的管理。 請注意:如果 你通過管道直接把下載的代碼傳給PHP解釋器,請先在線閱讀代碼以確保該代碼是安全的。

如何手動安裝Composer

手動安裝composer有點麻煩,不過很多開發者可能更喜歡這種安裝方式。使用交互式安裝程序,它會檢查你安裝的PHP:

  • PHP版本滿足要求
  • .phar文件可以正確執行 - 相關目錄的權限設置正確 - 沒有加載某些不兼容的擴展
  • 相應的php.ini設置正確

而手動安裝則需要你自己做這些事情,你必須自己權衡利弊,以決定是否手動安裝。下面是手動獲取Composer的方法:

curl -s http://getcomposer.org/composer.phar -o $HOME/local/bin/composer
chmod +x $HOME/local/bin/composer

目錄$HOME/local/bin(或你自己選擇其它目錄)應該在你的$PATH環境變量中,從而可以直接運行composer命令。

這樣文檔中描述的運行Composer的命令php composer.phar install,就可以用如下命令替代:

composer install

如何定義和安裝依賴

首先,在composer.phar所在目錄創建文件composer.json,下面是一個依賴Twig例子:

{
    "require": {
        "twig/twig": "1.8.*"
    }
}

第二步:在項目根目錄運行:

php composer.phar install

這會在vendors/下載和安裝項目依賴。最后在應用的PHP入口文件添加下面代碼,告訴PHP使用Composer自動加載器加載項目的依賴庫:

<?php require 'vendor/autoload.php'; 

現在你就可以使用項目依賴的庫了,它們會在需要的時候自動加載。

PEAR

另外一個常用的包管理器就是很多PHP開發者喜歡的PEAR,它的運行方式類似,值得你深入學習一下,深入了解PEAR.

Back to Top

Coding Practices

基礎知識

PHP是一個偉大的語言,可以讓各個層次的程序員都能夠快速高效地完成編碼任務。雖然如此,我們還是經常會因為臨時救急或者 壞習慣而忽視了PHP的基礎。為了解決這個問題,這部分專門給開發者回顧一下PHP的基礎編碼實踐。

日期和時間

PHP使用DateTime類完成讀取、設置、比較和計算日期與時間。雖然PHP中有很多日期和時間處理相關的函數,但是DateTime類提供了 完善的面向對象接口完成各項常見操作,而且還能處理時區,這里不作深入介紹。

要使用DateTime,可以用工廠方法createFromFormat()把原始的日期時間字符串轉換為DateTime對象,或直接用new \DateTime 獲得當前日期和時間的DateTime對象。用format()方法可以把DateTime對象轉換成字符串輸出。

<?php $raw = '22. 11. 1968'; $start = \DateTime::createFromFormat('d. m. Y', $raw); echo "Start date: " . $start->format('m/d/Y') . "\n"; 

DateTime計算時間時通常需要DateInterval類,如add()sub()方法,都是將DateInterval作為參數。盡量避免直接用 時間戳表示時間,夏令時和時區會讓時間戳產生歧義,使用間隔日期更為妥當。計算兩個日期的差值使用diff()方法,返回 DateInterval對象,輸出顯示也很方便。

<?php // create a copy of $start and add one month and 6 days $end = clone $start; $end->add(new \DateInterval('P1M6D')); $diff = $end->diff($start); echo "Difference: " . $diff->format('%m month, %d days (total: %a days)') . "\n"; // Difference: 1 month, 6 days (total: 37 days) 

DateTime對象之間可以直接比較:

<?php if($start < $end) {     echo "Start is before end!\n"; } 

最后一個例子是DatePeriod類的用法,它用于循環事項(recurring events)的迭代。它的構造函數參數為:start和end,均為 DateTime對象,以及返回事項的間隔周期。

<?php // output all thursdays between $start and $end $periodInterval = \DateInterval::createFromDateString('first thursday'); $periodIterator = new \DatePeriod($start, $periodInterval, $end, \DatePeriod::EXCLUDE_START_DATE); foreach($periodIterator as $date) {     // output each date in the period     echo $date->format('m/d/Y') . " "; } 

設計模式

在代碼和項目中使用常見模式是有好處的,可以讓代碼更易于管理,同時也便于其他開發者理解你的項目。

如果你的項目使用了框架,那么在代碼和項目結構上,都會遵循框架的約束,自然也就繼承了框架中的各種模式, 這時你所需要考慮的是讓上層代碼也能夠遵循最合適的模式。反之,如果沒有使用框架,那么就需要你自己選擇 適用于當前項目類型和規模的最佳模式了。

異常

異常是大部分流行語言的標準特性,但是PHP開發者卻不太重視。其他語言如 Ruby極度倚賴異常,在任何錯誤發生的時候,如HTTP請求失敗 、DB查詢錯誤,甚至圖片資源未找到,都會拋出一個異常,以及時提示那里發生了一個錯誤。

PHP則對此很寬松,如調用file_get_contents()失敗,只是返回FALSE并提示一個warning信息而已。很多老的PHP框架,如 CodeIgniter會返回false,然后在自己的日志里記錄一個消息,開發者需要使用如$this->upload->get_error()的方式來查看發生了什么 錯誤。這么做需要你自己檢查是否有錯誤,并需要根據不同類調用不同的方法來獲取錯誤消息,而不能讓錯誤明顯的顯示出來。

這種做法的另外一個弊端是當類自動在屏幕打印一個錯誤,然后退出進程,阻止了其他開發者動態處理該錯誤的機會。而異常則是讓開發者知道 發生了錯誤,并讓他們選擇如何處理:

<?php $email = new Fuel\Email; $email->subject('My Subject'); $email->body('How the heck are you?'); $email->to('guy@example.com', 'Some Guy'); try {     $email->send(); } catch(Fuel\Email\ValidationFailedException $e) {     // The validation failed } catch(Fuel\Email\SendingFailedException $e) {     // The driver could not send the email } 

SPL異常

默認的異常類Exception沒有含義,常見的做法是給它設定一個有意義的名字:

<?php class ValidationException extends Exception {} 

這使得你可以包含多個catch子句來處理不同的異常,但是這又會導致創建_很多的_自定義異常類,可以用SPL中的異常類來緩解這個問題 SPL擴展.

如使用__call()魔術方法,對不存在的方法調用拋出一個throw new BadFunctionCallException;,既避免了拋出含義模糊的 Exception異常,也省去了自定義異常類的麻煩。

Back to Top

數據庫

通常PHP代碼使用數據庫來持久化存儲數據,并有多種方式去連接和操作數據庫。在_PHP 5.1.0_之前,推薦的方式有mysqlmysqlipgsql等。

如果應用只是使用一個數據庫的話,原生驅動就工作的非常好,否則使用MySQL的同時,還需要使用MSSQL或Oracle數據庫的話,那么 就沒有辦法只使用一個原生驅動了,只能分別學習各個數據庫驅動的API,這非常令人生厭。

另外需要注意,mysql這個原生驅動已經不在活躍開發狀態了,從PHP 5.4.0開始被標記為不推薦使用,意味著將來版本如PHP 5.6可能會 移除這個擴展。如果你正在使用mysql_connect()mysql_query(),那么將來可能要重寫部分代碼,所以最好用mysqli或PDO來 代替。_如果你正在開發新項目,請不要用mysql擴展,嘗試用MySQLi擴展或PDO來替代_

PDO

PDO是數據庫連接抽象庫,從PHP 5.1.0開始提供,提供多種數據庫的統一的操作接口。PDO不會轉化你的SQL查詢或者模擬缺失特性; 它只是提供統一的API去連接不同的數據庫而已。

更重要的是,PDO允許你綁定SQL查詢語句中的變量,而無需擔心SQL注入問題,這主要通過PDO statements和變量綁定來實現。

假設PHP腳本接收一個數字ID作為查詢參數,從數據庫取回一條記錄。下面是一種錯誤的做法:

<?php $pdo = new PDO('sqlite:users.db'); $pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NO! 

這是非常糟糕的代碼,直接在SQL中插入一個原始輸入變量,導致潛在的SQL注入風險。假如黑客構造URL: http://domain.com/?id=1%3BDELETE+FROM+users來傳入惡意參數id,則$_GET['id']的變量值為id=1;DELETE FROM users, 這將刪除數據表中的所有用戶!因此,你應該使用PDO的綁定參數功能來處理ID輸入參數。

<?php $pdo = new PDO('sqlite:users.db'); $stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id'); $stmt->bindParam(':id', filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT), PDO::PARAM_INT); $stmt->execute(); 

這才是正確的代碼,在PDO statement中綁定一個參數,使得查詢被發給數據庫之前,對輸入參數進行轉義,防止SQL注入攻擊。

另外一個要注意的問題是,如果數據庫連接沒有隱式地關閉,那么數據庫連接數可能會超過數據庫服務器的限制而連接失敗,這種 錯誤在其他編程語言中比較常見。PDO對象在銷毀的時候會隱式的關閉數據庫連接,只要你把指向它的引用全部刪除即可,如設置 為NULL。如果沒有,PHP也會在腳本結束時關閉所有非持久化的數據庫連接。

抽象層

很多框架都提供了自己的數據庫抽象層,有的是基于PDO,有的不是。它們通過PHP方法來包裝實際的查詢,能夠模擬出只存在于 某些數據庫系統的特性,給你一個真正的數據庫抽象層。這么做會帶來一些性能的損失,但是在一個需要支持MySQL、PostgreSQL 和SQLite的應用中,這個損失相對于由此帶來的代碼一致性而言是可以接受的。

有些抽象層遵循PSR-0命名空間標準,可以集成在任意的應用中:

Back to Top

安全

Web應用安全

總有一些人會千方百計的想著破壞你的Web應用,提前想辦法加強自己的Web應用的安全性非常重要。幸運的是, The Open Web Application Security Project (OWASP) 已經提供詳盡的已知安全問題列表和防范對策。 每個關注Web安全的開發者都應該仔細閱讀該列表。

使用Bcrypt做密碼哈希

一個存在用戶登錄的PHP應用,用戶名和密碼(哈希后的)會保存在數據庫中,用于將來登錄時進行身份驗證。因而對存儲在數據庫中的 密碼進行正確的_哈希_非常重要。如果密碼沒有哈希,那么數據庫被黑或者非法訪問時,所有的用戶帳號都將泄漏。

使用Bcrypt做密碼哈希,Bcrypt非常簡單,并可以確保數據庫被攻擊后,無法反向工程恢復密碼的明文。

下面是幾個PHP的Bcrypt庫:

數據過濾

永遠不要在PHP代碼中信任外部輸入,在使用之前一定要先過濾和驗證,filter_varfilter_input函數可以幫助過濾文本和 驗證文本格式(如郵箱地址)。

外部輸入可以是:$_GET$_POST表單輸入數據、$_SERVER超級變量中的某些值和通過fopen('php://input', 'r')獲取的 HTTP請求體。要記住外部輸入不僅僅是用戶提交的表單數據,還包括上傳和下載的文件、session變量、cookie數據和第三方Web服務提供的 數據等。

當外部數據被存儲合并之后,下次讀取時,它們仍然算是外部輸入,每次在代碼中處理的時候,需要問自己是否已經正確過濾,是否可以 信任它們。

數據需要根據不同用處,進行不同的_過濾_,如果把未經過濾的數據輸出到HTML頁面,它可以在你的網站里執行HTML和JavaScript!即通常 說的跨站腳本攻擊(XSS)。避免XSS的一個策略就是過濾外部輸入的所有HTML標簽,刪除或者轉義其中的HTML實體。

另外一個例子是傳給命令行命令的選項,這可能非常危險(通常不是一個好主意),不過你可以用內置的escapeshellarg函數過濾命令行的 參數。

最后一個例子是根據外部輸入來從文件系統中加載文件的操作,可以通過修改路徑中的文件名實施攻擊。你需要過濾輸入中的”/”, “../”, null bytes, 或其他特殊字符,以防止加載不能公開的包含敏感數據的文件。

數據清洗

數據清洗就是刪除或轉義外部輸入中的非法或不安全字符。比如把外部數據輸出到HTML或插入到SQL語句之前,需要先清洗外部輸入。 當你通過PDO綁定數據時,它會替你轉義輸入數據。

有時候需要允許外部數據包含安全的HTML標簽,并輸出到HTML頁面中。這個比較難處理,可以考慮使用其他更嚴格的格式,如 或BBCode,實在不能的話,可以使用HTML Purifier庫來進行數據清洗。

了解更多數據清洗過濾器

數據驗證

數據驗證外部輸入就是你預期的,如你在處理注冊表單時,需要驗證email地址、電話號碼和年齡等數據

了解更多數據驗證過濾器

配置文件

在創建應用的配置文件時,請遵循下面的業界最佳實踐:

  • 配置文件保存在Web不能直接訪問和上傳的目錄中。
  • 如果配置文件只能放在文檔根目錄時,請使用.php作為文件名后綴,這樣即使直接訪問該配置文件,也不會輸出配置信息。 - 配置文件內容應該加密,或者對文件設置訪問權限。

注冊全局變量

提示:從PHP 5.4.0開始,register_globals配置已經刪除,不再生效。保留這個配置,只是提示依賴該配置的應用進行升級。

啟用register_globals配置后,$_POST, $_GET$_REQUEST中的變量自動注冊為全局變量,使得應用很難辨別變量的確切來源,從而產生安全漏洞。

例如:$_GET['foo']將注冊變量$foo,這會覆蓋程序中未聲明的同名變量。如果你使用PHP 5.4.0之前的版本,請確保已經把register_globals設置為off

錯誤提示

錯誤日志可以幫助追查應用的Bug,但是也會暴露應用的結構信息而產生安全問題,為此,需要在開發環境和線上環境設置不同的配置,防止 敏感信息的泄漏。

開發環境

要在開發環境顯示錯誤提示,需要在php.ini中配置以下配置項:

  • display_errors: On
  • error_reporting: E_ALL
  • log_errors: On

線上環境

要在線上環境隱藏錯誤提示,需要在php.ini中配置以下配置項:

  • display_errors: Off
  • error_reporting: E_ALL
  • log_errors: On

這樣設置后,線上錯誤會記錄到Web服務器的錯誤日志中,而不是直接顯示給用戶。如果想了解更多錯誤提示相關的設置,請參考手冊:

Back to Top

測試

為PHP代碼編寫自動化測試被認為是一個最佳實踐,可以幫助你構建出高質量的應用。自動化測試可以幫助你確認沒有因為重構或添加 新功能而破壞原有功能,所以應該重視自動化測試。

PHP有多種類型的測試工具和框架可以使用,具體方法各有區別——但是它們的目標都是避免手工測試,滿足大型QA組織的需求,保證最新的 更改沒有破壞已有功能。

測試驅動開發

Wikipedia的定義:

測試驅動開發(TDD)是以非常短的開發周期,不斷進行迭代的軟件開發流程:首先開發者針對改進或新功能編寫失敗的自動化測試用例,然后編寫代碼使測試用例通過, 最后重構代碼,讓代碼滿足可接受的標準。Kent Beck,該技術的創建者或者說重新發現者,在2003年聲明TDD鼓勵簡單的設計和提振信心。

目前對應用有多種類型的測試:

單元測試

單元測試是從編寫開始,貫穿于整個開發周期的一種用于保證函數、類和方法的行為與預期一致的編程方法。通過檢查各個函數和方法的輸入和輸出值,你可以保證它們 內部邏輯已經正確執行;通過依賴注入、編寫mock類和stubs,你可以驗證依賴是否已經正確處理,提高測試覆蓋率。

在編寫一個類或函數的時候,應該為它的每一個行為創建一個單元測試,至少你要保證它收到錯誤參數時能夠觸發錯誤,而參數正確時能正常工作。這可以幫你在后面 修改類或函數的時候,確認已有功能仍然正常工作。PHP中var_dump()的功能與此類似,但是它是無法用于創建應用的。

單元測試的另外一個用武之地是在給開源項目貢獻代碼時,如果你編寫一個測試,證明代碼存在bug,然后修復代碼,讓測試通過,這樣該補丁被接受的概率要高很多。 如果你的項目接受人家的補丁,你應該把單元測試作為項目的一項要求。

PHPUnit是PHP應用的單元測試框架的業界標準,其他幾個可選框架是:

集成測試

Wikipedia的定義:

集成測試(也稱集成與測試,縮寫為I&T)是把各個獨立模塊集成在一起,作為一個整體進行測試的軟件測試階段,它處于單元測試和驗收測試之間。集成測試把已經 做過單元測試的模塊集成在一塊,然后運行集成測試用例,最終輸出一個可以進行系統測試的系統。

很多單元測試工具同時也可以用于集成測試,并且原理也是相通的。

功能測試

有時也稱為驗收測試,使用工具創建自動化的測試用例,然后在真實的系統上運行,這一點與單元測試驗證單個模塊的正確性和集成測試驗證模塊間交互的正確性是有 區別的,這些工具通常使用真實的數據集來模擬真實用戶的使用行為來驗證系統的正確性。

功能測試工具

行為驅動開發

行為驅動開發(BDD)有兩種方式:SpecBDD和StoryBDD。SpecBDD關注技術行為或代碼,而StoryBDD關注業務、特性和交互,這兩種方式都有對應的PHP框架。

采用StoryBDD,開發者編寫人類可讀的故事來描述應用的行為,然后這些故事可以作為應用的測試用例。PHP中用于StoryBDD編程的框架是Behat,從Ruby 的Cucumber項目演化而來,實現了Gherkin DSL來描述特性行為。

采用SpecBDD,開發者編寫規格說明來描述實際代碼的行為,與測試一個函數或方法不同,規格描述了一個函數或方法應該具有的行為。PHP中的PHPSpec框 架提供該編程方式的支持,它也是從Ruby的RSpec project演化而來。

BDD鏈接

  • Behat, the StoryBDD framework for PHP, inspired by Ruby’s Cucumber project;
  • PHPSpec, the SpecBDD framework for PHP, inspired by Ruby’s RSpec project;
  • Codeception is a full-stack testing framework that uses BDD principles.

測試輔助工具

除了測試驅動和行為驅動開發框架,還有大量的通用框架和函數庫,可以在各種開發方法下使用。

工具鏈接

Back to Top

服務器和部署

部署PHP應用到線上Web服務器的方式有很多種。

平臺即服務(PaaS)

PaaS提供運行PHP Web應用所需的系統和網絡環境,對PHP應用和框架只需要做少量的配置即可。

現在PaaS已經成為部署、托管和擴展各種規模的PHP應用的流行方式,可以在 resources部分查看PHP PaaS “平臺即服務”供應商列表。

虛擬或獨立主機

如果你愿意或想學習系統管理,那么虛擬或獨立主機可以讓你完全控制自己的運行環境。

nginx和PHP-FPM

PHP通過內置的FastCGI進程管理器(FPM),可以非常高效地和輕量級的高性能Web服務器nginx進行通信。 nginx比Apache消耗更少的內存,能更好的處理并發請求,這在內存限制較多的虛擬主機環境中尤為重要。

Apache和PHP

PHP和Apache是一個老搭檔,歷史悠久。Apache有很強的可配置性和大量的擴展模塊, 是共享主機中常見的Web服務器,完美支持各種PHP框架和開源應用(如WordPress)。可惜的是,默認情況下,Apache比nginx更耗資源,并發處理能力不強。

Apache有多種方式運行PHP,最常見簡單的方式是使用mod_php5的prefork MPM方式, 缺點是它對內存的利用效率不高,如果你不想深入學習服務器的管理,那么這種最簡單的方式就是你的最佳選擇了。注意,如果你使用mod_php5,最好使用 prefork MPM方式。

如果你想追求高性能和高穩定性,那么也可以為Apache選擇與nginx類似的FPM系統worker MPMevent MPM,它們分別使用mod_fastcgi和mod_fcgid模塊。FPM方式可以更高效的利用內存,運行 速度更快,但是配置也相對復雜一些。

共享主機

PHP非常流行,很少有服務器沒有安裝PHP的,因而有很多共享主機,不過需要注意服務器上的PHP是否是最新穩定 版本。共享主機允許多個開發者把自己的網站部署在上面,這樣的好處是費用非常便宜,壞處是你不知道將和哪些 網站共享主機,因此需要仔細考慮機器負載和安全問題。如果項目預算允許的話,避免使用共享主機是上策。

Back to Top

緩存

PHP自身效率很高,但是執行創建遠程連接、加載文件等操作時容易出現瓶頸,幸運的是,我們有很多工具來加速這部分操作,或減少 這些耗時操作的執行次數。

字節碼緩存

W在一個PHP文件被執行時,它先被編譯為字節碼(也稱opcode),然后這些字節碼被執行。如果文件沒有修改,那么字節碼也會保持不變, 這意味著編譯這一步白白浪費了CPU資源。

這就是引入字節碼緩存的原因,通過把字節碼保存在內存中來消除冗余的編譯,重用它們完成后續的調用。配置字節碼緩存非常簡單, 而且可以極大地提高應用的執行效率,沒有理由不使用字節碼緩存。

流行的字節碼緩存方案有:

對象緩存

很多時候,在代碼中緩存對象可以帶來很大的收益,例如獲取代價很大的數據和查詢結果很少變化的數據庫調用。我們可以使用對象 緩存系統緩存這些數據,大大加快后續的同類訪問請求。如果你在取得這些數據之后,把它們緩存在系統中,在后續對這些數據的請求 中,就可以直接使用緩存中的對象,這么做可以很大的提示系統性能,減少服務器的負載。

很多流行的字節碼緩存方案也允許你緩存自定義數據,因此我們更應該充分利用對象緩存功能。APC、XCache和WinCache都提供API, 讓你把數據緩存在他們的內存cache中。

使用最多的內存對象緩存系統是APC和memcached,APC是很好的一個對象緩存方案,它提供了簡單的API來讓你把對象存儲在內存中,而且 配置和使用都非常容易,它的一個缺點是只能在本機使用。Memcached則是另外一種方式,它是一個單獨的服務,可以通過網絡訪問,這 意味著可以在一個地方寫入數據,然后在不同的系統中訪問這份數據。

在單機性能上,APC通常比Memcached更高,如果你不需要多臺服務器或者其他Memcached的高級功能,APC可能是你的最佳選擇。

APC的示例:

<?php // check if there is data saved as 'expensive_data' in cache if (apc_fetch('expensive_data') === false) {     // data is not in cache; save expensive call for later use     apc_add('expensive_data', get_expensive_data()); } print_r(apc_fetch('expensive_data')); 

學習更多對象緩存系統:

Back to Top

資源

From the Source

People to Follow

Mentoring

PHP PaaS供應商

框架

大量的PHP開發者使用框架,而不是重復發明輪子來創建自己的Web應用。框架抽象出底層通用的業務邏輯,給使用者了提供簡單易用的接口。

不是所有的項目都需要框架,有時候原生的PHP就能滿足需求,但是需要框架的時候,有三種類型的框架可供選擇:

* 微框架 * 全能(Full-Stack)框架 * 組件框架

微框架僅是一個包裝器(Wrapper),盡量快地把HTTP請求路由到回調函數、控制器或方法上,有些框架也會提供一些函數庫,如基本的數據庫 操作。微框架主要用于構建遠程HTTP服務。

全能框架則是在微框架的功能之上提供了更多的功能特性,如ORM,驗證組件等。

組件框架則是一組獨立功能庫的集合,多個基于組件的框架集合在一起,甚至可以用作微框架或者全能框架。

組件

如前所述,組件是另外一種創建、實現和發布開源代碼的方式,當前社區存在很多組件庫,最主要的兩個:

這兩個庫都有用于安裝和升級的命令行工具,已經在依賴管理部分講述.

還有基于組件的框架,你可以使用其中的組件,它們相互之間依賴很少,或完全獨立,如FuelPHP驗證包, 就可以脫離FuelPHP框架而獨立使用。這些項目就相當于一個可重用的組件庫:

Back to Top

社區

The PHP community is as diverse as it is large, and it’s members are ready and willing to support new PHP programmers. You should consider joining your local PHP user group (PUG) or attending larger PHP conferences to learn more about the best practices shown here. You can also hang out on IRC in the #phpc channel on irc.freenode.com and follow the @phpc 推ter account. Get out there, meet new developers, learn new topics and, above all, make new friends.

Read the Official PHP Events Calendar

PHP User Groups

If you live in a larger city, odds are there’s a PHP user group nearby. Although there’s not yet an official list of PUGs, you can easily find your local PUG by searching on Google or Meetup.com. If you live in a smaller town, there may not be a local PUG; if that’s the case, start one!

Read about User Groups on the PHP Wiki

PHP Conferences

The PHP community also hosts larger regional and national conferences in many countries around the world. Well-known members of the PHP community usually speak at these larger events, so it’s a great opportunity to learn directly from industry leaders.

Find a PHP Conference

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!