PSR--PHP Standards Recommendation

960x220.png

什么是PSR瑞驱?

PSR是PHP Standards Recommendation的簡稱捌刮,這個是php-fig組織制定的一套規(guī)范浸间。至今恨憎,php-fig已經(jīng)發(fā)布了五個規(guī)范:

  • PSR-0:自動加載標準,2014-10-21該標準已經(jīng)被廢棄验夯,使用PSR-4替代猖吴,不再細講
  • PSR-1:基本的編碼風格
  • PSR-2:編碼風格(更嚴格)
  • PSR-3:日志記錄器接口
  • PSR-4:自動加載

PSR-1

PHP標簽:
PHP代碼必須放在<?php ?>標簽或<?= ?>標簽中。

編碼:
PHP文件必須使用無BOMUTF-8編碼挥转。

副作用:
一個PHP文件可以定義符號(比如類海蔽、函數(shù)、常量等)绑谣,或者執(zhí)行只有唯一副作用的操作(比如輸出結(jié)果党窜、處理數(shù)據(jù)等),但是不能同時做這兩件事借宵,盡量是一個PHP文件的功能單一幌衣。在操作的時候盡量把變量、類壤玫、函數(shù)的聲明分開豁护,通過includerequire文件的方式來使用。

如下不符合規(guī)范:

<?php
// 改變設(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
    }
}

命名空間和類:
命名空間和類必須遵循PSR-4自動加載器標準欲间。

類的名稱:
每個類都有自己的命名空間楚里,且都在頂級命名空間下,類名必須使用駝峰式(CamelCase)猎贴。
PHP 5.3 及以上班缎,必須使用正式的命名空間,例如:

<?php
// PHP 5.3 及以后
namespace Vendor\Model;

class Foo
{
}

PHP 5.3一下應該使用Vendor_開頭的偽命名空間約定她渴,例如:

<?php
// PHP 5.3以下
class Vendor_Model_Foo
{
}

常量:
常量必須全部是用大寫达址,并且使用下劃線(_)分開。例如:

<?php
namespace Vendor\Model;

class Foo
{
    const VERSION = '1.0';
    const DATE_APPROVED = '2012-06-01';
}

類的方法:
類的方法必須使用小寫字母開頭的駝峰式(camelCase)命名惹骂。

PSR-2

PSR-2是對PSR-1的PHP的擴充苏携。

貫徹PSR-1:
使用PSR-2代碼標準之前要先貫徹PSR-1的代碼標準。

文件和代碼行:
PHP文件必須使用Unix風格的換行符(LF对粪, linefeed)右冻,最后要有一個空行,僅包含PHP代碼的文件而且不能使用PHP關(guān)閉標簽?>著拭,每行代碼不應該超過80個字符纱扭,每行末尾不能有空格,每行只能有一條語句儡遮,可以在適當?shù)牡胤教砑涌招刑岣叽a的閱讀性乳蛾。

不加上?>關(guān)閉標簽,可以避免意料之外的輸出錯誤鄙币,如果加上關(guān)閉標簽肃叶,且在關(guān)閉標簽后有空行,那么空行會被當成輸出十嘿,導致意想不到的錯誤因惭。

縮進:
必須以4個空格為縮進,不能使用制表符(Tab鍵)縮進绩衷。

在不同的編輯器中蹦魔,空格的渲染效果基本一致,而制表符的寬度各有差異咳燕。

關(guān)鍵字:
PHP的關(guān)鍵字必須使用小寫勿决,而且true, false, 和 null必須小寫。

命名空間和use聲明:
現(xiàn)在招盲,namespace聲明之后必須要有一個空行低缩,而且use聲明必須放在namespace之后,必須分別使用use引入命名空間曹货,而且use后要有空行咆繁,例如:

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

// ... additional PHP code ...

類的繼承和實現(xiàn):
extendsimplements關(guān)鍵字必須和類名在同一行,類控乾、接口和Traits定義體的起始括號應該在類名之后新起一行么介,結(jié)束括號也必須新起一行,例如:

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

class ClassName extends ParentClass implements \ArrayAccess, \Countable
{
    // constants, properties, methods
}

如果implements后面后很多類導致一行很長蜕衡,可以依次將需要的類另起新行并縮進4個空格壤短,如下:

<?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
}

可見性:
類中的每個屬性和方法都要聲明可見性,有public慨仿、privateprotected久脯,不能使用var關(guān)鍵詞來聲明,老版本的PHP會在私有屬性前加上_镰吆,一行只能聲明一個屬性帘撰,例如:

<?php
namespace Vendor\Package;

class ClassName
{
    public $foo = null;
}

方法:
類中的所有方法也應該定義可見性,方法名后面不能有空格万皿,方法體的括號位置和類定義體的括號位置一樣摧找,都要新起一行核行,結(jié)束括號也要新起一行。方法參數(shù)的起始圓括號之后沒有空格蹬耘,結(jié)束括號之前也沒有空格芝雪,有多個參數(shù)是,每個參數(shù)的逗號后面加一個空格综苔,例如:

<?php
namespace Vendor\Package;

class ClassName
{
    public function fooBarBaz($arg1, &$arg2, $arg3 = [])
    {
        // method body
    }
}

如果參數(shù)比較多惩系,需要換行時,可以如下:

<?php
namespace Vendor\Package;

class ClassName
{
    public function aVeryLongMethodName(
        ClassTypeHint $arg1,
        &$arg2,
        array $arg3 = []
    ) {
        // method body
    }
}

abstract如筛、finalstatic
現(xiàn)在堡牡,abstractfinal必須在可見性修飾符之前杨刨,static聲明必須放在可見性修飾符之后晤柄,例如:

<?php
namespace Vendor\Package;

abstract class ClassName
{
    protected static $foo;

    abstract protected function zim();

    final public static function bar()
    {
        // method body
    }
}

方法和函數(shù)的調(diào)用:
在調(diào)用方法和函數(shù)時,圓括號必須跟在函數(shù)名之后拭嫁,函數(shù)的參數(shù)之間有一個空格:

<?php
bar();
$foo->bar($arg1);
Foo::bar($arg2, $arg3);

如果參數(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)鍵詞后面有一對原括號涨薪,開始括號前必須有一個空格,與方法和類的定義體不同炫乓,控制結(jié)構(gòu)關(guān)鍵詞后面的起始括號應該和控制結(jié)構(gòu)關(guān)鍵詞寫在同一行刚夺,例如:

<?php
$gorilla = new \Animals\Gorilla;
$ibis = new \Animals\StrawNeckedIbis;

if ($gorilla->isWake() === true) {
    do {
        $gorilla->beatChest();
    } while ($ibis->isAsleep() === true);

    $ibis->flyAway();
}

PHP閉包函數(shù):
閉包函數(shù)在聲明時,function關(guān)鍵詞后必須有一個空格末捣,同時use關(guān)鍵詞前后也必須有一個空格侠姑。起始大括號不需要另起新行,詳細的如下代碼:

<?php
$closureWithArgs = function ($arg1, $arg2) {
    // body
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // body
};

閉包函數(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ī)范安吁,框架必須實現(xiàn)該規(guī)范中的接口醉蚁,這樣可以更多的兼容第三方應用。PSR-3規(guī)范中包含了9個方法柳畔,每個方法都對應了RFC 5424協(xié)議的一個日志級別馍管,而且都接受兩個參數(shù)$message$context郭赐,如下:

<?php

namespace 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必須是一個字符串或者是含有__toString()方法的對象薪韩,$message應該包含占位符,例如{placeholder_name}捌锭,占位符由{俘陷、占位符名稱和}組成,不能包含空格观谦,占位符名稱可以由A-Z, a-z, 0-9, _組成拉盾,第三方實現(xiàn)可以用$context參數(shù)來替換占位符,占位符名稱必須$context數(shù)組的key對應豁状。如下例子是使用$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 string
        if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
            $replace['{' . $key . '}'] = $val;
        }
    }

    // interpolate replacement values into the message and return
    return 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是一個數(shù)組參數(shù)捉偏,用于構(gòu)造復雜的日志消息,$context中的值不能跑出任何PHP異承汉欤或錯誤夭禽。如果$context中包含Exception對象,則該對象的key必須exception谊路。

PSR-3日志記錄器的使用

推薦使用monolog/monolog讹躯,這樣可以讓我們不需要浪費更多的時間在編寫一個日志記錄器了。Monolog組建完全實現(xiàn)了PSR-3接口缠劝,而且便于使用自定義的消息格式化程序和處理程序擴展功能潮梯,通過Monolog可以把日志消息寫入文本文件、系統(tǒng)日志和數(shù)據(jù)庫中惨恭,還能通過電子郵件發(fā)送秉馏,并且還支持Slack和遠程服務器。如下展示了如何設(shè)置Monolog脱羡,并把日志消息寫入文本文件:

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ī)范描述了一個標準的自動加載器策略萝究,指在運行時按需查找PHP類、接口或Traits轻黑。支持PSR-4自動加載器標準的PHP組建和框架糊肤,使用同一個自動加載器就能找到相關(guān)代碼,然后將其載入PHP解釋器氓鄙。有了這個功能馆揉,就可以把現(xiàn)代PHP生態(tài)系統(tǒng)中很多客戶操作的組件聯(lián)系起來。

編寫一個PSR-4自動加載器

PSR-4規(guī)范不要求改變代碼的實現(xiàn)方式抖拦,只建議如何使用文件系統(tǒng)目錄結(jié)構(gòu)和PHP命名空間組織代碼升酣,PSR-4規(guī)范以來PHP命名空間和文件系統(tǒng)目錄結(jié)構(gòu)查找并加載PHP類舷暮、接口和Traits,這正是PSR-4的精髓所在噩茄。下面我們來自己手動實現(xiàn)一個PSR-4自動加載器:

<?php
/**
 * 使用SPL組冊這個自動加載函數(shù)后下面,遇到下述代碼時這個函數(shù)會嘗試   從/path/to/project/src/Baz/Qux.php文件中加載\Foo\Bar\Baz\Qux類:
 *  new \Foo\Bar\Baz\Qux;
 * @param string $class 完全限定的類名。
 * @return void
 **/
spl_autoload_register(function ($class) {
    // 項目的命名空間前綴
    $prefix = 'Foo\\Bar\\';

    // 目錄前綴對應的根目錄
    $base_dir = __DIR__ . '/src/';

    // 判斷傳入的類是否使用了這個命名空間前綴
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // 沒有使用绩聘,交給注冊的下一個自動加載器處理
        return;
    }

    // 獲取去掉前綴后的類名
    $relative_class = substr($class, $len);

    // 把命名空間前綴替換成根目錄沥割,
    // 在去掉前綴的類名中,把命名空間分隔符替換成目錄分隔符凿菩,
    // 然后在后面加上.php
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    // 如果該文件存在机杜,就將其導入
    if (file_exists($file)) {
        require $file;
    }
});

這樣我們就寫好了一個PSR-4的自動加載器了,更多關(guān)于PHP的自動加載機器可以看我這篇文章衅谷。

參考:

  1. 《Morden PHP中文版》
  2. https://www.php-fig.org
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末椒拗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子获黔,更是在濱河造成了極大的恐慌蚀苛,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件玷氏,死亡現(xiàn)場離奇詭異堵未,居然都是意外死亡,警方通過查閱死者的電腦和手機预茄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進店門兴溜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人耻陕,你說我怎么就攤上這事拙徽。” “怎么了诗宣?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵膘怕,是天一觀的道長。 經(jīng)常有香客問我召庞,道長岛心,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任篮灼,我火速辦了婚禮忘古,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诅诱。我一直安慰自己髓堪,他們只是感情好,可當我...
    茶點故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著干旁,像睡著了一般驶沼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上争群,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天回怜,我揣著相機與錄音,去河邊找鬼换薄。 笑死玉雾,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的专控。 我是一名探鬼主播抹凳,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼伦腐!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起失都,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤柏蘑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粹庞,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咳焚,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年庞溜,在試婚紗的時候發(fā)現(xiàn)自己被綠了革半。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,673評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡流码,死狀恐怖又官,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情漫试,我是刑警寧澤六敬,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站驾荣,受9級特大地震影響外构,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜播掷,卻給世界環(huán)境...
    茶點故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一审编、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧歧匈,春花似錦垒酬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽般码。三九已至,卻和暖如春乱顾,著一層夾襖步出監(jiān)牢的瞬間板祝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工走净, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留券时,地道東北人。 一個月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓伏伯,卻偏偏與公主長得像橘洞,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子说搅,可洞房花燭夜當晚...
    茶點故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 什么是psr-0,psr-1,psr-2標準 FIG組織在制定跟PHP相關(guān)規(guī)范炸枣,簡稱PSR,PSR旨在通過討論我們...
    meng_philip123閱讀 1,673評論 0 2
  • 什么是PSR弄唧? PSR是PHP Standards Recommendation的簡稱适肠,這個是php-fig[ht...
    正義的程序員閱讀 47,311評論 4 34
  • 整理自 PHP 標準規(guī)范 作為程序員來說,采用統(tǒng)一的編碼風格是非常重要的候引。這將給未來代碼的編寫侯养、閱讀節(jié)省大量時間。...
    野塵lxw閱讀 705評論 0 2
  • 因為在心底里感覺先母就和在世時一樣澄干,從未離開過我逛揩; 因為母子之情是世上任何情感都無與倫比,最難形諸筆端麸俘; 因為紅塵...
    清水一滴閱讀 2,006評論 28 33
  • 時節(jié)不居辩稽,歲月如流~轉(zhuǎn)眼間時間來到了2019年~雖然我已經(jīng)從一個小孩成為了一個小孩的媽媽,但依然清晰的記得小時候過...
    咖菲菲閱讀 447評論 0 5