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