PHP 之道

PHP之道

翻譯:wulijun    關注個人微博php

Welcome

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

翻譯

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

免責聲明

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

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

如何參與網站建設

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

推廣網站

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

查看banner圖片程序員

Back to Topgithub

Getting Started

使用當前穩定版本 (5.4)

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

內置的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

Vagrant

若是你在開發應用和發佈應用的時候採用了不一樣的環境,那麼在正式使用時,應用可能出現許多奇怪的BUG。若是你是在開發團隊裏工做,那麼保證各位的開發環境和全部的庫文件都是最新的而且處在同一版本,會是件更麻煩的事。 若是你在Windows平臺開發並準備部署到Linux(或其餘非Windows的平臺)上,或者你是在開發團隊裏工做,那你應該考慮用個虛擬機。這雖然聽起來挺麻煩,可是 Vagrant這個程序能夠輔助你用幾步就建立一個簡單的虛擬機。 接下來,你能夠手動配置這些基礎的環境,或者你能夠找個部署軟件來替你完成這些事情,好比說PuppetChef。部署個基礎環境,能很好地保證你們的開發環境創建的方式都大體類似,並且還能省去你維護那些複雜的「安裝命令」列表的麻煩。 你也能夠輕易地毀掉現有的基礎環境後再作一個新的出來,這樣你就能有一個全新的換精靈。

Vagrant會建立一些共享文件夾,用來給你在主機和虛擬機之間共享代碼用。也就是說,你能夠在主機上寫好程序,而後在虛擬機中運行。

Back to Top

代碼風格指南

PHP社區百花齊放,擁有大量的函數庫、框架和組件。PHP開發者一般會在本身的項目中使用若干個外部庫,於是PHP代碼遵循或儘可能接近 同一個代碼風格就很是重要,可讓開發者方便地把多個代碼庫集成在本身的項目中。

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

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

可使用PHP_CodeSniffer來檢查代碼是否符合這些標準,文本編輯器插件Sublime Text 2還能 提供實時檢查。若是不符合規範,可使用Fabien Potencier提供的工 具PHP Coding Standards Fixer自動修復,不用本身手工修復。

變量名和代碼結構建議使用英文符號編寫,註釋則可使用各類語言,沒有限制。

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

XDebug

調試器是軟件開發過程當中很是重要的一個工具,經過它,能夠跟蹤代碼的執行過程,查看堆棧信息。XDebug是一個PHP調試器,能夠集成在常見的IDE中,提供設置斷點、 查看堆棧信息等功能,還能夠和PHPUnit、KCacheGrind等工具配合,執行代碼覆蓋率測試和性能調優。

若是你如今尚未使用調試器,僅僅依靠var_dump/print_r調試的話,XDebug就是你的最佳選擇。

安裝XDebug有點複雜,其中一個重要功能」遠程調試」——若是你在本地開發代碼,而後在虛擬機或其餘主機中測試,那麼它對你就很是有用。

一般,你須要修改Apache虛擬主機或者.htaccess配置文件,增長:

php_value xdebug.remote_host=192.168.?.?
php_value xdebug.remote_port=9000

「remote host」和」remote port」對應本機IDE的監聽地址和端口,而後設置IDE爲」等待鏈接」模式,打開URL:

http://your-website.example.com/index.php?XDEBUG_SESSION_START=1

這樣IDE就會監控腳本的執行,容許用戶設置斷點和查看內存中的變量值。

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 https://getcomposer.org/installer | php

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

如何手動安裝Composer

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

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

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

curl -s https://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,它的運行方式和Composer有些相似,可是也有一些區別.

PEAR requires each package to have a specific structure, which means that the author of the package must prepare it for usage with PEAR. Using a project which was not prepared to work with PEAR is not possible.

PEAR installs packages globally, which means after installing them once they are available to all projects on that server. This can be good if many projects rely on the same package with the same version but might lead to problems if version conflicts between two projects arise.

How to install PEAR

You can install PEAR by downloading the phar installer and executing it. The PEAR documentation has detailed install instructions for every operating system.

If you are using Linux, you can also have a look at your distribution package manager. Debian and Ubuntu for example have a apt php-pear package.

How to install a package

If the package is listed on the PEAR packages list, you can install it by specifying the official name:

pear install foo

If the package is hosted on another channel, you need to discover the channel first and also specify it when installing. See the Using channel docs for more information on this topic.

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包含的上下文信息不多,對於debug不方便,常見的作法是建立更具體的子類:

<?php
class ValidationException extends Exception {}

這使得你能夠包含多個catch子句來處理不一樣的異常,可是這又會致使建立_不少的_自定義異常類,能夠用SPL中的異常類來緩解這個問題 SPL擴展.

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

Back to Top

數據庫

一般PHP代碼使用數據庫來持久化存儲數據,並有多種方式去鏈接和操做數據庫。在_PHP 5.1.0_以前,推薦的方式有mysql、 mysqlipgsql等。

若是應用只是使用一個數據庫的話,原生驅動就工做的很是好,不然使用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']的變量值爲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安全的開發者都應該仔細閱讀該列表。

Password Hashing

Eventually everyone builds a PHP application that relies on user login. Usernames and passwords are stored in a database and later used to authenticate users upon login.

It is important that you properly hash passwords before storing them. Password hashing is an irreversible, one way function performed against the users password. This produces a fix length string that can not be feasibly reversed. This means you can compare a hash against another to determine if they both came from the same source string, but you can not determine the original string. If passwords are not hashed and your database is accessed by an unauthorized third-party, all user accounts are now compromised. Some users may (unfortunately) use the same password for other services. Therefore, it is important to take security seriously.

Hashing passwords with password_hash

In PHP 5.5 password_hash will be introduced. At this time it is using BCrypt, the strongest algorithm currently supported by PHP. It will be updated in the future to support more algorithms as needed though. The password_compat library was created to provide forward compatibility for PHP >= 5.3.7.

Below we hash a string, we then check the hash against a new string. Because our two source strings are different (‘secret-password’ vs. ‘bad-password’) this login will fail.

<?php                                                                                                                                                                                                            
require 'password.php';

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
    //Correct Password
} else {
    //Wrong password
}

數據過濾

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

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

當外部數據被存儲合併以後,下次讀取時,它們仍然算是外部輸入,每次在代碼中處理的時候,須要問本身是否已經正確過濾,是否能夠 信任它們。

數據須要根據不一樣用處,進行不一樣的_過濾_,若是把未通過濾的數據輸出到HTML頁面,它能夠在你的網站裏執行HTML和JavaScript!即一般 說的跨站腳本攻擊(XSS)。避免XSS的一個策略就是使用strip_tags函數過濾外部輸入的全部HTML標籤,或者使用htmlentitieshtmlspecialchars轉義其中的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
display_startup_errors = On
error_reporting = -1
log_errors = On

來自php.net:

-1表示顯示各類錯誤,包括未來增長的新錯誤類型,和PHP 5.4中的E_ALL行爲相同。

E_STRICT錯誤級別在5.3.0版本引入,不在E_ALL中,不過5.4.0版本開始,E_ALL包含E_STRICT級別的錯誤。因此在5.3版本中,要顯示 全部錯誤,須要把error_reporting設置爲-1或者E_ALL | E_STRICT

各PHP版本顯示全部錯誤的配置

  • < 5.3 -1 or E_ALL
  •   5.3 -1 or E_ALL | E_STRICT
  • > 5.3 -1 or E_ALL

線上環境

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

display_errors = Off
display_startup_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 MPM或 event MPM,它們分別使用mod_fastcgi和mod_fcgid模塊。FPM方式能夠更高效的利用內存,運行 速度更快,可是配置也相對複雜一些。

共享主機

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

Building and Deploying your Application

If you find yourself doing manual database schema changes or running your tests manually before updating your files (manually), think twice! With every additional manual task needed to deploy a new version of your app, the chances for potentially fatal mistakes increase. Whether you’re dealing with a simple update, a comprehensive build process or even a continuous integration strategy, build automation is your friend.

Among the tasks you might want to automate are:

  • Dependency management
  • Compilation, minification of your assets
  • Running tests
  • Creation of documentation
  • Packaging
  • Deployment

Build Automation Tools

Build tools can be described as a collection of scripts that handle common tasks of software deployment. The build tool is not a part of your software, it acts on your software from ‘outside’.

There are many open source tools available to help you with build automation, some are written in PHP others aren’t. This shouldn’t hold you back from using them, if they’re better suited for the specific job. Here are a few examples:

Phing is the easiest way to get started with automated deployment in the PHP world. With Phing you can control your packaging, deployment or testing process from within a simple XML build file. Phing (which is based on Apache Ant) provides a rich set of tasks usually needed to install or update a web app and can be extended with additional custom tasks, written in PHP.

Capistrano is a system for intermediate-to-advanced programmers to execute commands in a structured, repeatable way on one or more remote machines. It is pre-configured for deploying Ruby on Rails applications, however people are successfully deploying PHP systems with it. Successful use of Capistrano depends on a working knowledge of Ruby and Rake.

Dave Gardner’s blog post PHP Deployment with Capistrano is a good starting point for PHP developers interested in Capistrano.

Chef is more than a deployment framework, it is a very powerful Ruby based system integration framework that doesn’t just deploy your app but can build your whole server environment or virtual boxes.

Chef resources for PHP developers:

Further reading:

Continuous Integration

Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily — leading to multiple integrations per day. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly.

– Martin Fowler

There are different ways to implement continuous integration for PHP. Recently Travis CI has done a great job of making continuous integration a reality even for small projects. Travis CI is a hosted continuous integration service for the open source community. It is integrated with GitHub and offers first class support for many languages including PHP.

Further reading:

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
$data = apc_fetch('expensive_data');
if ($data === false) {
    // data is not in cache; save result of expensive call for later use
    apc_add('expensive_data', $data = get_expensive_data());
}

print_r($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 twitter account. Get out there, meet new developers, learn new topics and, above all, make new friends. Also there is a large Google+ Community for PHP Programmers where 9,000+ PHP Programmers are available to discuss about problem and solving others problem by each other. You can also join there on Google+ community for PHP Programmer

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

Back to Top

Created and maintained by

Project collaborators

Project contributors

Project sponsors

Creative Commons License
PHP: The Right Way by Josh Lockhart is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
Based on a work at www.phptherightway.com.

相關文章
相關標籤/搜索