PSR-1 提到基本程式設計規範,PSR-12 是對已棄用的 PSR-2 再修改,對於 PSR-1 的規範不足之處,再補充說明。
內文筆者會依自己的見解翻譯原文及介紹整理,如果詞不及義或錯誤,歡迎指正。
<?php
和短輸出標籤 <?=
,其它不得使用。?>
標簽。不建議使用的範例:
asp_tags = 1
(預設值:0)後可使用 ASP 樣式標簽。<%
。PHP 7.0 以後此設定已棄用。short_open_tag = 1
(預設值:1)可使用短輸出標籤 <?
,PHP 5.4 以後,短輸出標籤 <?=
永久可用,不受此值影響。以 Notepad++ 為例,轉換檔案的字元編碼,請勿選帶有 BOM 的選項。
「副作用」一詞指的是,執行的邏輯操作並不是和宣告類別、函式和常數等等,而是透過(包含但不限於)載入檔案、修改 ini 設定、產生輸出、丟出異常、修改全域變數或靜態變數、讀寫文件等等。
以下是含有副作用的範例:
<?php
// 副作用: 改變 ini 的設定值。
ini_set('error_reporting', E_ALL);
// 副作用: 載入檔案。
include "file.php";
// 副作用: 產生輸出。
echo "<html>\n";
// 宣告函式
function foo()
{
echo 'Hello, world.';
}
以下是不含有副作用的範例:
<?php
// 宣告函式
function foo()
{
echo 'Hello, world.';
}
// 條件式的宣告 *不是* 副作用。
if (! function_exists('bar')) {
function bar()
{
echo 'Hello, world.';
}
}
要嘛,只有宣告函式、類別或常數。
<?php
ini_set('error_reporting', E_ALL);
include __DIR__ . '../vendor/autoload.php';
include __DIR__ . '../app/init.php';
不然,就只有副作用的邏輯執行。不然,就只有副作用的邏輯執行。
行的長度沒有硬性規定,以下規範都是軟性建議:
LF
。LF
行分隔符當作結尾。不建議使用的範例:
CRLF
切到 LF
並存檔。指的是每一個類別是一個獨立的檔案。類別名稱依 StudlyCaps
命名。(字首大寫駝峰式命名法)。
定義的例子:
namespcae VendorName\PackageName;
class HelloWorld
{
// 省略
}
VendorName 指的是套件供應商名稱,你可以依喜好命名,將你所有的 PHP 作品都統一一個響亮的品牌名稱是很不錯的想法喔。例如筆者的套件的 VendorName 都命名為 Shieldon,接來才接各套件的名稱,例如 Messenger、Event 之類。
駝峰式命名指的是,連續的英文單字,沒有空格直接連在一起,每個字的字首大寫,看起來就像駱駝的駝峰上下起伏一樣。
hello world => HelloWorld
get user name => GetUserName
say hello to the world => SayHelloToTheWorld
_
作為分隔。範例:
<?php
namespace Vendor\Model;
class Foo
{
const VERSION = '1.0';
const DATE_APPROVED = '2012-06-01';
}
沒有特別建議命名方式,例如:
字首大寫駝峰式命名法:$StudlyCaps
字首小寫駝峰式命名法:$camelCase
底線分隔式命名法:$under_score
必須宣告屬性的「可見範圍類型」 (public, protected, private)。
「var」關鍵字不能用來宣告屬性。
不能在同一行宣告多個屬性。
宣告屬性和可見範圍之間只能有一個空格。
宣告屬性的範例:
<?php
namespace Vendor\Package;
class ClassName
{
public $foo = null;
public static int $bar = 0;
}
,
之前,不能有空格。參數分長多行:
)
和左大括{
號必須獨立一行,之間放一個空格。範例:
<?php
namespace Vendor\Package;
class ClassName
{
public function aVeryLongMethodName(
ClassTypeHint $arg1,
&$arg2,
array $arg3 = []
) {
// 方法內容
}
}
(
之間不能有空格。左大括號{
和右大括號}
必須自成一行。宣告方法的範例。注意括號()
,逗號,
,空格和大括號{}
的位置:
<?php
namespace Vendor\Package;
class ClassName
{
public function fooBarBaz($arg1, &$arg2, $arg3 = [])
{
// 方法內容
}
}
有宣告返回的型別時:
:
和右括號)
沒有空格。?
和型別宣告之間沒影空格。範例:
<?php
declare(strict_types=1);
namespace Vendor\Package;
class ReturnTypeVariations
{
public function functionName(int $arg1, $arg2): string
{
return 'foo';
}
public function anotherFunction(
string $foo,
string $bar,
int $baz
): string {
return 'foo';
}
public function functionName(?string $arg1, ?int &$arg2): ?string
{
return 'foo';
}
}
有參考運算子時:
&
時,和參數間不能有空格。(同上例)有可變長度參數時:
...
時,和參數間不能有空格。範例:
public function process(string $algorithm, ...$parts)
{
// 方法內容
}
有參考運算子及可變長度參數併用時:
&
及可變長度參數...
兩者之間不能有空格。public function process(string $algorithm, &...$parts)
{
// 方法內容
}
abstract
及 final
宣告必須在可見範圍宣告 (public, protected, private) 之前。範例:
<?php
namespace Vendor\Package;
abstract class ClassName
{
protected static $foo;
abstract protected function zim();
final public static function bar()
{
// 方法內容
}
}
static
宣告必須在可見範圍宣告 (public, protected, private) 之後。(如上例)函式的規範與類別的方法相同。
(
之間不能有空格。)
之前及之後不能有空格。,
之前不能有空格,之後保持一個空格。範例:
<?php
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);
範例:
<?php
$foo->bar(
$longArgument,
$longerArgument,
$muchLongerArgument
);
範例:
<?php
somefunction($foo, $bar, [
// ...
], $baz);
$app->get('/hello/{name}', function ($name) use ($app) {
return 'Hello ' . $app->escape($name);
});
if
的結構看起來如下,右大括號 }
和 elseif
、else
都在同一行。範例:
<?php
if ($expr1) {
// if 內容
} elseif ($expr2) {
// elseif 內容
} else {
// else 內容
}
elseif
而不是 else if
。範例:
<?php
if (
$expr1
&& $expr2
) {
// if 內容
} elseif (
$expr3
&& $expr4
) {
// elseif 內容
}
witch
的結構看起來如下。case
宣告必須從 switch
縮排一次。break
或其它終止流程的關鍵字必須和 `case 內容的縮排同層級。case
內容不為空,且無終止流程的關鍵字,必須加上註解 // no break
範例:
<?php
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;
}
)
和左大括{
號必須獨立一行,之間放一個空格。範例:
<?php
switch (
$expr1
&& $expr2
) {
// 內容
}
while
宣告的結構看起如下:
範例:
<?php
while ($expr) {
// 內容
}
)
和左大括{
號必須獨立一行,之間放一個空格。範例:
<?php
while (
$expr1
&& $expr2
) {
// 內容
}
do while
宣告的結構看起如下:
<?php
do {
// 內容
} while ($expr);
)
和左大括{
號必須獨立一行,之間放一個空格。範例:
<?php
do {
// 內容
} while (
$expr1
&& $expr2
);
for
宣告的結構看起如下:
範例:
<?php
for ($i = 0; $i < 10; $i++) {
// for 內容
}
)
和左大括{
號必須獨立一行,之間放一個空格。範例:
<?php
for (
$i = 0;
$i < 10;
$i++
) {
// for 內容
}
foreach
宣告的結構看起如下:
範例:
<?php
foreach ($iterable as $key => $value) {
// foreach 內容
}
try
、catch
、finally
宣告的結構看起如下:
範例:
<?php
try {
// try 內容
} catch (FirstThrowableType $e) {
// catch 內容
} catch (OtherThrowableType | AnotherThrowableType $e) {
// catch 內容
} finally {
// finally 內容
}
$i++;
++$j;
$intValue = (int) $input;
if ($a === $b) {
$foo = $bar ?? $a ?? $b;
} elseif ($a > $b) {
$foo = $a + $b * $c;
}
?
及 :
之間至少留一個空格。$variable = $foo ? 'foo' : 'bar';
$variable = $foo ?: 'bar';
function
關鍵字之後留一空格,並在 use
關鍵字之前留一空格。{
在同一行, 右大括號 }
在函式主體之後單獨一行。(
、)
之間不得有空格。,
之前不能有空格,之後保持一個空格。<?php
$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
// 內容
};
use
關鍵字,則冒號 :
必須在 use
的右括號之後且之間不能有空格。<?php
$closureWithArgsVarsAndReturn = function ($arg1, $arg2) use ($var1, $var2): bool {
// 內容
};
<?php
$longArgs_noVars = function (
$longArgument,
$longerArgument,
$muchLongerArgument
) {
// 內容
};
$noArgs_longVars = function () use (
$longVar1,
$longerVar2,
$muchLongerVar3
) {
// 內容
};
$longArgs_longVars = function (
$longArgument,
$longerArgument,
$muchLongerArgument
) use (
$longVar1,
$longerVar2,
$muchLongerVar3
) {
// 內容
};
$longArgs_shortVars = function (
$longArgument,
$longerArgument,
$muchLongerArgument
) use ($var1) {
// 內容
};
$shortArgs_longVars = function ($arg) use (
$longVar1,
$longerVar2,
$muchLongerVar3
) {
// 內容
};
<?php
$foo->bar(
$arg1,
function ($arg2) use ($var1) {
// 內容
},
$arg3
);
<?php
$instance = new class {};
implements
之後的介面在同一行,左大括號{
可以在同一行。<?php
$instance = new class extends \Foo implements \HandleableInterface {
// 類別內容
};
implements
之後的介面列表有換行,右大括號{
放在最後一個介面的下一行,獨立一行。$instance = new class extends \Foo implements
\ArrayAccess,
\Countable,
\Serializable
{
// 類別內容
};
雖然規格看似很多,但還是有沒提到的地方,就依個人習慣了。
例如 Zend Framework,程式碼中會看到像這樣的排版:
public function __construct(
string $containerName,
Router\RouterInterface $router,
?TemplateRendererInterface $template = null
) {
$this->containerName = $containerName;
$this->router = $router;
$this->template = $template;
}
變數的 =
會併排的很整齊。它的陣列排法也是:
return [
'paths' => [
'app' => [__DIR__ . '/../templates/app'],
'error' => [__DIR__ . '/../templates/error'],
'layout' => [__DIR__ . '/../templates/layout'],
],
];
陣列的 =>
也會依層級對齊。
如果有 PSR 建議規範沒提到的地方,不妨參考知名框架的程式碼,能得到一些想法。
程式碼風格建議規範可以大致看一下、瞭解一下即可。一開始還不熟悉的時候,我們有工具可以輔助。再後面的章節會介紹到 PHP CS Fixer 這套工具,能幫助我們在寫程式時提醒應該注意的地方。
下一篇筆者將介紹 PSR-4 自動載入,我們明天見囉。
本文同步更新於 TerryL 部落格 Day 11 - PHP 程式碼風格:PSR-1, PSR-12,歡迎前往討論。