PHP中的PSR规范

什么是PSR?

PSR是PHP Standards Recommendation的简称,这个是php-fig组织制定的一套规范。至今,php-fig已经发布了五个规范:

PSR-0:自动加载标准,2014-10-21该标准已经被废弃,使用PSR-4替代,不再细讲

PSR-1:基本的编码风格

PSR-2:编码风格(更严格)

PSR-3:日志记录器接口

PSR-4:自动加载

PSR-1

PHP标签:

PHP代码必须放在标签或标签中。

编码:

PHP文件必须使用无BOM的UTF-8编码。

副作用:

一个PHP文件可以定义符号(比如类、函数、常量等),或者执行只有唯一副作用的操作(比如输出结果、处理数据等),但是不能同时做这两件事,尽量是一个PHP文件的功能单一。在操作的时候尽量把变量、类、函数的声明分开,通过或文件的方式来使用。

如下不符合规范:

// 改变设置

ini_set('error_reporting',E_ALL);

// 加载文件

include"file.php";

// 打印输出

echo"\n";

// 声明

functionfoo()

{

// function body

}

符合规范如下:

// 声明

functionfoo()

{

// function body

}

// 条件判断

if(! function_exists('bar')) {

functionbar()

{

// function body

}

}

命名空间和类:

命名空间和类必须遵循PSR-4自动加载器标准。

类的名称:

每个类都有自己的命名空间,且都在顶级命名空间下,类名必须使用驼峰式(CamelCase)。

PHP 5.3 及以上,必须使用正式的命名空间,例如:

// PHP 5.3 及以后

namespaceVendor\Model;

classFoo

{

}

PHP 5.3一下应该使用开头的伪命名空间约定,例如:

// PHP 5.3以下

classVendor_Model_Foo

{

}

常量必须全部是用大写,并且使用下划线(_)分开。例如:

namespaceVendor\Model;

classFoo

{

constVERSION='1.0';

constDATE_APPROVED='2012-06-01';

}

类的方法:

类的方法必须使用小写字母开头的驼峰式(camelCase)命名。

PSR-2

PSR-2是对PSR-1的PHP的扩充。

贯彻PSR-1:

使用PSR-2代码标准之前要先贯彻PSR-1的代码标准。

文件和代码行:

PHP文件必须使用Unix风格的换行符(LF, linefeed),最后要有一个空行,仅包含PHP代码的文件而且不能使用PHP关闭标签,每行代码不应该超过80个字符,每行末尾不能有空格,每行只能有一条语句,可以在适当的地方添加空行提高代码的阅读性。

不加上关闭标签,可以避免意料之外的输出错误,如果加上关闭标签,且在关闭标签后有空行,那么空行会被当成输出,导致意想不到的错误。

缩进:

必须以4个空格为缩进,不能使用制表符(Tab键)缩进。

在不同的编辑器中,空格的渲染效果基本一致,而制表符的宽度各有差异。

关键字:

PHP的关键字必须使用小写,而且, , 和 也必须小写。

命名空间和声明:

现在,声明之后必须要有一个空行,而且声明必须放在之后,必须分别使用引入命名空间,而且后要有空行,例如:

namespaceVendor\Package;

useFooClass;

useBarClassasBar;

useOtherVendor\OtherPackage\BazClass;

// ... additional PHP code ...

类的继承和实现:

和关键字必须和类名在同一行,类、接口和定义体的起始括号应该在类名之后新起一行,结束括号也必须新起一行,例如:

namespaceVendor\Package;

useFooClass;

useBarClassasBar;

useOtherVendor\OtherPackage\BazClass;

classClassNameextendsParentClassimplements\ArrayAccess,\Countable

{

// constants, properties, methods

}

如果后面后很多类导致一行很长,可以依次将需要的类另起新行并缩进4个空格,如下:

namespaceVendor\Package;

useFooClass;

useBarClassasBar;

useOtherVendor\OtherPackage\BazClass;

classClassNameextendsParentClassimplements

\ArrayAccess,

\Countable,

\Serializable

{

// constants, properties, methods

}

可见性:

类中的每个属性和方法都要声明可见性,有、和,不能使用关键词来声明,老版本的PHP会在私有属性前加上,一行只能声明一个属性,例如:

namespaceVendor\Package;

classClassName

{

public$foo=null;

}

方法:

类中的所有方法也应该定义可见性,方法名后面不能有空格,方法体的括号位置和类定义体的括号位置一样,都要新起一行,结束括号也要新起一行。方法参数的起始圆括号之后没有空格,结束括号之前也没有空格,有多个参数是,每个参数的逗号后面加一个空格,例如:

namespaceVendor\Package;

classClassName

{

public functionfooBarBaz($arg1,&$arg2,$arg3= [])

{

// method body

}

}

如果参数比较多,需要换行时,可以如下:

namespaceVendor\Package;

classClassName

{

public functionaVeryLongMethodName(

ClassTypeHint$arg1,

&$arg2,

array$arg3= []

) {

// method body

}

}

、和:

现在,、必须在可见性修饰符之前,声明必须放在可见性修饰符之后,例如:

namespaceVendor\Package;

abstract classClassName

{

protected static$foo;

abstract protected functionzim();

final public static functionbar()

{

// method body

}

}

方法和函数的调用:

在调用方法和函数时,圆括号必须跟在函数名之后,函数的参数之间有一个空格:

bar();

$foo->bar($arg1);

Foo::bar($arg2,$arg3);

如果参数比较多,一行放不下时,如下处理:

$foo->bar(

$longArgument,

$longerArgument,

$muchLongerArgument

);

PHP的控制结构:

PHP的控制结构包括if、else、elseif、switch、case、while、do while、for、foreach、try和catch。如果这些关键词后面有一对原括号,开始括号前必须有一个空格,与方法和类的定义体不同,控制结构关键词后面的起始括号应该和控制结构关键词写在同一行,例如:

$gorilla=new\Animals\Gorilla;

$ibis=new\Animals\StrawNeckedIbis;

if($gorilla->isWake() ===true) {

do{

$gorilla->beatChest();

}while($ibis->isAsleep() ===true);

$ibis->flyAway();

}

PHP闭包函数:

闭包函数在声明时,关键词后必须有一个空格,同时关键词前后也必须有一个空格。起始大括号不需要另起新行,详细的如下代码:

$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

};

注意:以上规则同样适用于将闭包作为函数或方法的参数,如下:

$foo->bar(

$arg1,

function($arg2)use($var1) {

// body

},

$arg3

);

PSR-3

与PSR-1和PSR-2不同,PSR-3规定了一套通用的日志记录器接口(),为了符合PSR-3规范,框架必须实现该规范中的接口,这样可以更多的兼容第三方应用。PSR-3规范中包含了9个方法,每个方法都对应了RFC 5424协议的一个日志级别,而且都接受两个参数和,如下:

namespacePsr\Log;

/**

* Describes a logger instance

*

* The message MUST be a string or object implementing __toString().

*

* The message MAY contain placeholders in the form: 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".

*

* Seehttps://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md

* for the full interface specification.

*/

interfaceLoggerInterface

{

/**

* System is unusable.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functionemergency($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.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functionalert($message,array$context=array());

/**

* Critical conditions.

*

* Example: Application component unavailable, unexpected exception.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functioncritical($message,array$context=array());

/**

* Runtime errors that do not require immediate action but should typically

* be logged and monitored.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functionerror($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.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functionwarning($message,array$context=array());

/**

* Normal but significant events.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functionnotice($message,array$context=array());

/**

* Interesting events.

*

* Example: User logs in, SQL logs.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functioninfo($message,array$context=array());

/**

* Detailed debug information.

*

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functiondebug($message,array$context=array());

/**

* Logs with an arbitrary level.

*

*@parammixed $level

*@paramstring $message

*@paramarray $context

*@returnvoid

*/

public functionlog($level,$message,array$context=array());

}

关于message参数

必须是一个字符串或者是含有方法的对象,应该包含占位符,例如,占位符由、占位符名称和组成,不能包含空格,占位符名称可以由组成,第三方实现可以用参数来替换占位符,占位符名称必须和数组的key对应。如下例子是使用中的值替换中的占位符:

/**

* Interpolates context values into the message placeholders.

*/

functioninterpolate($message,array$context=array())

{

// build a replacement array with braces around the context keys

$replace=array();

foreach($contextas$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

returnstrtr($message,$replace);

}

// a message with brace-delimited placeholder names

$message="User created";

// a context array of placeholder names => replacement values

$context=array('username'=>'Bolivar');

// echoes "User Bolivar created"

echointerpolate($message,$context);

关于context参数:

是一个数组参数,用于构造复杂的日志消息,中的值不能跑出任何PHP异常或错误。如果中包含对象,则该对象的必须为。

PSR-3日志记录器的使用

推荐使用monolog/monolog,这样可以让我们不需要浪费更多的时间在编写一个日志记录器了。Monolog组建完全实现了PSR-3接口,而且便于使用自定义的消息格式化程序和处理程序扩展功能,通过Monolog可以把日志消息写入文本文件、系统日志和数据库中,还能通过电子邮件发送,并且还支持Slack和远程服务器。如下展示了如何设置Monolog,并把日志消息写入文本文件:

useMonolog/Logger;useMonolog/Handler/StreamHandler;// 创建日志记录器

$log =newLogger('myApp');$log->pushHandler(newStreamHandler('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规范描述了一个标准的自动加载器策略,指在运行时按需查找PHP类、接口或。支持PSR-4自动加载器标准的PHP组建和框架,使用同一个自动加载器就能找到相关代码,然后将其载入PHP解释器。有了这个功能,就可以把现代PHP生态系统中很多客户操作的组件联系起来。

编写一个PSR-4自动加载器

PSR-4规范不要求改变代码的实现方式,只建议如何使用文件系统目录结构和PHP命名空间组织代码,PSR-4规范以来PHP命名空间和文件系统目录结构查找并加载PHP类、接口和,这正是PSR-4的精髓所在。下面我们来自己手动实现一个PSR-4自动加载器:

/**

* 使用SPL组册这个自动加载函数后,遇到下述代码时这个函数会尝试 从/path/to/project/src/Baz/Qux.php文件中加载\Foo\Bar\Baz\Qux类:

* new \Foo\Bar\Baz\Qux;

*@paramstring $class 完全限定的类名。

*@returnvoid

**/

spl_autoload_register(function($class) {

// 项目的命名空间前缀

$prefix='Foo\\Bar\\';

// 目录前缀对应的根目录

$base_dir=__DIR__.'/src/';

// 判断传入的类是否使用了这个命名空间前缀

$len= strlen($prefix);

if(strncmp($prefix,$class,$len) !==) {

// 没有使用,交给注册的下一个自动加载器处理

return;

}

// 获取去掉前缀后的类名

$relative_class= substr($class,$len);

// 把命名空间前缀替换成根目录,

// 在去掉前缀的类名中,把命名空间分隔符替换成目录分隔符,

// 然后在后面加上.php

$file=$base_dir. str_replace('\\','/',$relative_class) .'.php';

// 如果该文件存在,就将其导入

if(file_exists($file)) {

require$file;

}

});

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180604G1Q50R00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券