PHP的PSR规范
什么是PSR?
PSR是PHP Standards Recommendation的簡(jiǎn)稱(chēng),這個(gè)是php-fig組織制定的一套規(guī)范。至今,php-fig已經(jīng)發(fā)布了五個(gè)規(guī)范:
- PSR-0:自動(dòng)加載標(biāo)準(zhǔn),2014-10-21該標(biāo)準(zhǔn)已經(jīng)被廢棄,使用PSR-4替代,不再細(xì)講
- PSR-1:基本的編碼風(fēng)格
- PSR-2:編碼風(fēng)格(更嚴(yán)格)
- PSR-3:日志記錄器接口
- PSR-4:自動(dòng)加載
PSR-1
PHP標(biāo)簽:
PHP代碼必須放在<?php ?>標(biāo)簽或<?= ?>標(biāo)簽中。
編碼:
PHP文件必須使用無(wú)BOM的UTF-8編碼。
副作用:
一個(gè)PHP文件可以定義符號(hào)(比如類(lèi)、函數(shù)、常量等),或者執(zhí)行只有唯一副作用的操作(比如輸出結(jié)果、處理數(shù)據(jù)等),但是不能同時(shí)做這兩件事,盡量是一個(gè)PHP文件的功能單一。在操作的時(shí)候盡量把變量、類(lèi)、函數(shù)的聲明分開(kāi),通過(guò)include或require文件的方式來(lái)使用。
如下不符合規(guī)范:原因有四種類(lèi)型的操作
<?php // 改變?cè)O(shè)置 ini_set('error_reporting', E_ALL);// 加載文件 include "file.php";// 打印輸出 echo "<html>\n";// 聲明 function foo() {// function body }符合規(guī)范如下:
<?php // 聲明 function foo() {// function body }// 條件判斷 if (! function_exists('bar')) {function bar(){// function body} }命名空間和類(lèi):
命名空間和類(lèi)必須遵循PSR-4自動(dòng)加載器標(biāo)準(zhǔn)。
類(lèi)的名稱(chēng):
每個(gè)類(lèi)都有自己的命名空間,且都在頂級(jí)命名空間下,類(lèi)名必須使用駝峰式(CamelCase)。
PHP 5.3 及以上,必須使用正式的命名空間,例如:
常量:
常量必須全部是用大寫(xiě),并且使用下劃線(xiàn)(_)分開(kāi)。例如:
類(lèi)的方法:
類(lèi)的方法必須使用小寫(xiě)字母開(kāi)頭的駝峰式(camelCase)命名。單獨(dú)一個(gè)的話(huà)就直接開(kāi)頭大寫(xiě)
PSR-2
貫徹PSR-1:
使用PSR-2代碼標(biāo)準(zhǔn)之前要先貫徹PSR-1的代碼標(biāo)準(zhǔn)。
文件和代碼行:
PHP文件必須使用Unix風(fēng)格的換行符(LF, linefeed),最后要有一個(gè)空行,僅包含PHP代碼的文件而且不能使用PHP關(guān)閉標(biāo)簽?>,每行代碼不應(yīng)該超過(guò)80個(gè)字符,每行末尾不能有空格,每行只能有一條語(yǔ)句,可以在適當(dāng)?shù)牡胤教砑涌招刑岣叽a的閱讀性。
不加上?>關(guān)閉標(biāo)簽,可以避免意料之外的輸出錯(cuò)誤,如果加上關(guān)閉標(biāo)簽,且在關(guān)閉標(biāo)簽后有空行,那么空行會(huì)被當(dāng)成輸出,導(dǎo)致意想不到的錯(cuò)誤。
縮進(jìn):
必須以4個(gè)空格為縮進(jìn),不能使用制表符(Tab鍵)縮進(jìn)。
在不同的編輯器中,空格的渲染效果基本一致,而制表符的寬度各有差異。
?關(guān)鍵字:
PHP的關(guān)鍵字必須使用小寫(xiě),而且true,?false, 和?null也必須小寫(xiě)。
命名空間和use聲明:
現(xiàn)在,namespace聲明之后必須要有一個(gè)空行,而且use聲明必須放在namespace之后,必須分別使用use引入命名空間,而且use后要有空行,例如:
類(lèi)的繼承和實(shí)現(xiàn):
extends和implements關(guān)鍵字必須和類(lèi)名在同一行,類(lèi)、接口和Traits定義體的起始括號(hào)應(yīng)該在類(lèi)名之后新起一行,結(jié)束括號(hào)也必須新起一行,例如:
如果implements后面后很多類(lèi)導(dǎo)致一行很長(zhǎng),可以依次將需要的類(lèi)另起新行并縮進(jìn)4個(gè)空格,如下:
<?php namespace Vendor\Package;use FooClass; use BarClass as Bar; use OtherVendor\OtherPackage\BazClass;class ClassName extends ParentClass implements\ArrayAccess,\Countable,\Serializable {// constants, properties, methods }可見(jiàn)性:
類(lèi)中的每個(gè)屬性和方法都要聲明可見(jiàn)性,有public、private和protected,不能使用var關(guān)鍵詞來(lái)聲明,老版本的PHP會(huì)在私有屬性前加上_,一行只能聲明一個(gè)屬性,例如:
方法:
類(lèi)中的所有方法也應(yīng)該定義可見(jiàn)性,方法名后面不能有空格,方法體的括號(hào)位置和類(lèi)定義體的括號(hào)位置一樣,都要新起一行,結(jié)束括號(hào)也要新起一行。方法參數(shù)的起始圓括號(hào)之后沒(méi)有空格,結(jié)束括號(hào)之前也沒(méi)有空格,有多個(gè)參數(shù)是,每個(gè)參數(shù)的逗號(hào)后面加一個(gè)空格,例如:
如果參數(shù)比較多,需要換行時(shí),可以如下:
<?php namespace Vendor\Package;class ClassName {public function aVeryLongMethodName(ClassTypeHint $arg1,&$arg2,array $arg3 = []) {// method body} }abstract、final和static:
現(xiàn)在,abstract、final必須在可見(jiàn)性修飾符之前,static聲明必須放在可見(jiàn)性修飾符之后,例如:
方法和函數(shù)的調(diào)用:
在調(diào)用方法和函數(shù)時(shí),圓括號(hào)必須跟在函數(shù)名之后,函數(shù)的參數(shù)之間有一個(gè)空格:
如果參數(shù)比較多,一行放不下時(shí),如下處理:
<?php $foo->bar($longArgument,$longerArgument,$muchLongerArgument );PHP的控制結(jié)構(gòu):
PHP的控制結(jié)構(gòu)包括if、else、elseif、switch、case、while、do while、for、foreach、try和catch。如果這些關(guān)鍵詞后面有一對(duì)原括號(hào),開(kāi)始括號(hào)前必須有一個(gè)空格,與方法和類(lèi)的定義體不同,控制結(jié)構(gòu)關(guān)鍵詞后面的起始括號(hào)應(yīng)該和控制結(jié)構(gòu)關(guān)鍵詞寫(xiě)在同一行,例如:
PHP閉包函數(shù):
閉包函數(shù)在聲明時(shí),function關(guān)鍵詞后必須有一個(gè)空格,同時(shí)use關(guān)鍵詞前后也必須有一個(gè)空格。起始大括號(hào)不需要另起新行,詳細(xì)的如下代碼:
閉包函數(shù)有多個(gè)參數(shù)時(shí),處理方式和方法的參數(shù)一樣:
<?php $longArgs_noVars = function ($longArgument,$longerArgument,$muchLongerArgument ) {// body };$noArgs_longVars = function () use ($longVar1,$longerVar2,$muchLongerVar3 ) {// body };$longArgs_longVars = function ($longArgument,$longerArgument,$muchLongerArgument ) use ($longVar1,$longerVar2,$muchLongerVar3 ) {// body };$longArgs_shortVars = function ($longArgument,$longerArgument,$muchLongerArgument ) use ($var1) {// body };$shortArgs_longVars = function ($arg) use ($longVar1,$longerVar2,$muchLongerVar3 ) {// body };注意:以上規(guī)則同樣適用于將閉包作為函數(shù)或方法的參數(shù),如下:
<?php $foo->bar($arg1,function ($arg2) use ($var1) {// body},$arg3 );PSR-3
與PSR-1和PSR-2不同,PSR-3規(guī)定了一套通用的日志記錄器接口(Psr\Log\LoggerInterface),為了符合PSR-3規(guī)范,框架必須實(shí)現(xiàn)該規(guī)范中的接口,這樣可以更多的兼容第三方應(yīng)用。PSR-3規(guī)范中包含了9個(gè)方法,每個(gè)方法都對(duì)應(yīng)了RFC 5424協(xié)議的一個(gè)日志級(jí)別,而且都接受兩個(gè)參數(shù)$message和$context,如下
<?phpnamespace Psr\Log;/*** Describes a logger instance** The message MUST be a string or object implementing __toString().** The message MAY contain placeholders in the form: {foo} where foo* will be replaced by the context data in key "foo".** The context array can contain arbitrary data, the only assumption that* can be made by implementors is that if an Exception instance is given* to produce a stack trace, it MUST be in a key named "exception".** See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md* for the full interface specification.*/ interface LoggerInterface {/*** System is unusable.** @param string $message* @param array $context* @return void*/public function emergency($message, array $context = array());/*** Action must be taken immediately.** Example: Entire website down, database unavailable, etc. This should* trigger the SMS alerts and wake you up.** @param string $message* @param array $context* @return void*/public function alert($message, array $context = array());/*** Critical conditions.** Example: Application component unavailable, unexpected exception.** @param string $message* @param array $context* @return void*/public function critical($message, array $context = array());/*** Runtime errors that do not require immediate action but should typically* be logged and monitored.** @param string $message* @param array $context* @return void*/public function error($message, array $context = array());/*** Exceptional occurrences that are not errors.** Example: Use of deprecated APIs, poor use of an API, undesirable things* that are not necessarily wrong.** @param string $message* @param array $context* @return void*/public function warning($message, array $context = array());/*** Normal but significant events.** @param string $message* @param array $context* @return void*/public function notice($message, array $context = array());/*** Interesting events.** Example: User logs in, SQL logs.** @param string $message* @param array $context* @return void*/public function info($message, array $context = array());/*** Detailed debug information.** @param string $message* @param array $context* @return void*/public function debug($message, array $context = array());/*** Logs with an arbitrary level.** @param mixed $level* @param string $message* @param array $context* @return void*/public function log($level, $message, array $context = array()); }關(guān)于message參數(shù):
$message必須是一個(gè)字符串或者是含有__toString()方法的對(duì)象,$message應(yīng)該包含占位符,例如{placeholder_name},占位符由{、占位符名稱(chēng)和}組成,不能包含空格,占位符名稱(chēng)可以由A-Z, a-z, 0-9, _組成,第三方實(shí)現(xiàn)可以用$context參數(shù)來(lái)替換占位符,占位符名稱(chēng)必須和$context數(shù)組的key對(duì)應(yīng)。如下例子是使用$context中的值替換$message中的占位符:
<?php/*** Interpolates context values into the message placeholders.*/ function interpolate($message, array $context = array()) {// build a replacement array with braces around the context keys$replace = array();foreach ($context as $key => $val) {// check that the value can be casted to stringif (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {$replace['{' . $key . '}'] = $val;}}// interpolate replacement values into the message and returnreturn strtr($message, $replace); }// a message with brace-delimited placeholder names $message = "User {username} created";// a context array of placeholder names => replacement values $context = array('username' => 'Bolivar');// echoes "User Bolivar created" echo interpolate($message, $context);關(guān)于context參數(shù):
$context是一個(gè)數(shù)組參數(shù),用于構(gòu)造復(fù)雜的日志消息,$context中的值不能跑出任何PHP異常或錯(cuò)誤。如果$context中包含Exception對(duì)象,則該對(duì)象的key必須為exception。
PSR-3日志記錄器的使用
推薦使用monolog/monolog,這樣可以讓我們不需要浪費(fèi)更多的時(shí)間在編寫(xiě)一個(gè)日志記錄器了。Monolog組建完全實(shí)現(xiàn)了PSR-3接口,而且便于使用自定義的消息格式化程序和處理程序擴(kuò)展功能,通過(guò)Monolog可以把日志消息寫(xiě)入文本文件、系統(tǒng)日志和數(shù)據(jù)庫(kù)中,還能通過(guò)電子郵件發(fā)送,并且還支持Slack和遠(yuǎn)程服務(wù)器。如下展示了如何設(shè)置Monolog,并把日志消息寫(xiě)入文本文件:
use Monolog/Logger; use Monolog/Handler/StreamHandler;// 創(chuàng)建日志記錄器 $log = new Logger('myApp'); $log->pushHandler(new StreamHandler('logs/development.log, Logger::DEBUG)); $log->pushHandler(new StreamHandler('logs/production.log', Logger::WARNING));// 使用日志記錄器 $log->debug("This is a debug message"); $log->warning("This is a warning message");PSR-4
PSR-4規(guī)范描述了一個(gè)標(biāo)準(zhǔn)的自動(dòng)加載器策略,指在運(yùn)行時(shí)按需查找PHP類(lèi)、接口或Traits。支持PSR-4自動(dòng)加載器標(biāo)準(zhǔn)的PHP組建和框架,使用同一個(gè)自動(dòng)加載器就能找到相關(guān)代碼,然后將其載入PHP解釋器。有了這個(gè)功能,就可以把現(xiàn)代PHP生態(tài)系統(tǒng)中很多客戶(hù)操作的組件聯(lián)系起來(lái)。
編寫(xiě)一個(gè)PSR-4自動(dòng)加載器
PSR-4規(guī)范不要求改變代碼的實(shí)現(xiàn)方式,只建議如何使用文件系統(tǒng)目錄結(jié)構(gòu)和PHP命名空間組織代碼,PSR-4規(guī)范以來(lái)PHP命名空間和文件系統(tǒng)目錄結(jié)構(gòu)查找并加載PHP類(lèi)、接口和Traits,這正是PSR-4的精髓所在。下面我們來(lái)自己手動(dòng)實(shí)現(xiàn)一個(gè)PSR-4自動(dòng)加載器:
<?php /*** 使用SPL組冊(cè)這個(gè)自動(dòng)加載函數(shù)后,遇到下述代碼時(shí)這個(gè)函數(shù)會(huì)嘗試 從/path/to/project/src/Baz/Qux.php文件中加載\Foo\Bar\Baz\Qux類(lèi):* new \Foo\Bar\Baz\Qux;* @param string $class 完全限定的類(lèi)名。* @return void**/ spl_autoload_register(function ($class) {// 項(xiàng)目的命名空間前綴$prefix = 'Foo\\Bar\\';// 目錄前綴對(duì)應(yīng)的根目錄$base_dir = __DIR__ . '/src/';// 判斷傳入的類(lèi)是否使用了這個(gè)命名空間前綴$len = strlen($prefix);if (strncmp($prefix, $class, $len) !== 0) {// 沒(méi)有使用,交給注冊(cè)的下一個(gè)自動(dòng)加載器處理return;}// 獲取去掉前綴后的類(lèi)名$relative_class = substr($class, $len);// 把命名空間前綴替換成根目錄,// 在去掉前綴的類(lèi)名中,把命名空間分隔符替換成目錄分隔符,// 然后在后面加上.php$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';// 如果該文件存在,就將其導(dǎo)入if (file_exists($file)) {require $file;} });?
總結(jié)
- 上一篇: 造手机的富士康也要造车,这到底靠谱吗
- 下一篇: 微信转银行卡手续费 超出免费额度后收0.