什么是psr-0,psr-1,psr-2標(biāo)準(zhǔn)
FIG組織在制定跟PHP相關(guān)規(guī)范,簡稱PSR,PSR旨在通過討論我們代碼項(xiàng)目的共同點(diǎn)以找出一個(gè)協(xié)作編程的方法。
什么是psr0強(qiáng)調(diào)自動(dòng)加載的方式
下文描述了若要使用一個(gè)通用的自動(dòng)加載器(autoloader),你所需要遵守的規(guī)范:
規(guī)范
一個(gè)完全標(biāo)準(zhǔn)的命名空間(namespace)和類(class)的結(jié)構(gòu)是這樣的:\()*
每個(gè)命名空間(namespace)都必須有一個(gè)頂級(jí)的空間名(namespace)("組織名(Vendor Name)")。
每個(gè)命名空間(namespace)中可以根據(jù)需要使用任意數(shù)量的子命名空間(sub-namespace)。
從文件系統(tǒng)中加載源文件時(shí),空間名(namespace)中的分隔符將被轉(zhuǎn)換為 DIRECTORY_SEPARATOR。
類名(class name)中的每個(gè)下劃線_都將被轉(zhuǎn)換為一個(gè)DIRECTORY_SEPARATOR。下劃線_在空間名(namespace)中沒有什么特殊的意義。
完全標(biāo)準(zhǔn)的命名空間(namespace)和類(class)從文件系統(tǒng)加載源文件時(shí)將會(huì)加上.php后綴。
組織名(vendor name),空間名(namespace),類名(class name)都由大小寫字母組合而成。
示例
DoctrineCommonIsolatedClassLoader?=>?/path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
SymfonyCoreRequest?=>?/path/to/project/lib/vendor/Symfony/Core/Request.php
ZendAcl?=>?/path/to/project/lib/vendor/Zend/Acl.php
ZendMailMessage?=>?/path/to/project/lib/vendor/Zend/Mail/Message.php
空間名(namespace)和類名(class name)中的下劃線
namespacepackageClass_Name?=>?/path/to/project/lib/vendor/namespace/package/Class/Name.php
namespacepackage_nameClass_Name?=>?/path/to/project/lib/vendor/namespace/package_name/Class/Name.php
以上是我們?yōu)閷?shí)現(xiàn)通用的自動(dòng)加載而制定的最低標(biāo)準(zhǔn)。你可以利用能夠自動(dòng)加載PHP 5.3類的SplClassLoader來測試你的代碼是否符合這些標(biāo)準(zhǔn)。
實(shí)例
下面是一個(gè)怎樣利用上述標(biāo)準(zhǔn)來實(shí)現(xiàn)自動(dòng)加載的示例函數(shù)。
function?autoload($className)
{
$className?=?ltrim($className,?'\');
$fileName??=?'';
$namespace?=?'';
if?($lastNsPos?=?strrpos($className,?'\'))?{
$namespace?=?substr($className,?0,?$lastNsPos);
$className?=?substr($className,?$lastNsPos?+?1);
$fileName??=?str_replace('\',?DIRECTORY_SEPARATOR,?$namespace)?.?DIRECTORY_SEPARATOR;
}
$fileName?.=?str_replace('_',?DIRECTORY_SEPARATOR,?$className)?.?'.php';
require?$fileName;
}
SplClassLoader實(shí)現(xiàn)
下面的gist是一個(gè)按照上面建議的標(biāo)準(zhǔn)來自動(dòng)加載類的SplClassLoader實(shí)例。這是依據(jù)這些標(biāo)準(zhǔn)來加載PHP 5.3類的推薦方案。
什么是psr1,定義基本代碼規(guī)范
本節(jié)我們將會(huì)討論一些基本的代碼規(guī)范問題,以此作為將來討論更高級(jí)別的代碼分享和技術(shù)互用的基礎(chǔ)。
RFC 2119中的必須(MUST),不可(MUST NOT),建議(SHOULD),不建議(SHOULD NOT),可以/可能(MAY)等關(guān)鍵詞將在本節(jié)用來做一些解釋性的描述。
1. 概述
源文件必須只使用和這兩種標(biāo)簽。
源文件中php代碼的編碼格式必須只使用不帶字節(jié)順序標(biāo)記(BOM)的UTF-8。
一個(gè)源文件建議只用來做聲明(類(class),函數(shù)(function),常量(constant)等)或者只用來做一些引起副作用的操作(例如:輸出信息,修改.ini配置等),但不建議同時(shí)做這兩件事。
命名空間(namespace)和類(class) 必須遵守PSR-0標(biāo)準(zhǔn)。
類名(class name) 必須使用駱駝式(StudlyCaps)寫法 (譯者注:駝峰式(cameCase)的一種變種,后文將直接用StudlyCaps表示)。
類(class)中的常量必須只由大寫字母和下劃線(_)組成。
方法名(method name) 必須使用駝峰式(cameCase)寫法(譯者注:后文將直接用camelCase表示)。
2. 文件
2.1. PHP標(biāo)簽
PHP代碼必須只使用長標(biāo)簽()或者短輸出式標(biāo)簽();而不可使用其他標(biāo)簽。
2.2. 字符編碼
PHP代碼的編碼格式必須只使用不帶字節(jié)順序標(biāo)記(BOM)的UTF-8。
2.3. 副作用
一個(gè)源文件建議只用來做聲明(類(class),函數(shù)(function),常量(constant)等)或者只用來做一些引起副作用的操作(例如:輸出信息,修改.ini配置等),但不建議同時(shí)做這兩件事。
短語副作用(side effects)的意思是 在包含文件時(shí) 所執(zhí)行的邏輯與所聲明的類(class),函數(shù)(function),常量(constant)等沒有直接的關(guān)系。
副作用(side effects)包含但不局限于:產(chǎn)生輸出,顯式地使用require或include,連接外部服務(wù),修改ini配置,觸發(fā)錯(cuò)誤或異常,修改全局或者靜態(tài)變量,讀取或修改文件等等
下面是一個(gè)既包含聲明又有副作用的示例文件;即應(yīng)避免的例子:
//?副作用:修改了ini配置
ini_set('error_reporting',?E_ALL);
//?副作用:載入了文件
include?"file.php";
//?副作用:產(chǎn)生了輸出
echo?"n";
//?聲明
function?foo()
{
//?函數(shù)體
}
下面是一個(gè)僅包含聲明的示例文件;即應(yīng)提倡的例子:
//?聲明
function?foo()
{
//?函數(shù)體
}
//?條件式聲明不算做是副作用
if?(!?function_exists('bar'))?{
function?bar()
{
//?函數(shù)體
}
}
3. 空間名(namespace)和類名(class name)
命名空間(namespace)和類(class)必須遵守 PSR-0.
這意味著一個(gè)源文件中只能有一個(gè)類(class),并且每個(gè)類(class)至少要有一級(jí)空間名(namespace):即一個(gè)頂級(jí)的組織名(vendor name)。
類名(class name) 必須使用StudlyCaps寫法。
PHP5.3之后的代碼必須使用正式的命名空間(namespace) 例子:
//?PHP?5.3?及之后:
namespace?VendorModel;
class?Foo
{
}
PHP5.2.x之前的代碼建議用偽命名空間Vendor_作為類名(class?name)的前綴
//?PHP?5.2.x?及之前:
class?Vendor_Model_Foo
{
}
4. 類的常量、屬性和方法
術(shù)語類(class)指所有的類(class),接口(interface)和特性(trait)
4.1. 常量
類常量必須只由大寫字母和下劃線(_)組成。 例子:
namespace?VendorModel;
class?Foo
{
const?VERSION?=?'1.0';
const?DATE_APPROVED?=?'2012-06-01';
}
4.2. 屬性
本指南中故意不對(duì)$StulyCaps,$camelCase或者$unser_score中的某一種風(fēng)格作特別推薦,完全由讀者依據(jù)個(gè)人喜好決定屬性名的命名風(fēng)格。
但是不管你如何定義屬性名,建議在一個(gè)合理的范圍內(nèi)保持一致。這個(gè)范圍可能是組織(vendor)級(jí)別的,包(package)級(jí)別的,類(class)級(jí)別的,或者方法(method)級(jí)別的。
4.3. 方法
方法名則必須使用camelCase()風(fēng)格來聲明。
什么是PSR2定義代碼風(fēng)格
代碼風(fēng)格指南
本手冊(cè)是基礎(chǔ)代碼規(guī)范(PSR-1)的繼承和擴(kuò)展。
為了盡可能的提升閱讀其他人代碼時(shí)的效率,下面例舉了一系列的通用規(guī)則,特別是有關(guān)于PHP代碼風(fēng)格的。
各個(gè)成員項(xiàng)目間的共性組成了這組代碼規(guī)范。當(dāng)開發(fā)者們?cè)诙鄠€(gè)項(xiàng)目中合作時(shí),本指南將會(huì)成為所有這些項(xiàng)目中共用的一組代碼規(guī)范。 因此,本指南的益處不在于這些規(guī)則本身,而在于在所有項(xiàng)目中共用這些規(guī)則。
RFC 2119中的必須(MUST),不可(MUST NOT),建議(SHOULD),不建議(SHOULD NOT),可以/可能(MAY)等關(guān)鍵詞將在本節(jié)用來做一些解釋性的描述。
1. 概述
代碼必須遵守 PSR-1。
代碼必須使用4個(gè)空格來進(jìn)行縮進(jìn),而不是用制表符。
一行代碼的長度不建議有硬限制;軟限制必須為120個(gè)字符,建議每行代碼80個(gè)字符或者更少。
在命名空間(namespace)的聲明下面必須有一行空行,并且在導(dǎo)入(use)的聲明下面也必須有一行空行。
類(class)的左花括號(hào)必須放到其聲明下面自成一行,右花括號(hào)則必須放到類主體下面自成一行。
方法(method)的左花括號(hào)必須放到其聲明下面自成一行,右花括號(hào)則必須放到方法主體的下一行。
所有的屬性(property)和方法(method) 必須有可見性聲明;抽象(abstract)和終結(jié)(final)聲明必須在可見性聲明之前;而靜態(tài)(static)聲明必須在可見性聲明之后。
在控制結(jié)構(gòu)關(guān)鍵字的后面必須有一個(gè)空格;而方法(method)和函數(shù)(function)的關(guān)鍵字的后面不可有空格。
控制結(jié)構(gòu)的左花括號(hào)必須跟其放在同一行,右花括號(hào)必須放在該控制結(jié)構(gòu)代碼主體的下一行。
控制結(jié)構(gòu)的左括號(hào)之后不可有空格,右括號(hào)之前也不可有空格。
1.1. 示例
這個(gè)示例中簡單展示了上文中提到的一些規(guī)則:
namespace?VendorPackage;
use?FooInterface;
use?BarClass?as?Bar;
use?OtherVendorOtherPackageBazClass;
class?Foo?extends?Bar?implements?FooInterface
{
public?function?sampleFunction($a,?$b?=?null)
{
if?($a?===?$b)?{
bar();
}?elseif?($a?>?$b)?{
$foo->bar($arg1);
}?else?{
BazClass::bar($arg2,?$arg3);
}
}
final?public?static?function?bar()
{
//?方法主體
}
}
2. 通則
2.1 基礎(chǔ)代碼規(guī)范
代碼必須遵守 PSR-1 中的所有規(guī)則。
2.2 源文件
所有的PHP源文件必須使用Unix LF(換行)作為行結(jié)束符。
所有PHP源文件必須以一個(gè)空行結(jié)束。
純PHP代碼源文件的關(guān)閉標(biāo)簽?> 必須省略。
2.3. 行
行長度不可有硬限制。
行長度的軟限制必須是120個(gè)字符;對(duì)于軟限制,代碼風(fēng)格檢查器必須警告但不可報(bào)錯(cuò)。
一行代碼的長度不建議超過80個(gè)字符;較長的行建議拆分成多個(gè)不超過80個(gè)字符的子行。
在非空行后面不可有空格。
空行可以用來增強(qiáng)可讀性和區(qū)分相關(guān)代碼塊。
一行不可多于一個(gè)語句。
2.4. 縮進(jìn)
代碼必須使用4個(gè)空格,且不可使用制表符來作為縮進(jìn)。
注意:代碼中只使用空格,且不和制表符混合使用,將會(huì)對(duì)避免代碼差異,補(bǔ)丁,歷史和注解中的一些問題有幫助。空格的使用還可以使通過調(diào)整細(xì)微的縮進(jìn)來改進(jìn)行間對(duì)齊變得更加的簡單。
2.5. 關(guān)鍵字和 True/False/Null
PHP關(guān)鍵字(keywords)必須使用小寫字母。
PHP常量true, false和null 必須使用小寫字母。
3. 命名空間(Namespace)和導(dǎo)入(Use)聲明
命名空間(namespace)的聲明后面必須有一行空行。
所有的導(dǎo)入(use)聲明必須放在命名空間(namespace)聲明的下面。
一句聲明中,必須只有一個(gè)導(dǎo)入(use)關(guān)鍵字。
在導(dǎo)入(use)聲明代碼塊后面必須有一行空行。
示例:
namespace?VendorPackage;
use?FooClass;
use?BarClass?as?Bar;
use?OtherVendorOtherPackageBazClass;
//?...?其它PHP代碼?...
4. 類(class),屬性(property)和方法(method)
術(shù)語“類”指所有的類(class),接口(interface)和特性(trait)。
4.1. 擴(kuò)展(extend)和實(shí)現(xiàn)(implement)
一個(gè)類的擴(kuò)展(extend)和實(shí)現(xiàn)(implement)關(guān)鍵詞必須和類名(class name)在同一行。
類(class)的左花括號(hào)必須放在下面自成一行;右花括號(hào)必須放在類(class)主體的后面自成一行。
namespace?VendorPackage;
use?FooClass;
use?BarClass?as?Bar;
use?OtherVendorOtherPackageBazClass;
class?ClassName?extends?ParentClass?implements?ArrayAccess,?Countable
{
//?常量、屬性、方法
}
實(shí)現(xiàn)(implement)列表可以被拆分為多個(gè)縮進(jìn)了一次的子行。如果要拆成多個(gè)子行,列表的第一項(xiàng)必須要放在下一行,并且每行必須只有一個(gè)接口(interface)。
namespace?VendorPackage;
use?FooClass;
use?BarClass?as?Bar;
use?OtherVendorOtherPackageBazClass;
class?ClassName?extends?ParentClass?implements
ArrayAccess,
Countable,
Serializable
{
//?常量、屬性、方法
}
4.2. 屬性(property)
所有的屬性(property)都必須聲明其可見性。
變量(var)關(guān)鍵字不可用來聲明一個(gè)屬性(property)。
一條語句不可聲明多個(gè)屬性(property)。
屬性名(property name) 不推薦用單個(gè)下劃線作為前綴來表明其保護(hù)(protected)或私有(private)的可見性。
一個(gè)屬性(property)聲明看起來應(yīng)該像下面這樣。
namespace?VendorPackage;
class?ClassName
{
public?$foo?=?null;
}
4.3. 方法(method)
所有的方法(method)都必須聲明其可見性。
方法名(method name) 不推薦用單個(gè)下劃線作為前綴來表明其保護(hù)(protected)或私有(private)的可見性。
方法名(method name)在其聲明后面不可有空格跟隨。其左花括號(hào)必須放在下面自成一行,且右花括號(hào)必須放在方法主體的下面自成一行。左括號(hào)后面不可有空格,且右括號(hào)前面也不可有空格。
一個(gè)方法(method)聲明看來應(yīng)該像下面這樣。 注意括號(hào),逗號(hào),空格和花括號(hào)的位置:
namespace?VendorPackage;
class?ClassName
{
public?function?fooBarBaz($arg1,?&$arg2,?$arg3?=?[])
{
//?方法主體部分
}
}
4.4. 方法(method)的參數(shù)
在參數(shù)列表中,逗號(hào)之前不可有空格,而逗號(hào)之后則必須要有一個(gè)空格。
方法(method)中有默認(rèn)值的參數(shù)必須放在參數(shù)列表的最后面。
namespace?VendorPackage;
class?ClassName
{
public?function?foo($arg1,?&$arg2,?$arg3?=?[])
{
//?方法主體部分
}
}
參數(shù)列表可以被拆分為多個(gè)縮進(jìn)了一次的子行。如果要拆分成多個(gè)子行,參數(shù)列表的第一項(xiàng)必須放在下一行,并且每行必須只有一個(gè)參數(shù)。
當(dāng)參數(shù)列表被拆分成多個(gè)子行,右括號(hào)和左花括號(hào)之間必須又一個(gè)空格并且自成一行。
namespace?VendorPackage;
class?ClassName
{
public?function?aVeryLongMethodName(
ClassTypeHint?$arg1,
&$arg2,
array?$arg3?=?[]
)?{
//?方法主體部分
}
}
4.5. 抽象(abstract),終結(jié)(final)和 靜態(tài)(static)
當(dāng)用到抽象(abstract)和終結(jié)(final)來做類聲明時(shí),它們必須放在可見性聲明的前面。
而當(dāng)用到靜態(tài)(static)來做類聲明時(shí),則必須放在可見性聲明的后面。
namespace?VendorPackage;
abstract?class?ClassName
{
protected?static?$foo;
abstract?protected?function?zim();
final?public?static?function?bar()
{
//?方法主體部分
}
}
4.6. 調(diào)用方法和函數(shù)
調(diào)用一個(gè)方法或函數(shù)時(shí),在方法名或者函數(shù)名和左括號(hào)之間不可有空格,左括號(hào)之后不可有空格,右括號(hào)之前也不可有空格。參數(shù)列表中,逗號(hào)之前不可有空格,逗號(hào)之后則必須有一個(gè)空格。
bar($arg1);
Foo::bar($arg2,?$arg3);
參數(shù)列表可以被拆分成多個(gè)縮進(jìn)了一次的子行。如果拆分成子行,列表中的第一項(xiàng)必須放在下一行,并且每一行必須只能有一個(gè)參數(shù)。
bar(
$longArgument,
$longerArgument,
$muchLongerArgument
);
5. 控制結(jié)構(gòu)
下面是對(duì)于控制結(jié)構(gòu)代碼風(fēng)格的概括:
控制結(jié)構(gòu)的關(guān)鍵詞之后必須有一個(gè)空格。
控制結(jié)構(gòu)的左括號(hào)之后不可有空格。
控制結(jié)構(gòu)的右括號(hào)之前不可有空格。
控制結(jié)構(gòu)的右括號(hào)和左花括號(hào)之間必須有一個(gè)空格。
控制結(jié)構(gòu)的代碼主體必須進(jìn)行一次縮進(jìn)。
控制結(jié)構(gòu)的右花括號(hào)必須主體的下一行。
每個(gè)控制結(jié)構(gòu)的代碼主體必須被括在花括號(hào)里。這樣可是使代碼看上去更加標(biāo)準(zhǔn)化,并且加入新代碼的時(shí)候還可以因此而減少引入錯(cuò)誤的可能性。
5.1. if,elseif,else
下面是一個(gè)if條件控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。同時(shí)注意else和elseif要和前一個(gè)條件控制結(jié)構(gòu)的右花括號(hào)在同一行。
if?($expr1)?{
//?if?body
}?elseif?($expr2)?{
//?elseif?body
}?else?{
//?else?body;
}
推薦用elseif來替代else if,以保持所有的條件控制關(guān)鍵字看起來像是一個(gè)單詞。
5.2. switch,case
下面是一個(gè)switch條件控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。case語句必須要縮進(jìn)一級(jí),而break關(guān)鍵字(或其他中止關(guān)鍵字)必須和case結(jié)構(gòu)的代碼主體在同一個(gè)縮進(jìn)層級(jí)。如果一個(gè)有主體代碼的case結(jié)構(gòu)故意的繼續(xù)向下執(zhí)行則必須要有一個(gè)類似于// no break的注釋。
switch?($expr)?{
case?0:
echo?'First?case,?with?a?break';
break;
case?1:
echo?'Second?case,?which?falls?through';
//?no?break
case?2:
case?3:
case?4:
echo?'Third?case,?return?instead?of?break';
return;
default:
echo?'Default?case';
break;
}
5.3. while,do while
下面是一個(gè)while循環(huán)控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。
while?($expr)?{
//?structure?body
}
下面是一個(gè)do while循環(huán)控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。
do?{
//?structure?body;
}?while?($expr);
5.4. for
下面是一個(gè)for循環(huán)控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。
for?($i?=?0;?$i?<?10;?$i++)?{
//?for?body
}
5.5. foreach
下面是一個(gè)foreach循環(huán)控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。
?$value)?{
//?foreach?body
}
5.6. try, catch
下面是一個(gè)try catch異常處理控制結(jié)構(gòu)的示例,注意其中括號(hào),空格和花括號(hào)的位置。
try?{
//?try?body
}?catch?(FirstExceptionType?$e)?{
//?catch?body
}?catch?(OtherExceptionType?$e)?{
//?catch?body
}
6. 閉包
聲明閉包時(shí)所用的function關(guān)鍵字之后必須要有一個(gè)空格,而use關(guān)鍵字的前后都要有一個(gè)空格。
閉包的左花括號(hào)必須跟其在同一行,而右花括號(hào)必須在閉包主體的下一行。
閉包的參數(shù)列表和變量列表的左括號(hào)后面不可有空格,右括號(hào)的前面也不可有空格。
閉包的參數(shù)列表和變量列表中逗號(hào)前面不可有空格,而逗號(hào)后面則必須有空格。
閉包的參數(shù)列表中帶默認(rèn)值的參數(shù)必須放在參數(shù)列表的結(jié)尾部分。
下面是一個(gè)閉包的示例。注意括號(hào),空格和花括號(hào)的位置。
$closureWithArgs?=?function?($arg1,?$arg2)?{
//?body
};
$closureWithArgsAndVars?=?function?($arg1,?$arg2)?use?($var1,?$var2)?{
//?body
};
參數(shù)列表和變量列表可以被拆分成多個(gè)縮進(jìn)了一級(jí)的子行。如果要拆分成多個(gè)子行,列表中的第一項(xiàng)必須放在下一行,并且每一行必須只放一個(gè)參數(shù)或變量。
當(dāng)列表(不管是參數(shù)還是變量)最終被拆分成多個(gè)子行,右括號(hào)和左花括號(hào)之間必須要有一個(gè)空格并且自成一行。
下面是一個(gè)參數(shù)列表和變量列表被拆分成多個(gè)子行的示例。
$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
};
把閉包作為一個(gè)參數(shù)在函數(shù)或者方法中調(diào)用時(shí),依然要遵守上述規(guī)則。
bar(
$arg1,
function?($arg2)?use?($var1)?{
//?body
},
$arg3
);
7. 結(jié)論
本指南有意的省略了許多元素的代碼風(fēng)格。主要包括:
全局變量和全局常量的聲明
函數(shù)聲明
操作符和賦值
行間對(duì)齊
注釋和文檔塊
類名的前綴和后綴
最佳實(shí)踐
以后的代碼規(guī)范中可能會(huì)修正或擴(kuò)展本指南中規(guī)定的代碼風(fēng)格。