目前網絡上充斥著大量的過時資訊,讓 PHP 新手誤入歧途,并且傳播著錯誤的實踐以及不安全的代碼。PHP 之道 收集了現有的 PHP 最佳實踐、編碼規范和權威學習指南,方便 PHP 開發者閱讀和查找
使用 PHP 沒有規范化的方式。本網站主要是向 PHP 新手介紹一些他們沒有發現或者是太晚發現的主題, 或是經驗豐富的專業人士已經實踐已久的做法提供一些新想法。本網站也不會告訴您應該使用什么樣的工具,而是提供多種選擇的建議,并盡可能地說明方法及用法上的差異。
當有更多有用的資訊以及范例時,此文件會隨著相關技術的發展而持續更新。
PHP 之道 已經翻譯成多種語言:
EnglishDeutschEspa?olFran?aisIndonesiaItalianoPolskiPortuguês do BrasilRoman?Sloven??inaSrpskiTürk?eбългарскиРусский языкУкра?нська???????????????????????日本語簡體中文繁體中文幫助我們讓本網站作為 PHP 新手的最佳資源!在 GitHub 上貢獻
您可以在網站上放置 PHP之道 的橫幅來支持我們,讓 PHP 的新人知道哪里可以獲取到好的資料!
廣告橫幅
Back to Top
如果你剛開始學習 PHP,請使用最新的穩定版本 PHP 5.6。PHP 近年來有了巨大的改進,增加了許多強大的 新特性。雖然 5.2 和 5.6 之間增加的版本號似乎很小, 但它代表了 重大的 改進。如果你想查找一個函數及其用法,可以去官方手冊 php.net 中查找。
PHP 5.4 之后, 你可以不用安裝和配置功能齊全的 Web 服務器,就可以開始學習 PHP。 要啟動內置的 Web 服務器,需要從你的命令行終端進入項目的 Web 根目錄,執行下面的命令:
> php -S localhost:8000了解更多內置的命令行服務器Mac 安裝
OS X 系統會預裝 PHP, 只是一般情況下版本會比最新穩定版低一些。目前 Lion 是 5.3.10, Mavericks 是 5.4.17, Yosemite 則是 5.5.9, 但在 PHP 5.6 出來之后, 這些往往是不夠的。
這里有許多方式在 OS X 上安裝 PHP 。
通過 Homebrew 安裝 PHP
Homebrew 是一個強大的 OS X 專用包管理器, 它可以幫助你輕松的安裝 PHP 和各種擴展。 Homebrew PHP 是一個包含與 PHP 相關的 Formulae,能讓你通過 homebrew 安裝 PHP 的倉庫。
也就是說, 你可以通過 brew install
命令安裝 php53
, php54
, php55
或者 php56
,并且通過修改 PATH
變量來切換各個版本?;蛘吣阋部梢允褂?nbsp;brew-php-switcher 來自動切換。
通過 Macports 安裝 PHP
MacPorts 是一個開源的,社區發起的項目,它的目的在于設計一個易于使用的系統,方便編譯,安裝以及升級 OS X 系統上的 command-line, X11 或者基于 Aqua 的開源軟件。
MacPorts 支持預編譯的二進制文件,因此你不必每次都重新從源碼壓縮包編譯,如果你的系統沒有安裝這些包,它會節省你很多時間。
此時,你可以通過 port install
命名來安裝 php53
,php54
,php55
或者 php56
,比如:
> php -i選項 -i
將會打印 PHP 配置,類似于 phpinfo()
函數。
選項 -a
提供交互式 shell,和 Ruby 的 IRB 或 python 的交互式 shell 相似,此外還有很多其他有用的命令行選項。
接下來寫一個簡單的 “Hello, $name” CLI 程序,先創建名為 hello.php
的腳本:
<?phpif($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.phpUsage: php hello.php [name]> php hello.php worldHello, world學習如何在命令行運行 PHP學習如何在 Windows 環境下運行 PHP 命令行程序Xdebug
合適的調試器是軟件開發中最有用的工具之一,它使你可以跟蹤程序執行結果并監視程序堆棧中的信息。 Xdebug 是一個 php 的調試器,它可以被用來在很多 IDE(集成開發環境) 中做斷點調試以及堆棧檢查。它還可以像 PHPUnit 和 KCacheGrind 一樣,做代碼覆蓋檢查或者程序性能跟蹤。
如果你仍在使用 var_dump()
/PRint_r()
調錯,經常會發現自己處于困境,并且仍然找不到解決辦法。這時,你該使用調試器了。
安裝 Xdebug 可能很費事,但其中一個最重要的「遠程調試」特性 —— 如果你在本地開發,并在虛擬機或者其他服務器上測試,遠程調試可能是你想要的一種方式。
通常,你需要修改你的 Apache VHost 或者 .htaccess 文件的這些值:
php_value xdebug.remote_host=192.168.?.?php_value xdebug.remote_port=9000「remote host」 和 「remote port」 這兩項對應和你本地開發機監聽的地址和端口。然后將你的 IDE 設置成「listen for connections」模式,并訪問網址:
<?phprequire 'vendor/autoload.php';現在你可以使用你項目中的依賴,且它們會在需要時自動完成加載。
更新你的依賴
Composer 會建立一個 composer.lock
文件,在你第一次執行 php composer.phar install
時,存放下載的每個依賴包精確的版本編號。假如你要分享你的項目給其他開發者,并且 composer.lock
文件也在你分享的文件之中的話。 當他們執行 php composer.phar install
這個命令時,他們將會得到與你一樣的依賴版本。 當你要更新你的依賴時請執行 php composer.phar update
。
當你需要靈活的定義你所需要的依賴版本時,這是最有用。 舉例來說需要一個版本為 ~1.8 時,意味著 “任何大于 1.8.0 ,但小于 2.0.x-dev 的版本”。你也可以使用通配符 *
在 1.8.*
之中?,F在Composer在composer update
時將升級你的所有依賴到你限制的最新版本。
更新通知
要接收關于新版本的更新通知。你可以注冊 VersionEye,這個 web 服務可以監控你的 Github 及 BitBucket 帳號中的 composer.json
文件,并且當包有新更新時會發送郵件給你。
檢查你的依賴安全問題
Security Advisories Checker 是一個 web 服務和一個命令行工具,二者都會仔細檢查你的 composer.lock
文件,并且告訴你任何你需要更新的依賴。
處理 Composer 全局依賴
Composer 也可以處理全局依賴和他們的二進制文件。用法很直接,你所要做的就是在命令前加上global
前綴。如果你想安裝 PHPUnit 并使它全局可用,你可以運行下面的命令:
composer global require phpunit/phpunit這將會創建一個 ~/.composer
目錄存放全局依賴,要讓已安裝依賴的二進制命令隨處可用,你需要添加 ~/.composer/vendor/bin
目錄到你的 $PATH
變量。
其他學習 Composer 相關資源PEAR 介紹
PEAR 是另一個常用的依賴包管理器, 它跟 Composer 很類似,但是也有一些顯著的區別。
PEAR 需要擴展包有專屬的結構, 開發者在開發擴展包的時候要提前考慮為 PEAR 定制, 否則后面將無法使用 PEAR.
PEAR 安裝擴展包的時候, 是全局安裝的, 意味著一旦安裝了某個擴展包, 同一臺服務器上的所有項目都能用上, 當然, 好處是當多個項目共同使用同一個擴展包的同一個版本, 壞處是如果你需要使用不同版本的話, 就會產生沖突.
如何安裝 PEAR
你可以通過下載 .phar
文件來安裝 PEAR. 官方文檔安裝部分 里面有不同系統中安裝 PEAR 的詳細信息.
如果你是使用 linux, 你可以嘗試找下系統應用管理器, 舉個栗子, Debian 和 Ubuntu 有個 php-pear
的 apt 安裝包.
如何安裝擴展包
如果擴展包是在 PEAR packages list 這個列表里面的, 你可以使用以下命令安裝:
pear install foo如果擴展包是托管到別的渠道上, 你需要 發現 (discover)
渠道先, 請見文檔 使用渠道.
Learn about PEAR使用 Composer 來安裝 PEAR 擴展包
如果你正在使用 Composer, 并且你想使用一些 PEAR 的代碼, 你可以通過 Composer 來安裝 PEAR 擴展包.
下面是從 pear2.php.net
安裝代碼依賴的示例:
{ "repositories": [ { "type": "pear", "url": "http://pear2.php.net" } ], "require": { "pear-pear2/PEAR2_Text_Markdown": "*", "pear-pear2/PEAR2_HTTP_Request": "*" }}第一部分 "repositories"
是讓 Composer 從哪個渠道去獲取擴展包, 然后, "repositories"
部分使用下面的命名規范:
pear-channel/Package
前綴 “pear” 是為了避免沖突寫死的.
成功安裝擴展包以后, 代碼會放到項目的 vendor
文件夾中, 并且可以通過加載 Composer 的自動加載器進行加載:
vendor/pear-pear2.php.net/PEAR2_HTTP_Request/pear2/HTTP/Request.php
在代碼里面可以這樣使用:
<?php$request = new pear2/HTTP/Request();學習更多 PEAR 和 Composer 的使用Back to Top
開發實踐
基礎知識
PHP 是一門龐大的語言,各個水平層次的開發者都可以利用它進行迅捷高效的開發。然而在對語言逐漸深入的學習過程中,我們往往會因為走捷徑和/或不良習慣而忘記(或忽視掉)基礎的知識。為了幫助徹底解決這個問題,這一章的目的就是提醒開發人員注意有關 PHP 的基礎編程實踐。
學習更多基礎知識日期和時間
PHP 中 DateTime 類的作用是在你讀、寫、比較或者計算日期和時間時提供幫助。除了 DateTime 類之外,PHP 還有很多與日期和時間相關的函數,但 DateTime 類為大多數常規使用提供了優秀的面向對象接口。它還可以處理時區,不過這并不在這篇簡短的介紹之內。
在使用 DateTime 之前,通過 createFromFormat()
工廠方法將原始的日期與時間字符串轉換為對象或使用 new DateTime
來取得當前的日期和時間。使用 format()
將 DateTime 轉換回字符串用于輸出。
<?php$raw = '22. 11. 1968';$start = DateTime::createFromFormat('d. m. Y', $raw);echo 'Start date: ' . $start->format('Y-m-d') . "/n";對 DateTime 進行計算時可以使用 DateInterval 類。DateTime 類具有例如 add()
和 sub()
等將 DateInterval 當作參數的方法。編寫代碼時注意不要認為每一天都是由相同的秒數構成的,不論是夏令時(DST)還是時區轉換,使用時間戳計算都會遇到問題,應當選擇日期間隔。使用 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 對象之間可以直接進行比較:
<?phpif ($start < $end) { echo "Start is before end!/n";}最后一個例子來演示 DatePeriod 類。它用來對循環的事件進行迭代。向它傳入開始時間、結束時間和間隔區間,會得到這其中所有的事件。
<?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('Y-m-d') . ' ';}閱讀 DateTime閱讀日期格式 (支持的日期字符串格式)設計模式
當你在編寫自己的應用程序時,最好在項目的代碼和整體架構中使用通用的設計模式,這將幫助你更輕松地對程序進行維護,也能夠讓其他的開發者更快地理解你的代碼。
當你使用框架進行開發時,絕大部分的上層代碼以及項目結構都會基于所使用的框架,因此很多關于設計模式的決定已經由框架幫你做好了。當然,你還是可以挑選你最喜歡的模式并在你的代碼中進行應用。但如果你并沒有使用框架的話,你就需要自己去尋找適合你的應用的最佳模式了。
學習更多設計模式使用 UTF-8 編碼
本章是由 Alex Cabal 最初撰寫在 PHP Best Practices 中的,我們使用它作為進行建議的基礎。
這不是在開玩笑。請小心、仔細并且前后一致地處理它。
目前,PHP 仍未在底層實現對 Unicode 的支持。雖然有很多途徑可以確保 UTF-8 字符串能夠被正確地處理,但這并不是很簡單的事情,通常需要對 Web 應用進行全方面的檢查,從 HTML 到 SQL 再到 PHP。我們將爭取進行一個簡潔實用的總結。
PHP 層面的 UTF-8
最基本的字符串操作,像是連結兩個字符串或將字符串賦值給變量,并不需要對 UTF-8 做特別的處理。然而大多數字符串的函數,像 strpos()
和 strlen()
,確實需要特別的處理。這些函數名中通常包含 mb_*
:比如,mb_strpos()
和 mb_strlen()
。這些 mb_*
字符串是由 Multibyte String Extension 提供支持的,它專門為操作 Unicode 字符串而特別進行了設計。
在操作 Unicode 字符串時,請你務必使用 mb_*
函數。例如,如果你對一個 UTF-8 字符串使用 substr()
,那返回的結果中有很大可能會包含一些亂碼。正確的方式是使用 mb_substr()
。
最難的地方在于每次都要記得使用 mb_*
函數。如果你哪怕只有一次忘記了使用,你的 Unicode 字符串就有在接下來的過程中變成亂碼的風險。
不是所有的字符串函數都有一個對應的 mb_*
函數。如果你想要的功能沒有對應的 mb_*
函數的話,那只能說你運氣不佳了。
你應該在你所有的 PHP 腳本(或全局包含的腳本)的開頭使用 mb_internal_encoding()
函數,然后緊接著在會對瀏覽器進行輸出的腳本中使用 mb_http_output()
。在每一個腳本當中明確聲明字符串的編碼可以免去很多日后的煩惱。
另外,許多對字符串進行操作的函數都有一個可選的參數用來指定字符串編碼。當可以設定這類參數時,你應該始終明確指定使用 UTF-8。例如,htmlentities()
有一個字符編碼的選項,你應該始終將其設為 UTF-8。從 PHP 5.4.0 開始, htmlentities()
和 htmlspecialchars()
的編碼都已經被默認設為了 UTF-8。
最后,如果你所編寫的是分布式的應用程序并且不能確定 mbstring
擴展一定開啟的話,可以考慮使用 patchwork/utf8 Composer 包。它會在 mbstring
可用時自動使用,否則自動切換回非 UTF-8 函數。
數據庫層面的 UTF-8
如果你使用 PHP 來操作到 MySQL,有些時候即使你做到了上面的每一點,你的字符串仍可能面臨在數據庫中以非 UTF-8 的格式進行存儲的問題。
為了確保你的字符串從 PHP 到 MySQL都使用 UTF-8,請檢查確認你的數據庫和數據表都設定為 utf8mb4
字符集和整理,并且確保你的 PDO 連接請求也使用了 utf8mb4
字符集。請看下方的示例代碼,這是 非常重要 的。
請注意為了完整的 UTF-8 支持,你必須使用 utf8mb4
而不是 utf8
!你會在進一步閱讀中找到原因。
瀏覽器層面的 UTF-8
使用 mb_http_output()
函數來確保 PHP 向瀏覽器輸出 UTF-8 格式的字符串。
隨后瀏覽器需要接收 HTTP 應答來指定頁面是由 UTF-8 進行編碼的。以前這一步是通過在頁面 <head>
標簽下包含字符集 <meta>
標簽實現的,這是一種可行的方式。但更好的做法是在 Content-Type
響應頭中進行設置,因為這樣做的速度會更快。
<?php// Tell PHP that we're using UTF-8 strings until the end of the scrWord', array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_PERSISTENT => false ));// Store our transformed string as UTF-8 in our database// Your DB and tables are in the utf8mb4 character set and collation, right?$handle = $link->prepare('insert into ElvishSentences (Id, Body) values (?, ?)');$handle->bindValue(1, 1, PDO::PARAM_INT);$handle->bindValue(2, $string);$handle->execute();// Retrieve the string we just stored to prove it was stored correctly$handle = $link->prepare('select * from ElvishSentences where Id = ?');$handle->bindValue(1, 1, PDO::PARAM_INT);$handle->execute();// Store the result into an object that we'll output later in our HTML$result = $handle->fetchAll(/PDO::FETCH_OBJ);header('Content-Type: text/html; charset=UTF-8');?><!doctype html><html> <head> <meta charset="UTF-8"> <title>UTF-8 test page</title> </head> <body> <?php foreach($result as $row){ print($row->Body); // This should correctly output our transformed UTF-8 string to the browser } ?> </body></html>延伸閱讀
PHP 手冊:字符串運算符PHP 手冊:字符串函數strpos()
strlen()
substr()
PHP 手冊:多字節字符串函數mb_strpos()
mb_strlen()
mb_substr()
mb_internal_encoding()
mb_http_output()
htmlentities()
htmlspecialchars()
PHP UTF-8 CheatsheetHandling UTF-8 with PHPStack Overflow: What factors make PHP Unicode-incompatible?Stack Overflow: Best practices in PHP and MySQL with international stringsHow to support full Unicode in MySQL databasesBringing Unicode to PHP with Portable UTF-8Stack Overflow: DOMDocument loaDHTML does not encode UTF-8 correctlyBack to Top
依賴注入
出自維基百科 Wikipedia:
依賴注入是一種允許我們從硬編碼的依賴中解耦出來,從而在運行時或者編譯時能夠修改的軟件設計模式。
這句解釋讓依賴注入的概念聽起來比它實際要復雜很多。依賴注入通過構造注入,函數調用或者屬性的設置來提供組件的依賴關系。就是這么簡單。
基本概念
我們可以用一個簡單的例子來說明依賴注入的概念
下面的代碼中有一個 Database
的類,它需要一個適配器來與數據庫交互。我們在構造函數里實例化了適配器,從而產生了耦合。這會使測試變得很困難,而且 Database
類和適配器耦合的很緊密。
<?phpnamespace Database;class Database{ protected $adapter; public function __construct() { $this->adapter = new MySqlAdapter; }}class MysqlAdapter {}這段代碼可以用依賴注入重構,從而解耦
<?phpnamespace Database;class Database{ protected $adapter; public function __construct(MySqlAdapter $adapter) { $this->adapter = $adapter; }}class MysqlAdapter {}現在我們通過外界給予 Database
類的依賴,而不是讓它自己產生依賴的對象。我們甚至能用可以接受依賴對象參數的成員函數來設置,或者如果 $adapter
屬性本身是 public
的,我們可以直接給它賦值。
復雜的問題
如果你曾經了解過依賴注入,那么你可能見過 “控制反轉”(Inversion of Control) 或者 “依賴反轉準則”(Dependency Inversion Principle)這種說法。這些是依賴注入能解決的更復雜的問題。
控制反轉
顧名思義,一個系統通過組織控制和對象的完全分離來實現”控制反轉”。對于依賴注入,這就意味著通過在系統的其他地方控制和實例化依賴對象,從而實現了解耦。
一些 PHP 框架很早以前就已經實現控制反轉了,但是問題是,應該反轉哪部分以及到什么程度?比如, MVC 框架通常會提供超類或者基本的控制器類以便其他控制器可以通過繼承來獲得相應的依賴。這就是控制反轉的例子,但是這種方法是直接移除了依賴而不是減輕了依賴。
依賴注入允許我們通過按需注入的方式更加優雅地解決這個問題,完全不需要任何耦合。
依賴反轉準則
依賴反轉準則是面向對象設計準則 S.O.L.I.D 中的 “D” ,倡導 “依賴于抽象而不是具體”。簡單來說就是依賴應該是接口/約定或者抽象類,而不是具體的實現。我們能很容易重構前面的例子,使之遵循這個準則
<?phpnamespace Database;class Database{ protected $adapter; public function __construct(AdapterInterface $adapter) { $this->adapter = $adapter; }}interface AdapterInterface {}class MysqlAdapter implements AdapterInterface {}現在 Database
類依賴于接口,相比依賴于具體實現有更多的優勢。
假設你工作的團隊中,一位同事負責設計適配器。在第一個例子中,我們需要等待適配器設計完之后才能單元測試?,F在由于依賴是一個接口/約定,我們能輕松地模擬接口測試,因為我們知道同事會基于約定實現那個適配器
這種方法的一個更大的好處是代碼擴展性變得更高。如果一年之后我們決定要遷移到一種不同的數據庫,我們只需要寫一個實現相應接口的適配器并且注入進去,由于適配器遵循接口的約定,我們不需要額外的重構。
容器
你應該明白的第一件事是依賴注入容器和依賴注入不是相同的概念。容器是幫助我們更方便地實現依賴注入的工具,但是他們通常被誤用來實現反模式設計 Service Location 。把一個依賴注入容器作為 Service Locator 注入進類中隱式地建立了對于容器的依賴,而不是真正需要替換的依賴,而且還會讓你的代碼更不透明,最終變得更難測試。
大多數現代的框架都有自己的依賴注入容器,允許你通過配置將依賴綁定在一起。這實際上意味著你能寫出和框架層同樣干凈、解耦的應用層代碼。
延伸閱讀
Learning about Dependency Injection and PHPWhat is Dependency Injection?Dependency Injection: An analogyDependency Injection: Huh?Dependency Injection as a tool for testingBack to Top
數據庫
絕大多數時候你的 PHP 程序都需要使用數據庫來長久地保存數據。這時你有一些不同的選擇可以來連接并與數據庫進行交互。在 PHP 5.1.0 之前,我們推薦的方式是使用例如 mysqli,pgsql,mssql 等原生驅動。
原生驅動是在只使用 一個 數據庫的情況下的不錯的方式,但如果,舉個例子來說,你同時使用了 MySQL 和一點點 MSSQL,或者你需要使用 Oracle 的數據庫,那你就不能夠只使用一個數據庫驅動了。你需要為每一個數據庫去學習各自不同的 API — 這樣做顯然不科學。
MySQL 擴展
PHP 中的 mysql 擴展已經不再進行新的開發了,并且已經在 PHP 5.5.0 版本中正式被廢棄,這意味著它將會在接下來的更新中被移除。如果你使用 mysql_*
開頭的函數,例如 mysql_connect()
和 mysql_query()
的話,它們將會在后續的 PHP 版本無法使用。因此在以后的某個時間你需要重寫你的代碼。最好的辦法是在你的開發計劃中使用 mysqli 或 PDO 來取代 mysql 擴展,這樣你才不會在后面手忙腳亂。
如果你正從零開始,請一定避免使用 mysql 擴展:請選擇 MySQLi 擴展,或者使用 PDO。
PHP: MySQL增強版擴展MySQL 開發者 PDO 使用教程PDO 擴展
PDO 是一個數據庫連接抽象類庫 — 自 5.1.0 版本起內置于 PHP 當中 — 它提供了一個通用的接口來與不同的數據庫進行交互。比如你可以使用相同的簡單代碼來連接 MySQL 或是 SQLite:
<?php// PDO + MySQL$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');$statement = $pdo->query("SELECT some_field FROM some_table");$row = $statement->fetch(PDO::FETCH_ASSOC);echo htmlentities($row['some_field']);// PDO + SQLite$pdo = new PDO('sqlite:/path/db/foo.sqlite');$statement = $pdo->query("SELECT some_field FROM some_table");$row = $statement->fetch(PDO::FETCH_ASSOC);echo htmlentities($row['some_field']);PDO 并不會對 SQL 請求進行轉換或者模擬實現并不存在的功能特性;它只是單純地使用相同的 API 連接不同種類的數據庫。
更重要的是,PDO
使你能夠安全的插入外部輸入(例如 ID)到你的 SQL 請求中而不必擔心 SQL 注入的問題。這可以通過使用 PDO 語句和限定參數來實現。
我們來假設一個 PHP 腳本接收一個數字 ID 作為一個請求參數。這個 ID 應該被用來從數據庫中取出一條用戶記錄。下面是一個錯誤
的做法:
<?php$pdo = new PDO('sqlite:/path/db/users.db');$pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NO!這是一段糟糕的代碼。你正在插入一個原始的請求參數到 SQL 請求中。這將讓被黑客輕松地利用[SQL 注入]方式進行攻擊。想一下如果黑客將一個構造的 id
參數通過像 http://domain.com/?id=1%3BDELETE+FROM+users
這樣的 URL 傳入。這將會使 $_GET['id']
變量的值被設為 1;DELETE FROM users
然后被執行從而刪除所有的 user 記錄!因此,你應該使用 PDO 限制參數來過濾 ID 輸入。
<?php$pdo = new PDO('sqlite:/path/db/users.db');$stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id');$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filter your data first (see [Data Filtering](#data_filtering)), especially important for INSERT, UPDATE, etc.$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- Automatically sanitized for SQL by PDO$stmt->execute();這是正確的代碼。它在一條 PDO 語句中使用了一個限制參數。這將對外部 ID 輸入在發送給數據庫之前進行轉義來防止潛在的 SQL 注入攻擊。
對于寫入操作,例如 INSERT 或者 UPDATE,進行數據過濾并對其他內容進行清理(去除 HTML 標簽,javascript 等等)是尤其重要的。PDO 只會為 SQL 進行清理,并不會為你的應用做任何處理。
了解 PDO你也應該知道數據庫連接有時會耗盡全部資源,如果連接沒有被隱式地關閉的話,有可能會造成可用資源枯竭的情況。不過這通常在其他語言中更為常見一些。使用 PDO 你可以通過銷毀(destroy)對象,也就是將值設為 NULL,來隱式地關閉這些連接,確保所有剩余的引用對象的連接都被刪除。如果你沒有親自做這件事情,PHP 會在你的腳本結束的時候自動為你完成 —— 除非你使用的是持久鏈接。
了解 PDO 連接數據庫交互
當開發者第一次接觸 PHP 時,通常會使用類似下面的代碼來將數據庫的交互與表示層邏輯混在一起:
<ul><?phpforeach ($db->query('SELECT * FROM table') as $row) { echo "<li>".$row['field1']." - ".$row['field1']."</li>";}?></ul>這從很多方面來看都是錯誤的做法,主要是由于它不易閱讀又難以測試和調試。而且如果你不加以限制的話,它會輸出非常多的字段。
其實還有許多不同的解決方案來完成這項工作 — 取決于你傾向于 面向對象編程(OOP)還是函數式編程 — 但必須有一些分離的元素。
來看一下最基本的做法:
<?phpfunction getAllFoos($db) { return $db->query('SELECT * FROM table');}foreach (getAllFoos($db) as $row) { echo "<li>".$row['field1']." - ".$row['field1']."</li>"; // BAD!!}這是一個不錯的開頭。將這兩個元素放入了兩個不同的文件于是你得到了一些干凈的分離。
創建一個類來放置上面的函數,你就得到了一個「Model」。創建一個簡單的.php
文件來存放表示邏輯,你就得到了一個「View」。這已經很接近 MVC — 一個大多數框架常用的面向對象的架構。
foo.php
<?php$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');// Make your model availableinclude 'models/FooModel.php';// Create an instance$fooModel = new FooModel($db);// Get the list of Foos$fooList = $fooModel->getAllFoos();// Show the viewinclude 'views/foo-list.php';models/FooModel.php
<?phpclass FooModel{ protected $db; public function __construct(PDO $db) { $this->db = $db; } public function getAllFoos() { return $this->db->query('SELECT * FROM table'); }}views/foo-list.php
<?php foreach ($fooList as $row): ?> <?= $row['field1'] ?> - <?= $row['field1'] ?><?php endforeach ?>向大多數現代框架的做法學習是很有必要的,盡管多了一些手動的工作。你可以并不需要每一次都完全這么做,但將太多的表示邏輯層代碼和數據庫交互摻雜在一些將會為你在想要對程序進行單元測試時帶來真正的麻煩。
PHPBridge 具有一項非常棒的資源叫做創建一個數據類。它包含了非常相似的邏輯而且非常適合剛剛習慣數據庫交互概念的開發者使用。
數據庫抽象層
許多框架都提供了自己的數據庫抽象層,其中一些是設計在 PDO 的上層的。這些抽象層通常將你的請求在 PHP 方法中包裝起來,通過模擬的方式來使你的數據庫擁有一些之前不支持的功能。這種抽象是真正的數據庫抽象,而不單單只是 PDO 提供的數據庫連接抽象。這類抽象的確會增加一定程度的性能開銷,但如果你正在設計的應用程序需要同時使用 MySQL,PostgreSQL 和 SQLite 時,一點點的額外性能開銷對于代碼整潔度的提高來說還是很值得的。
有一些抽象層使用的是PSR-0 或 PSR-4 命名空間標準,所以他們可以安裝在任何你需要的應用程序中。
Aura SQLDoctrine2 DBALPropelZF2 DbBack to Top
使用模板
模板提供了一種簡便的方式,將展現邏輯從控制器和業務邏輯中分離出來。
一般來說,模板包含應用程序的 HTML 代碼,但也可以使用其他的格式,例如 xml 。
模板通常也被稱為「視圖」, 而它是 模型-視圖-控制器 (MVC) 軟件架構模式第二個元素的 一部份 。
好處
使用模板的主要好處是可以將呈現邏輯與應用程序的其他部分進行分離。模板的單一職責就是呈現格式化后的內容。它不負責數據的查詢,保存或是其他復雜的任務。進一步促成了更干凈、更具可讀性的代碼,在團隊協作開發中尤其有用,開發者可以專注服務端的代碼(控制器、模型),而設計師負責客戶端代碼 (網頁) 。
模板同時也改善了前端代碼的組織架構。一般來說,模板放置在「視圖」文件夾中,每一個模板都放在獨立的一個文件中。這種方式鼓勵代碼重用,它將大塊的代碼拆成較小的、可重用的片段,通常稱為局部模板。舉例來說,網站的頭、尾區塊可以各自定義為一個模板,之后將它們放在每一個頁面模板的上、下位置。
最后,根據你選擇的類庫,模板可以通過自動轉義用戶的內容,從而帶來更多的安全性。有些類庫甚至提供沙箱機制,模板設計者只能使用在白名單中的變量和函數。
原生 PHP 模板
原生 PHP 模板就是指直接用 PHP 來寫模板,這是很自然的選擇,因為 PHP 本身其實是個模板語言。這代表你可以在其他的語言中結合 PHP 使用,比如 HTML 。這對 PHP 開發者相當有利,因為不需要額外學習新的語法,他們熟知可以使用的函數,并且使用的編輯器也已經內置了語法高亮和自動補全。此外,原生的 PHP 模板沒有了編譯階段,速度會更快。
現今的 PHP 框架都會使用一些模板系統,這當中多數是使用原生的 PHP 語法。在框架之外,一些類庫比如 Plates 或 Aura.View,提供了現代化模板的常見功能,比如繼承、布局、擴展,讓原生的 PHP 模板更容易使用。
原生 PHP 模板的簡單示例
使用 Plates 類庫。
<?php // user_profile.php ?><?php $this->insert('header', ['title' => 'User Profile']) ?><h1>User Profile</h1><p>Hello, <?=$this->escape($name)?></p><?php $this->insert('footer') ?>原生 PHP 模板使用繼承的示例
使用 Plates 類庫。
<?php // template.php ?><html><head> <title><?=$title?></title></head><body><main> <?=$this->section('content')?></main></body></html><?php // user_profile.php ?><?php $this->layout('template', ['title' => 'User Profile']) ?><h1>User Profile</h1><p>Hello, <?=$this->escape($name)?></p>編譯模板
盡管 PHP 不斷升級為成熟的、面向對象的語言,但它作為模板語言 沒有改善多少。編譯模板,比如 Twig 或 Smarty* ,提供了模板專用的新語法,填補了這片空白。從自動轉義到繼承以及簡化控制結構,編譯模板設計地更容易編寫,可讀性更高,同時使用上也更加的安全。編譯模板甚至可以在不同的語言中使用,Mustache 就是一個很好的例子。由于這些模板需要編譯,在性能上會帶來一些輕微的影響,不過如果適當的使用緩存,影響就變得非常小了。
*雖然 Smarty 提供了自動轉義的功能, 不過這個功能默認是關閉的
編譯模板簡單示例
使用 Twig 類庫。
{% include 'header.html' with {'title': 'User Profile'} %}<h1>User Profile</h1><p>Hello, {{ name }}</p>{% include 'footer.html' %}編譯模板使用繼承示例
使用 Twig 類庫。
// template.html<html><head> <title>{% block title %}{% endblock %}</title></head><body><main> {% block content %}{% endblock %}</main></body></html>// user_profile.html{% extends "template.html" %}{% block title %}User Profile{% endblock %}{% block content %} <h1>User Profile</h1> <p>Hello, {{ name }}</p>{% endblock %}延伸閱讀
文章與教程
Templating Engines in PHPAn Introduction to Views & Templating in CodeIgniterGetting Started With PHP TemplatingRoll Your Own Templating System in PHPMaster PagesWorking With Templates in Symfony 2Writing Safer Templates類庫
Aura.View (native)Blade (compiled, framework specific)Brainy (compiled)Dwoo (compiled)Latte (compiled)Mustache (compiled)PHPTAL (compiled)Plates (native)Smarty (compiled)Twig (compiled)Zend/View (native, framework specific)Back to Top
錯誤與異常
錯誤
在許多「重異常」(exception-heavy) 的編程語言中,一旦發生錯誤,就會拋出異常。這確實是一個可行的方式。不過 PHP 卻是一個 「輕異?!?exception-light) 的語言。當然它確實有異常機制,在處理對象時,核心也開始采用這個機制來處理,只是 PHP 會盡可能的執行而無視發生的事情,除非是一個嚴重錯誤。
舉例來說:
$ php -aphp > echo $foo;Notice: Undefined variable: foo in php shell code on line 1這里只是一個 notice 級別的錯誤,PHP 仍然會愉快的繼續執行。這對有「重異常」編程經驗的人來說會帶來困惑,例如在 Python 中,引用一個不存在的變量會拋出異常:
$ python>>> print fooTraceback (most recent call last): File "<stdin>", line 1, in <module>NameError: name 'foo' is not defined本質上的差異在于 Python 會對任何小錯誤進行拋錯,因此開發人員可以確信任何潛在的問題或者邊緣的案例都可以被捕捉到,與此同時 PHP 仍然會保持執行,除非極端的問題發生才會拋出異常。
錯誤嚴重性
PHP 有幾個錯誤嚴重性等級。三個最常見的的信息類型是錯誤(error)、通知(notice)和警告(warning)。它們有不同的嚴重性: E_ERROR
、E_NOTICE
和 E_WARNING
。錯誤是運行期間的嚴重問題,通常是因為代碼出錯而造成,必須要修正它,否則會使 PHP 停止執行。通知是建議性質的信息,是因為程序代碼在執行期有可能造成問題,但程序不會停止。 警告是非致命錯誤,程序執行也不會因此而中止。
另一個在編譯期間會報錯的信息類型是「E_STRICT」。這個信息用來建議修改程序代碼以維持最佳的互通性并能與今后的 PHP 版本兼容。
更改 PHP 錯誤報告行為
錯誤報告可以由 PHP 配置及函數調用改變。使用 PHP 內置的函數 error_reporting()
,可以設定程序執行期間的錯誤等級,方法是傳入預定義的錯誤等級常量,意味著如果你只想看到警告和錯誤(而非通知),你可以這樣設定:
<?phperror_reporting(E_ERROR | E_WARNING);你也可以控制錯誤是否在屏幕上顯示 (開發時比較有用)或隱藏后記錄日志 (適用于正式環境)。如果想知道更多細節,可以查看 錯誤報告 章節。
行內錯誤抑制
你可以讓 PHP 利用錯誤控制操作符 @
來抑制特定的錯誤。將這個操作符放置在表達式之前,其后的任何錯誤都不會出現。
<?phpecho @$foo['bar'];如果 $foo['bar']
存在,程序會將結果輸出,如果變量 $foo
或是 'bar'
鍵值不存在,則會返回 null 并且不輸出任何東西。如果不使用錯誤控制操作符,這個表達式會產生一個錯誤信息 PHP Notice: Undefined variable: foo
或 PHP Notice: Undefined index: bar
。
這看起來像是個好主意,不過也有一些討厭的代價。PHP 處理使用 @
的表達式比起不用時效率會低一些。過早的性能優化在所有程序語言中也許都是爭論點,不過如果性能在你的應用程序 / 類庫中占有重要地位,那么了解錯誤控制操作符的性能影響就比較重要。
其次,錯誤控制操作符會 完全 吃掉錯誤。不但沒有顯示,而且也不會記錄在錯誤日志中。此外,在正式環境中 PHP 也沒有辦法關閉錯誤控制操作符。也許你認為那些錯誤時無害的,不過那些較具傷害性的錯誤同時也會被隱藏。
如果有方法可以避免錯誤抑制符,你應該考慮使用,舉例來說,上面的程序代碼可以這樣重寫:
<?phpecho isset($foo['bar']) ? $foo['bar'] : '';當 fopen()
載入文件失敗時,也許是一個使用錯誤抑制符的合理例子。你可以在嘗試載入文件前檢查是否存在,但是如果這個文件在檢查后才被刪除,而此時 fopen()
還未執行 (聽起來有點不太可能,但是確實會發生),這時 fopen()
會返回 false 并且 拋出操作。這也許應該由 PHP 本身來解決,但這時一個錯誤抑制符才能有效解決的例子。
前面我們提到在正式的 PHP 環境中沒有辦法關閉錯誤控制操作符。但是 Xdebug 有一個 xdebug.scream
的 ini 配置項,可以關閉錯誤控制操作符。你可以按照下面的方式修改 php.ini
。
xdebug.scream = On你也可以在執行期間通過 ini_set
函數來設置這個值:
<?phpini_set('xdebug.scream', '1')「Scream」這個 PHP 擴展提供了和 xDebug 類似的功能,只是 Scream 的 ini 設置項叫做 scream.enabled
。
當你在調試代碼而錯誤信息被隱藏時,這是最有用的方法。請務必小心使用 scream ,而是把它當作暫時性的調試工具。有許多的 PHP 函數類庫代碼也許無法在錯誤抑制操作符停用時正常使用。
Error Control OperatorsSitePointXdebugScream錯誤異常類
PHP 可以完美化身為「重異常」的程序語言,只需要幾行代碼就能切換過去。基本上你可以利用 ErrorException
類拋出「錯誤」來當做「異常」,這個類是繼承自 Exception
類。
這在大量的現代框架中是一個常見的做法,比如 Symfony 和 Laravel。Laravel 默認使用 Whoops! 擴展包來處理錯誤,如果 app.debug
啟動的話,會將錯誤當成異常顯示出來,而關閉則會隱藏。
在開發過程中將錯誤當作異常拋出可以更好的處理它,如果在開發時發生異常,你可以將它包在一個 catch 語句中具體說明這種情況如何處理。每捕捉一個異常,都會使你的應用程序越來越健壯。
更多關于如何使用 ErrorException
來處理錯誤的細節,可以參考 ErrorException Class。
Error Control OperatorsPredefined Constants for Error Handlingerror_reporting()
Reporting異常
異常是許多流行編程語言的標配,但它們往往被 PHP 開發人員所忽視。像 Ruby 就是一個極度重視異常的語言,無論有什么錯誤發生,像是 HTTP 請求失敗,或者數據庫查詢有問題,甚至找不到一個圖片資源,Ruby (或是所使用的 gems),將會拋出異常,你可以通過屏幕立刻知道所發生的問題。
PHP 處理這個問題則比較隨意,調用 file_get_contents()
函數通常只會給出 FALSE
值和警告。許多較早的 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){ // 驗證失敗}catch(Fuel/Email/SendingFailedException $e){ // 這個驅動無法發送 email}finally{ // 無論拋出什么樣的異常都會執行,并且在正常程序繼續之前執行}SPL 異常
原生的 Exception
類并沒有提供太多的調試情境給開發人員,不過可以通過建立一個特殊的 Exception
來彌補它,方式就是建立一個繼承自原生 Exception
類的一個子類:
<?phpclass ValidationException extends Exception {}如此一來,可以加入多個 catch 區塊,并且根據不同的異常分別處理。通過這樣可以建立 許多自定義異常,其中有些已經在 SPL 擴展 提供的 SPL 異常中定義了。
舉例來說,如果你使用了 __call()
魔術方法去調用一個無效的方法,而不是拋出一個模糊的標準 Exception 或是建立自定義的異常處理,你可以直接拋出 throw new BadMethodCallException;
。
Read about ExceptionsRead about SPL ExceptionsNesting Exceptions In PHPException Best Practices in PHP 5.3Back to Top
安全
Web 應用程序安全
攻擊者無時無刻不在準備對你的 Web 應用程序進行攻擊,因此提高你的 Web 應用程序的安全性是非常有必要的。幸運的是,來自開放式 Web 應用程序安全項目 (OWasp) 的有心人已經整理了一份包含了已知安全問題和防御方式的全面的清單。這份清單對于具有安全意識的開發者來說是必讀的。
閱讀 OWASP 安全指南密碼哈希
每個人在建構 PHP 應用時終究都會加入用戶登錄的模塊。用戶的帳號及密碼會被儲存在數據庫中,在登錄時用來驗證用戶。
在存儲密碼前正確的哈希密碼是非常重要的。哈希密碼是單向不可逆的,該哈希值是一段固定長度的字符串且無法逆向推算出原始密碼。這就代表你可以哈希另一串密碼,來比較兩者是否是同一個密碼,但又無需知道原始的密碼。如果你不將密碼哈希,那么當未授權的第三者進入你的數據庫時,所有用戶的帳號資料將會一覽無遺。有些用戶可能(很不幸的)在別的網站也使用相同的密碼。所以務必要重視數據安全的問題。
使用 password_hash
來哈希密碼
password_hash
函數在 PHP 5.5 時被引入。 此函數現在使用的是目前 PHP 所支持的最強大的加密算法 BCrypt 。 當然,此函數未來會支持更多的加密算法。 password_compat
庫的出現是為了提供對 PHP >= 5.3.7 版本的支持。
在下面例子中,我們哈希一個字符串,然后和新的哈希值對比。因為我們使用的兩個字符串是不同的(’secret-password’ 與 ‘bad-password’),所以登錄失敗。
<?phprequire 'password.php';$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);if (password_verify('bad-password', $passwordHash)) { // Correct Password} else { // Wrong password}了解 password_hash()
PHP >= 5.3.7 && < 5.5 的 password_compat
了解密碼學中的哈希PHP password_hash()
RFC數據過濾
永遠不要信任外部輸入。請在使用外部輸入前進行過濾和驗證。filter_var()
和 filter_input()
函數可以過濾文本并對格式進行校驗(例如 email 地址)。
外部輸入可以是任何東西:$_GET
和 $_POST
等表單輸入數據,$_SERVER
超全局變量中的某些值,還有通過 fopen('php://input', 'r')
得到的 HTTP 請求體。記住,外部輸入的定義并不局限于用戶通過表單提交的數據。上傳和下載的文檔,session 值,cookie 數據,還有來自第三方 web 服務的數據,這些都是外部輸入。
雖然外部輸入可以被存儲、組合并在以后繼續使用,但它依舊是外部輸入。每次你處理、輸出、連結或在代碼中包含時,請提醒自己檢查數據是否已經安全地完成了過濾。
數據可以根據不同的目的進行不同的 過濾 。比如,當原始的外部輸入被傳入到了 HTML 頁面的輸出當中,它可以在你的站點上執行 HTML 和 Javascript 腳本!這屬于跨站腳本攻擊(XSS),是一種很有殺傷力的攻擊方式。一種避免 XSS 攻擊的方法是在輸出到頁面前對所有用戶生成的數據進行清理,使用 strip_tags()
函數來去除 HTML 標簽或者使用 htmlentities()
或是 htmlspecialchars()
函數來對特殊字符分別進行轉義從而得到各自的 HTML 實體。
另一個例子是傳入能夠在命令行中執行的選項。這是非常危險的(同時也是一個不好的做法),但是你可以使用自帶的 escapeshellarg()
函數來過濾執行命令的參數。
最后的一個例子是接受外部輸入來從文件系統中加載文件。這可以通過將文件名修改為文件路徑來進行利用。你需要過濾掉"/"
, "../"
, null 字符或者其他文件路徑的字符來確保不會去加載隱藏、私有或者敏感的文件。
學習更多數據過濾學習更多 filter_var
學習更多 filter_input
學習更多 null 字符問題數據清理
數據清理是指刪除(或轉義)外部輸入中的非法和不安全的字符。
例如,你需要在將外部輸入包含在 HTML 中或者插入到原始的 SQL 請求之前對它進行過濾。當你使用 PDO 中的限制參數功能時,它會自動為你完成過濾的工作。
有些時候你可能需要允許一些安全的 HTML 標簽輸入進來并被包含在輸出的 HTML 頁面中,但這實現起來并不容易。盡管有一些像 HTML Purifier 的白名單類庫為了這個原因而出現,實際上更多的人通過使用其他更加嚴格的格式限制方式例如使用 Markdown 或 BBCode 來避免出現問題。
查看 Sanitization Filters
有效性驗證
驗證是來確保外部輸入的是你所想要的內容。比如,你也許需要在處理注冊申請時驗證 email 地址、手機號碼或者年齡等信息的有效性。
查看 Validation Filters
配置文件
當你在為你的應用程序創建配置文件時,最好的選擇時參照以下的做法:
推薦你將你的配置信息存儲在無法被直接讀取和上傳的位置上。如果你一定要存儲配置文件在根目錄下,那么請使用 .php
的擴展名來進行命名。這將可以確保即使腳本被直接訪問到,它也不會被以明文的形式輸出出來。配置文件中的信息需要被針對性的保護起來,對其進行加密或者設置訪問權限。注冊全局變量
注意: 自 PHP 5.4.0 開始,register_globals
選項已經被移除并不再使用。這是在提醒你如果你正在升級舊的應用程序的話,你需要注意這一點。
當 register_globals
選項被開啟時,它會使許多類型的變量(包括 $_POST
, $_GET
和 $_REQUEST
)被注冊為全局變量。這將很容易使你的程序無法有效地判斷數據的來源并導致安全問題。
例如:$_GET['foo']
可以通過 $foo
被訪問到,也就是可以對未聲明的變量進行覆蓋。如果你使用低于 5.4.0 版本的 PHP 的話,請 確保register_globals
是被設為 off 的。
在 PHP 手冊中了解 Register_globals錯誤報告
錯誤日志對于發現程序中的錯誤是非常有幫助的,但是有些時候它也會將應用程序的結構暴露給外部。為了有效的保護你的應用程序不受到由此而引發的問題。你需要將在你的服務器上使用開發和生產(線上)兩套不同的配置。
開發環境
為了在開發環境中顯示所有可能的錯誤,將你的 php.ini
進行如下配置:
display_errors = Ondisplay_startup_errors = Onerror_reporting = -1log_errors = On將值設為 -1
將會顯示出所有的錯誤,甚至包括在未來的 PHP 版本中新增加的類型和參數。 和 PHP 5.4 起開始使用的 E_ALL
是相同的。- php.net
E_STRICT
類型的錯誤是在 5.3.0 中被引入的,并沒有被包含在 E_ALL
中。然而從 5.4.0 開始,它被包含在了 E_ALL
中。這意味著什么?這表示如果你想要在 5.3 中顯示所有的錯誤信息,你需要使用 -1
或者 E_ALL | E_STRICT
。
不同 PHP 版本下開啟全部錯誤顯示
< 5.3 -1
或 E_ALL
5.3 -1
或 E_ALL | E_STRICT
> 5.3 -1
或 E_ALL
生產環境
為了在生產環境中隱藏錯誤顯示,將你的 php.ini
進行如下配置:
display_errors = Offdisplay_startup_errors = Offerror_reporting = E_ALLlog_errors = On當在生產環境中使用這個配置時,錯誤信息依舊會被照常存儲在 web 服務器的錯誤日志中,唯一不同的是將不再顯示給用戶。更多關于設置的信息,請參考 PHP 手冊:
錯誤報告顯示錯誤顯示啟動錯誤記錄錯誤Back to Top
測試
為你的 PHP 程序編寫自動化測試被認為是最佳實踐,可以幫助你建立良好的應用程序。 自動化測試是非常棒的工具,它能確保你的應用程序在改變或增加新的功能時不會影響現有的功能,不應該忽視。
PHP 有一些不同種類的測試工具 (或框架) 可以使用,它們使用不同的方法 - 但他們都試圖避免手動測試和大型 QA 團隊的需求,確保最近的變更不會破壞既有功能。
測試驅動開發
Wikipedia 上的定義: > 測試驅動開發 (TDD) 是一種以非常短的開發周期不斷迭代的軟件開發過程:首先開發者對將要實現的功能或者新的方法寫一個失敗的自動化測試用例,然后就去寫代碼來通過這個測試用例,最終通過重構代碼讓一其達到可接受的水準。Kent Beck, 這個技術創造者或者說重新發現者,在2003年聲明TDD 鼓勵簡單的設計和激勵信心。
目前你可以應用的幾種不同類型的測試:
單元測試
單元測試是一種編程方法來確認函數,類和方法以我們預期的方式來工作,單元測試會貫穿整個項目的開發周期。通過檢查各個函數和方法的輸入輸出,你就可以保證內部的邏輯已經正確執行。通過使用依賴注入和編寫”mock” 類以及 stubs 來確認依賴被正確的使用,提高測試覆蓋率。
當你創建一個類或者一個函數,你應該為它們的每一個行為創建一個單元測試。至少你應該確認當你輸入一個錯誤參數會觸發一個錯誤,你輸入一個有效的參數會得到正確的結果。這會幫助你在開發周期后段對類或者函數做出修改后,確認已有的功能任然可以正常的工作??商娲姆椒ㄊ窃谠创a中使用 var_dump()
,但這種方法卻不能去構建一個或大或小的應用。
單元測試的其他用處是在給開源項目貢獻代碼時。如果你寫了一個測試證明代碼有bug,然后修復它,并且展示測試的過程,這樣補丁將會更容易被接受。如果你在維護一個項目,在處理 pull request 的時候可以將單元測試作為一個要求。
PHPUnit 是業界PHP應用開發單元測試框架的標準,但也有其他可選的框架:
atoumEnhance PHPPUnitSimpleTest集成測試
Wikipedia 上的定義: > 集成測試 (有時候稱為集成和測試,縮寫為 I&T
)是把各個模塊組合在一起進行整體測試的軟件測試階段。它處于單元測試之后,驗收測試之前。集成測試將已經經過了單元測試的模塊做為輸入模塊,組合成一個整體,然后運行集成測試用例,然后輸出一個可以進行系統測試的系統。
許多相同的測試工具既可以運用到單元測試,也可以運用到集成測試。
功能性測試
有時候也被稱之為驗收測試,功能測試是通過使用工具來生成自動化的測試用例,然后在真實的系統上運行。而不是單元測試中簡單的驗證單個模塊的正確性和集成測試中驗證各個模塊間交互的正確性。這些工具會使用代表性的真實數據來模擬真實用戶的行為來驗證系統的正確性。
功能測試的工具
SeleniumMinkCodeception 是一個全棧的測試框架包括驗收性測試工具。Storyplayer 是一個全棧的測試框架并且支持隨時創建和銷毀測試環境。行為驅動開發
有兩種不同的行為驅動開發 (BDD): SpecBDD 和 StoryBDD。 SpecBDD 專注于代碼的技術行為,而 StoryBDD 專注于業務邏輯或功能的行為和互動。這兩種 BDD 都有對應的 PHP 框架。
采用 StoryBDD 時, 你編寫可讀的故事來描述應用程序的行為。接著這些故事可以作為應用程序的實際測試案例執行。Behat 是使用在 PHP 應用程序中的 StoryBDD框架,它受到 Ruby 的 Cucumber 項目的啟發并且實現了 Gherkin DSL 來描述功能的行為。
采用 SpecBDD 時, 你編寫規格來描述實際的代碼應該有什么行為。你應該描述函數或者方法應該有什么行為,而不是測試函數或者方法。PHP 提供了 PHPSpec 框架來達到這個目的,這個框架受到了 Ruby 的 RSpec project 項目的啟發。
BDD 鏈接
Behat, PHP 的 StoryBDD 框架, 受到了 Ruby’s Cucumber 項目的啟發。PHPSpec, PHP 的 SpecBDD 框架, 受到了 Ruby’s RSpec 項目的啟發。Codeception 是一個使用 BDD 準則的全棧測試框架。其他測試工具
除了個別的測試驅動和行為驅動框架之外,還有一些通用的框架和輔助函數類庫,對任何的測試方法都很有用。
工具地址
Selenium 是一個瀏覽器自動化工具 integrated with PHPUnitMockery 是一個可以跟 PHPUnit 或者 PHPSpec 整合的 Mock 對象框架Prophecy 是個有自己的想法,且非常強大靈活的 PHP 對象 mocking 框架。它整合了 PHPSpec 并且可以和 PHPUnit 一起使用Back to Top
服務器與部署
部署 PHP 應用程序到生產環境中有多種方式。
Platform as a Service (PaaS)
PaaS 提供了運行 PHP 應用程序所必須的系統環境和網絡架構。這就意味著只需做少量配置就可以運行 PHP 應用程序或者 PHP 框架。
現在,PaaS 已經成為一種部署、托管和擴展各種規模的 PHP 應用程序的流行方式。你可以在 資源部分 查看 PHP PaaS “Platform as a Service” 提供商列表。
虛擬或專用服務器
如果你喜歡系統管理員的工作,或者對這方面感興趣,虛擬或者專用服務器可以讓你完全控制自己的生產環境。
nginx 和 PHP-FPM
PHP 通過內置的 FastCGI 進程管理器(FPM),可以很好的與輕量級的高性能 web 服務器 nginx 協作使用。nginx 比 Apache 占用更少內存而且可以更好的處理并發請求,這對于并沒有太多內存的虛擬服務器尤其重要。
閱讀更多 nginx 的內容閱讀更多 PHP-FPM 的內容學習如何配置安全的 nginx 和 PHP-FPMApache 和 PHP
PHP 和 Apache 有很長的合作歷史。Apache 有很強的可配置性和大量的 擴展模塊 。是共享主機中常見的Web服務器,完美支持各種 PHP 框架和開源應用(如 WordPress )。可惜的是,默認情況下,Apache 會比 nginx 消耗更多的資源,而且并發處理能力不強。
Apache 有多種方式運行 PHP,最常見的方式就是使用 mode_php5 的 prefork MPM 方式。但是它對內存的利用效率并不高,如果你不想深入服務器管理方面學習,那么這種簡單的方式可能是你最好的選擇。需要注意的事如果你使用 mod_php5,就必須使用 prefork MPM。
如果你追求高性能和高穩定性,可以為 Apache 選擇與 nginx 類似的的 FPM 系統 worker MPM 或者 event MPM,它們分別使用 mod_fastcgi 和 mod_fcgid。這種方式可以更高效的利用內存,運行速度也更快,但是配置也相對復雜一些。
閱讀更多 Apache閱讀更多多進程模塊閱讀更多 mod_fastcgi閱讀更多 mod_fcgid共享主機
PHP 非常流行,很少有服務器沒有安裝 PHP 的,因而有很多共享主機,不過需要注意服務器上的 PHP 是否是最新穩定 版本。共享主機允許多個開發者把自己的網站部署在上面,這樣的好處是費用非常便宜,壞處是你不知道將和哪些 網站共享主機,因此需要仔細考慮機器負載和安全問題。如果項目預算允許的話,避免使用共享主機是上策。
構建及部署應用
如果你在手動的進行數據庫結構的修改或者在更新文件前手動運行測試,請三思而后行!因為隨著每一個額外的手動任務的添加都需要去部署一個新的版本到應用程序,這些更改會增加程序潛在的致命錯誤。即使你是在處理一個簡單的更新,全面的構建處理或者持續集成策略,構建自動化絕對是你的朋友。
你可能想要自動化的任務有:
依賴管理靜態資源編譯、壓縮執行測試文檔生成打包部署構建自動化工具
構建工具可以認為是一系列的腳本來完成應用部署的通用任務。構建工具并不屬于應用的一部分,它獨立于應用層 ‘之外’。
現在已有很多開源的工具來幫助你完成構建自動化,一些是用 PHP 編寫,有一些不是。應該根據你的實際項目來選擇最適合的工具,不要讓語言阻礙了你使用這些工具,如下有一些例子:
Phing 是一種在 PHP 領域中最簡單的開始自動化部署的方式。通過 Phing 你可以控制打包,部署或者測試,只需要一個簡單的 XML 構建文件。Phing (基于Apache Ant) 提供了在安裝或者升級 web 應用時的一套豐富的任務腳本,并且可以通過 PHP 編寫額外的任務腳本來擴展。
Capistrano 是一個為 中高級程序員 準備的系統,以一種結構化、可復用的方式在一臺或多臺遠程機器上執行命令。對于部署 Ruby on Rails 的應用,它提供了預定義的配置,不過也可以用它來 部署 PHP 應用 。如果要成功的使用 Capistrano ,需要一定的 Ruby 和 Rake 的知識。
對 Capistrano 感興趣的 PHP 開發者可以閱讀 Dave Gardner 的博文 PHP Deployment with Capistrano ,來作為一個很好的開始。
Chef 不僅僅只是一個部署框架, 它是一個基于 Ruby 的強大的系統集成框架,除了部署你的應用之外,還可以構建整個服務環境或者虛擬機。
Deployer 是一個用 PHP 編寫的部署工具,它很簡單且實用。并行執行任務,原子化部署,在多臺服務器之間保持一致性。為 Symfony、Laravel、Zend Framework 和 Yii 提供了通用的任務腳本。
適用于 PHP 開發者的 Chef 資源:
Three part blog series about deploying a LAMP application with Chef, Vagrant, and EC2Chef Cookbook which installs and configures PHP 5.3 and the PEAR package management systemChef video tutorial series by Opscode, the makers of chef延伸閱讀:
Automate your project with Apache Ant持續集成
持續集成是一種軟件開發實踐,團隊的成員經常用來集成他們的工作, 通常每一個成員至少每天都會進行集成 — 因此每天都會有許多的集成。許多團隊發現這種方式會顯著地降低集成問題, 并允許一個團隊更快的開發軟件。
– Martin Fowler
對于 PHP 來說,有許多的方式來實現持續集成。近來 Travis CI 在持續集成上做的很棒,對于小項目來說也可以很好的使用。Travis CI 是一個托管的持續集成服務用于開源社區。它可以和 Github 很好的集成,并且提供了很多語言的支持包括 PHP 。
延伸閱讀:
使用 Jenkins 進行持續集成使用 PHPCI 進行持續集成使用 Teamcity 進行持續集成Back to Top
虛擬化技術
在開發和線上階段使用不同的系統運行環境的話, 經常會遇到各種各樣的 BUG, 并且在團隊開發的時候, 讓所有成員都保持使用最新版本的軟件和類庫, 也是一件很讓人頭痛的事情.
如果你是在 Windows 下開發, 線上環境是 Linux (或者別的非 Windows 系統) 的話, 或者團隊協同開發的時候, 建議使用虛擬機.
除了大家熟知的 VMware 和 VirtualBox 外, 還有很多工具可以讓你快速, 輕松的用上虛擬環境.
Vagrant 簡介
Vagrant 可以讓你使用單一的配置信息來部署一套虛擬環境, 最后打包為一個所謂的 box (就是已經部署好環境的虛擬機器). 你可以手動來安裝和配置 box, 也可以使用自動部署工具, 如 Puppet 或者 Chef .
自動部署工具可以讓你快速部署一套一模一樣的環境, 避免了一大堆的手動的命令輸入, 并且允許你隨時刪除和重建一個全新的 box, 虛擬機的管理變得更加簡單.
Vagrant 還可以在虛擬機和主機上分享文件夾, 意味著你可以在主機里面編輯代碼, 然后在虛擬機里面運行.
需要更多的幫助?
下面是一些其他的軟件, 可以幫助你更好的使用 Vagrant:
Rove: 使用 Chef 自動化安裝一些常用的軟件, PHP 包含在內.Puphpet: 簡單的 Web 圖形界面用來生成部署 PHP 環境的 Puppet 腳本, 此項目不僅可以用在開發上, 也可以在生產環境中使用.Protobox: 是一個基于 vagrant 的一個層, 還有 Web 圖形界面, 允許你使用一個 YAML 文件來安裝和配置虛擬機里面的軟件.Phansible: 提供了一個簡單的 Web 圖形界面, 用來創建 Ansible 自動化部署腳本, 專門為 PHP 項目定制.Docker 簡介
除了 Vagrant, Docker 是另一個實現生產和開發環境統一的非常棒的方案.
Docker 為各種應用程序提供了 Linux 容器.
你可以安裝 Docker 鏡像, 如 MySQL 和 PostgreSQL 等, 并且不會污染到你的本地機器, 可以看下 Docker Hub Registry, 在這里你可以找到你想要的, 提前配置好的, 允許你簡單幾部就能運行起來的 Linux 容器.
例子: 在 Docker 里面運行 PHP 應用
在你成功 安裝 Docker 后, 你只需要一步就可以安裝 Apache + PHP.
下面的命令, 會下載一個功能齊全的 Apache 和 最新版本的 PHP, 并會設置 WEB 目錄 /path/to/your/php/files
運行在 http://localhost:8080
:
docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache在使用 docker run
命令以后, 如果你想停止, 或者再次開啟容器的話, 只需要執行以下命令:
docker stop my-php-webserverdocker start my-php-webserver了解更多關于 Docker 的信息
The commands mentioned above only show a quick way to run an Apache web server with PHP support but there are a lot more things that you can do with Docker.
上面的命令能讓你輕松使用 Apache + PHP 環境, 然而, Docker 還提供了好多別的命令, 例如, 作為 PHP 程序員, 一個最重要的事情, 是讓你的 Web Server 和數據庫鏈接起來, 怎么實現可以仔細看下 Docker User Guide.
Docker WebsiteDocker InstallationDocker Images at the Docker Hub RegistryDocker User GuideBack to Top
緩存
PHP 本身來說是非??斓?,但是但你當發起遠程連接、加載文件等操作時也會遇到瓶頸。 幸運的是,有各種各樣的工具可以用來加速你應用程序某些耗時的部分,或者說減少某些耗時任務所需要運行的次數。
Opcode 緩存
當一個 PHP 文件被解釋執行的時候,首先是被編譯成名為 opcode 的中間代碼,然后才被底層的虛擬機執行。 如果PHP文件沒有被修改過,opcode 始終是一樣的。這就意味著編譯步驟白白浪費了 CPU 的資源。
此時 opcode 緩存就派上用場了。通過將 opcode 緩存在內存中,它能防止冗余的編譯步驟,并且在下次調用執行時得到重用。設置 opcode 緩存只需要幾分鐘的時間,你的應用程序便會因此大大加速,實在沒有理由不用它。
PHP 5.5 中自帶了 opcode 緩存工具,叫做OPcache,早期的版本也能通過一定的配置使用它。 更多關于 opcode 緩存的資料: * OPcache (built-in since PHP 5.5) * APC (PHP 5.4 and earlier) * XCache * Zend Optimizer+ (part of Zend Server package) * WinCache (extension for MS Windows Server) * list of PHP accelerators on Wikipedia
對象緩存
有時緩存代碼中的單個對象會很有用,比如有些需要很大開銷獲取的數據或者一些結果集不怎么變化的數據庫查詢。你可以使用一些緩存軟件將這些數據存放在內存中以便下次高速獲取。如果你獲得數據后把他們存起來,下次請求直接從緩存里面獲取數據,在減少數據庫負載的同時能極大提高性能。
許多流行的字節碼緩存方案也能緩存定制化的數據,所以更有理由好好使用它們了。APCu、XCache 以及 WinCache 都提供了 API,以便你將數據緩存到內存中
最常用的內存對象緩存系統是 APCu 和 Memcached 。APCu 對于對象緩存來說是個很好的選擇,它提供了簡單的 API 讓你能將數據緩存到內存,并且很容易設置和使用。APCu 的局限性表現在它依賴于所在的服務器。另一方面,Memcached 以獨立的服務的形式安裝,可以通過網絡交互,這意味著你能將數據集中存在一個高速存取的地方,而且許多不同的系統能從中獲取數據。
值得注意的是當你以 CGI(FastCGI) 的形式使用 PHP 時,每個進程將會有各自的緩存,比如說,APCu 緩存數據無法在多個工作進程中共享。在這種情況下,你可能得考慮 Memcached 了,由于它獨立于 PHP 進程。
通常 APCu 在存取速度上比 Memcached 更快,但是 Memcached 在擴展上更有優勢。如果你不希望應用程序涉及多個服務器,或者不需要 Memcached 提供的其他特性,那么 APCu 可能是最好的選擇。
使用 APCu 的例子:
<?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);注意在 PHP 5.5 之前,APC 同時提供了對象緩存與字節碼緩存。APCu 是為了將 APC 的對象緩存移植到 PHP 5.5+ 的一個項目,因為現在 PHP 有了內建的字節碼緩存方案 (OPcache)。
更多關于緩存系統的項目:
APCuAPC FunctionsMemcachedRedisXCache APIsWinCache FunctionsBack to Top
文檔撰寫
PHPDoc
PHPDoc 是注釋 PHP 代碼的非正式標準。它有許多不同的標記可以使用。完整的標記列表和范例可以查看 PHPDoc 指南。
如下是撰寫類方法時的一種寫法:
<?php/** * @author A Name <a.name@example.com> * @link http://www.phpdoc.org/docs/latest/index.html */class DateTimeHelper{ /** * @param mixed $anything Anything that we can convert to a /DateTime object * * @throws /InvalidArgumentException * * @return /DateTime */ public function dateTimeFromAnything($anything) { $type = gettype($anything); switch ($type) { // Some code that tries to return a /DateTime object } throw new /InvalidArgumentException( "Failed Converting param of type '{$type}' to DateTime object" ); } /** * @param mixed $date Anything that we can convert to a /DateTime object * * @return void */ public function printISO8601Date($date) { echo $this->dateTimeFromAnything($date)->format('c'); } /** * @param mixed $date Anything that we can convert to a /DateTime object */ public function printRFC2822Date($date) { echo $this->dateTimeFromAnything($date)->format('r'); }}這個類的說明使用了 @author 和 @link標記, @author 標記是用來說明代碼的作者,在多位開發者的情況下,可以同時列出好幾位。其次 @link 標記用來提供網站鏈接,進一步說明代碼和網站之間的關系。
在這個類中,第一個方法的 @param 標記,說明類型、名字和傳入方法的參數。此外,@return 和 @throws 標記說明返回類型以及可能拋出的異常。
第二、第三個方法非常類似,和第一個方法一樣使用一個 @param 標記。第二、和第三個方法之間關鍵差別在注釋區塊使用/排除 @return 標記。@return void
標記明確告訴我們沒有返回值,而過去省略 @return void
聲明也具有相同效果(沒有返回任何值)。
Back to Top
資源
PHP 官方
PHP 官方網站PHP 官方文檔值得關注的大牛
Rasmus LerdorfFabien PotencierDerick RethansChris ShiflettSebastian BergmannMatthew Weier O’PhinneyPádraic BradyAnthony FerraraNikita Popov指導
phpmentoring.org - PHP 社區中的一對一指導。PHP 的 Paas 提供商
PagodaBoxAppFogHerokufortrabbitEngine Yard CloudRed Hat OpenShift PlatformdotCloudAWS Elastic BeanstalkcloudControlWindows AzureGoogle App EngineJelastic框架
許多的 PHP 開發者都使用框架,而不是重新造輪子來構建 Web 應用??蚣艹橄罅嗽S多底層常用的邏輯,并提供了有益又簡便的方法來完成常見的任務。
你并不一定要在每個項目中都使用框架。有時候原生的 PHP 才是正確的選擇,但如果你需要一個框架,那么有如下三種主要類型:
微型框架全棧框架組件框架微型框架基本上是一個封裝的路由,用來轉發 HTTP 請求至一個閉包,控制器,或方法等等,盡可能地加快開發的速度,有時還會使用一些類庫來幫助開發,例如一個基本的數據庫封裝等等。他們用來構建 HTTP 的服務卓有成效。
許多的框架會在微型框架上加入相當多的功能,我們則稱之為全??蚣?。這些框架通常會提供 ORMs ,身份認證擴展包等等。
組件框架是多個獨立的類庫所結合起來的。不同的組件框架可以一起使用在微型或是全棧框架上。
熱門的 PHP 框架組件
正如標題提到的,「組件」是另一種建立,發布及推動開源的方式?,F在存在的各種的組件庫,其中最主要的兩個為:
PackagistPEAR這兩個組件庫都有用來安裝及升級的命令行工具,這部分已經在這部分已經在[依賴管理]中解釋過。
此外,還有基于組件的構成的框架的提供商提供不包含框架的組件。這些項目通常和其他的組件或者特定的框架沒有依賴關系。
例如,你可以使用 [FuelPHP 驗證類庫],而不使用 FuelPHP 整個框架。
AuraFuelPHPHoa ProjectOrnoSymfony ComponentsThe League of Extraordinary PackagesLaravel’s Illuminate ComponentsEloquent ORMQueueLaravel 的 [Illuminate 組件] 和 Laravel 框架將變得更加解耦。 現在我們只列出和 Laravel 框架最沒有依賴關系的組件。
其他有用的資源
Cheatsheets
PHP Cheatsheets - for variable comparisons, arithmetics and variable testing in various PHP versionsPHP Security Cheatsheet更多最佳實踐
PHP Best PracticesBest practices for Modern PHP DevelopmentPHP 世界
PHP Developer blogVideo Tutorials
Youtube 視頻
PHP AcademyThe New BostonSherif RamadanLevel Up Tuts付費視頻
Standards and Best practicesPHP Training on PluralsightPHP Training on Lynda.comPHP Training on TutsplusLaracasts書籍
市面上有很多關于 PHP 的書,但遺憾的是很多都已經非常陳舊而且不正確的資料。甚至還有出版商發布「 PHP 6 」,這是不存在的書,而且永遠不會出現。因為那些書,所以 PHP 的下一個版本為「 PHP 7 」。
這個章節的目錄主要是針對 PHP 開發,并且會隨著最新的技術趨勢而更新。如果你想在這里加入你的書,請發送一個 PR ,我們將會審查你提供的內容是否有相關性。
免費書籍
PHP The Right Way - This website is available as a book completely for free.付費書籍
Build APIs You Won’t Hate - Everyone and their dog wants an API, so you should probably learn how to build them.Building Secure PHP Apps - Learn the security basics that a senior developer usually acquires over years of experience, all condensed down into one quick and easy handbookModernizing Legacy Applications In PHP - Get your code under control in a series of small, specific stepsSecuring PHP: Core Concepts - A guide to some of the most common security terms and provides some examples of them in every day PHPScaling PHP - Stop playing sysadmin and get back to codingSignaling PHP - PCNLT signals are a great help when writing PHP scripts that run from the command line.The Grumpy Programmer’s Guide To Building Testable PHP Applications - Learning to write testable code doesn’t have to suckBack to Top
社區
PHP 社區多元化并且規模龐大,成員們也樂意并隨時準備好幫助新人。你可以考慮加入當地的 PHP 使用者社區 (PUG) 或者參加教大型的 PHP 會議,從中學習更多最佳實踐。你也可以使用 IRC 逛逛 irc.freenode.com 上的 #phpc 頻道,也可以關注 @phpc 的Twitter 賬號。試著去多結交一些新的開發者,學習新的東西,總之,交一些新朋友!其他的社區資源包含 Google+ 的 PHP Programmer community 以及 StackOverflow。
閱讀 PHP 官方事件日歷
PHP 用戶群
如果你住在較大的城市,附近應該就有 PHP 用戶群。你可以通過基于 PHP.ug 的 usergroup-list at php.net 這個地址找到當地的 PUG。也可以通過 Meetup.com 或者使用搜索引擎 (i.e. Google) 搜索 php user group near me
。如果你住在比較小的城鎮,當地也許還沒有 PUG ,如果是這種情形,不妨就開始組建一個。
這里要特別提到兩個全球的用戶組:NomadPHP 和 PHPWomen。NomadPHP 提供每月兩次的在線用戶組會議,由 PHP 社區里頂尖的高手進行演講。PHPWomen 原本是針對女性 PHP 開發者的非排他性的用戶組。會員資格發放給那些支持多元化社區的人。PHPWomen 提供了一技術支持,指導和教育的個平臺,并且促進了女性的創造力以及專業的氛圍。
了解關于 PHP Wiki 上的用戶群
PHP 會議
世界各地的 PHP 社區也會舉辦一些較大型的區域性或國際性的會議,一些知名的社區成員通常會在這些大型活動中現身演講,這是一個直接和業內領袖學習的好機會。
查找 PHP 會議
ElePHPants
ElePHPant is that beautiful mascot of the PHP project with elephant in their design. It was originally designed for the PHP project in 1998 by Vincent Pontier - spiritual father of thousands of elePHPants around the world and 10 years later adorable plush elephant toy came to birth as well. Now elePHPants are present at many PHP conferences and with many PHP developers at their computers for fun and inspiration.
Interview with Vincent Pontier
轉自:PHPHub社區
新聞熱點
疑難解答
圖片精選