Modern PHP PSR(什么是PSR-1 PSR-2 PSR-3 PSR-4标准?)

PSR是什么?

PSR是 PHP Standards Recommendation(PHP推荐标准)的简称/如果最近读过关于
PHP的博客,你或许见过PSR-1、PSR-2和PSR3等术语。这些都是 PHP-FIG制定的推荐 规范。这些规范的名称以PSR-开头,后面跟着一个数字。PHP-FIG制定的每个推荐规范
用于解决大多数PHP框架经常会遇到的某个具体问題。PHP框架无需频繁解决相同的问
题,它们可以遵守PHP-FIG制定的推荐规范,使用共用的方案来解决。

截至本书出版时,PHP-FIG发布了五个推荐规范

PSR-1:基本的代码风格(https://www.php-fig.org/psr/psr-1/)
PSR-2:严格的代码风格(https://www.php-fig.org/psr/psr-2/)
PSR-3:日志记录器接口(http://www.php-fig.org/psr/psr-3)
PSR-4:自动加载(http://www.php-fig.org/psr/psr-4)

注意:如果你数了、发现只有四个推荐規范、你数得对、PHP-FIG放弃了第一份推规范
PSR-0 (http://www.php-fig.org/psr/psr-0)。第一位推荐规范被新发布的PSR-4替代了。

注意、这些 PHP-FIG:推荐規范与我前面提到的三种实现互操作性的方法(接口、自动加
线和代码风格)是一一对应的、这可不是巧合。

PHP-FIG发布的这些推落規范让我特别高兴、它们是现代PP生态系的牢固基石,定义了PHP组件和框架实现互操作性的方式。我承认,PHP标准不是最吸引人的话题,但
(我觉得)这些标准是理解现代PHP的前提。

PSR-1:基本的代码风格

如果想写符合社区标准的PHP代码、首先要连守PSR。这是最容易守的PHP标准
簧单到你可能已经使用了・PSR-1提出了简单的指导方针・这方针易于实现,只 要极少的工作量。PSR1的目的是为遵守这一标准的PHP概架提供代码风格基准。符合

PSR-1的代码必須满足以下要求:

PHP标签

必须把PHP代码放在<php ?>或 <?= ?>标签中。不得使用其他PHIP标签句法。

编码*

所有PHP文件都必须使用UTF-8字符集编码,而且不能有字节顺序标记(Byte
Order Mark,BOM)。这个要求听起来很复杂,共实文本编辑器或IDE都能自动做
到这一点。

目的

个PHP文件可以定义符号(类、性状、函数和常量等),或者执行有副作用的操
作(例如,生成结果或处理数据)、但不能同时做这两件事。这是一个简单的要
求,我们只需稍微深谋远虑一些。

自动加载
PHP命名空间和类必须違守PSR+自动加器标准,我们只需为PHP符号选择合适
的名称,并把定义符号的文件放在预期的位置。稍后我们会讨论PSR-4

类的名称
PHP类的名称必须一直使用驼峰式( CamelCase)。这种格式也叫标题式
(TitleCase)。例如 CoffeeGrinder、CoffeeBean 和 PourOver。

常量的名称
PHP常量的名称必须全部使用大写字母。如果需要,可以使用下划线把单词分开。
例如WOOT、LET_OUR_POWERS COMBINE和 GREAT SCOTT。

方法的名称
PHP方法的名称必须一直使用cameLCase 这种驼峰式。也就是说,方法名的首字
母是小写的,后续单词的首字母都是大写的。例如 phpIsAwesome、 iLoveBacon和
tennantIsMyFavoriteDoctor。

PSR-2:严格的代码风格

贯彻PSR-1之后,下一步要实施PSR-2。PSR-2使用更严格的指导方针进一步定义PHP的
代码风格。

PSR-2制定的代码风格对有多个来自世界各地的贡献者的PHP框架来说,可谓是及时雨。因为每个贡献者都有自己独特的风格和偏好,严格的通用代码风格能让开发者轻易地编写代码,而且其他贡献者能快速理解代码的作用。

与PSR-1不同,PSR-2推荐规范中的指导方针更严格。你可能不喜欢PSR-2中的某些指导
方针,可这是很多PHP流行框架选择使用的代码风格。你没必要非得遵守PSR-2,但是
如果遵守的话,其他开发者能轻易地理解你的PHP代码,会有更多的人使用你的代码,为代码做贡献。

建议:你应该使用更为严格的PSR-2代码风格。虽然名称中“严格”两字,其实代码写起来十分容
易。写得多了,最终会变成第二天性。而且,有工具能把现有的PHP代码自动格式化成符
合PSR-2风格的代码。

贯彻PSR-1
使用PSR-2代码风格之前先要贯彻PSR-1代码风格

缩进
这个话题很热门,通常分为两个阵营:第一个阵营选择使用一个制表符缩进,第二
个阵营选择使用多个空格缩进(这样酷多了)。PSR-2推荐规范要求PHP代码使用
四个空格缩进。

建议:以我个人的经验来看,缩进更适合使用空格,因为空格最可靠,在不同的代码编辑器 中渲染的效果基本一致。而制表符的宽度各异,在不同的代码编辑器中渲染的效果也
不同。为了得到最好的外观一致性,请使用四个空格缩进代码。

文件和代码行
PHP文件必须使用UNIX风格的换行符(LF),最后要有一个空行、而且不
PHP关闭标签 ?>。每行代码不能超过80个字符,至少不能超过120个字答、每行末尾不能有空格。这些要求听起来需要做很多工作,其实不然。大多数代码编辑器都
能在指定的宽度处换行,删除行尾的空白,并使用UNIX风格的换行符、这些要求
都能自动实现,因此你无需担心。

建议:一开始我觉得不写PHP关闭标签 ?>很奇怪。其实,最好不写关闭标签,这样能避免意料之外的输出错误。如果加上关闭标签 ?>,而且在关闭标签后有空行,那么这个空
会被当成输出,导致出错(例如,设定HTTP首部时)

关键字
我认识很多PHP开发者,他们会使用全部大写的TRUE、 FALSE和NULL。如果你也是
这么写的,从现在开始摒弃这种写法,只使用小写字母形式。PSR-2推荐规范要求,PHP关健字都应该他用小写字

命名空间

每个命名空间声明语句后必须跟着一个空行。类似地,使用use关键字导入命名空
间或为命名空间创建别名时,在一系列use声明语句后要加一个空行。下面是一个示例:

<?php
namespace My\Component;

use Symfony\Components\HttpFoundation\Request;
use Symfony\Components\HttpFoundation\Response;

class App
{
	// 类的定义体
}


与缩进方式一样,类定义体的括号位置是另一个引起激烈争论的话题。有些人选择
把起始括号和类名放在同一行,另一些人则选择在类名之后新起一行写起始括号
PSR-2推荐规范要求,类定义体的起始括号应该在类名之后新起一行写,如下述示例
所示。类定义体的结東括号必须在定义体之后新起一行写。如果你已经这么做
了,会觉得这没什么。如果类扩展其他类或实现接口, extends和 implements 关键
字必须和类名写在同一行。

<?php 
namespace My\App; 

class Administrator extends User
{
	// 类的定义体
}

方法
方法定义体的括号位置和类定义体的括号位置一样:方法定义体的起始括号要在方
法名之后新起一行写;方法定义体的结東括号要在方法定义体之后新起一行写。要
特别注意方法的参数:起始圆括号之后没有空格,结束圆括号之前也没有空格。方
法的每个参数(除了最后一个)后面有一个返号和空格。

<?php
namespace Animals;

Class StrawNeckedibis
{
	public function flapWings($numberOfTimes = 3, $speed ="fast")
	{
		// 方法的定义体
	}
}

可见性

类中的每个属性和方法都要声明可见性。可见性由pub1ic、 protected或 private
指定,其作用是决定在类的内部和外部如何访问属性和方法。传统的PHP开发者
可能习惯在类的属性前加上vax关键字,在私有方法的名称前加上下划线(二)。
别这么做,我们应该使用前面列出的可见性关键字。如果把类属性或方法声明为
abstract或 final,这两个限定符必须放在可见性关键字之前。如果把属性或方法
声明为 static,这个限定符必须放在可见性关键字之后。

<?php
namespace Animals;

class StrawNeckedIbis
{
	//指定了可见性的静态属性
	public static $numberOfBirds = 0;

	// 指定了可见性的方法 
	public function __construct()
	{
		static::$numberOfBirds++;
	}
}

控制结构

我最常在这条指导方针上犯错。所有控制结构关键字后面都要有一个空格。控制
结构关键字包括:if、 elseif、else、 Switch、case、 while、 do while、for、
foreach、try和 catch。如果控制结构关键字后面有一对圆括号,起始圆括号后面
不能有空格,结東圆括号之前不能有空格。与类和方法的定义体不同,控结
键字后面的起始括号应该和控制结构关键字写在同一行。控制结构关键字后面的结
束括号必须单独写在一行。下面是一个简单的示例,展示了上述指导方针:

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

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

建议:我们可以自动实施PSR-1和PSR-2代码风格,很多代码编辑器都能根据PSR-1和PSR1.
自动格式化代码。有些工具还能根据PHP标准,帮你审核并格式化代码,例如PHP
Code Sniffer(也叫 phpcs,http://bit.ly/phpsniffer)。这个工具(可以直接在命令行中
使用,也可以集成到IDE中使用)能指出你的代码和指定PHP代码标准之间的差异
大多数包管理器(例如PEAR、 Homebrew、 Aptitude或Yum)都能安装 phpcs

你还可以使用法比安博滕斯尔开发的PHP-CS- Fixer(http://cs.sensiolabs.org/)自动
纠正大多数不符合标准的代码。这个工具并不完美,但是使用这个工具只需做少量工
作或无需做任何工作,就能让代码更符合PSR规范。

编写PSR-3:日志记录器接口

PHP-FIG发布的第三个推荐规范与前两个不同,不是一系列指导方针,而是一个接口,
规定PHP日志记录器组件可以实现的方法。

注意:日志记录器是对象,用于把不同重要程度的消息写入指定的输出。记录的消息用于诊断、
检査和排除应用中的操作、稳定性和性能方面的问题。例如,在开发过程中把调试信息
人文本文件;捕获网站的流量统计信息,写入数据库;把致命错误的诊断信息通过电子
件发给网站管理员。最受欢迎的PHP日志记录器组件是由乔迪·波哥阿诺开发的 monolog/monolog (https://packagist.org/packages/monolog/monolog)

大多数PHP框架都在某种程度上实现了日志功能。在PHP-FIG出现之前,每个框架使用
不同的方式实现日志功能,通常会使用专属的实现方式。为了实现互操作性和专业化
也就是现代PHP提倡的重复利用,PHP-FIG制定了PSR-3日志记录器接口。若想使用符合
PSR-3规范的日志记录器,框架要做到两件重要的事:日志功能委托给第三方库实现
最终用户能选择使用他们喜欢的日志记录器组件。这么做对所有人都好。

编写PSR-3日志记录器

符合PSR-3推荐规范的PHP日志记录器组件,必须包含一个实现 PSRILO8 \Loggerinter
face接口的PHP类。PSR-3接口复用了RFC5424系统日志协议 (hti/. tools, ietf. org/hrml
rfc5424),规定要实现九个方法:

<?php
namespace Psr\Log;

interface LoggerInterface
{ 
	public function emergency($message, array $context =array());
	public function alert($message, array $context=array());
	public function critical($message, array $context =array());
	public function error($message, array $context =array()); 
	public function warning($message, array $context =array());
	public function notice($message, array $context =array());
	public function info($message, array $context =array());
	public function debug($message, array $context =array());
	public function log($level, $message, array $context =array());
} 

每个方法对应RFC 5424协议的一个日志级别,而且都接受两个参数。第一个参数
$message必须是一个字符串,或者是一个有 _toString() 方法的对象。第二个参数
$context是可选的,这是一个数组,提供用于替换第一个参数中占位标记的值。

建议: c o n t e x t 参 数 用 于 构 造 复 杂 的 日 志 消 息 。 消 息 文 本 中 可 以 使 用 占 位 符 , 例 如 p l a c e h o l d e r n a m e 。 占 位 符 由 、 占 位 符 的 名 称 和 组 成 , 不 能 包 含 空 格 。 context 参数用于构造复杂的日志消息。消息文本中可以使用占位符,例如{ placeholder_name}。 占位符由{、占位符的名称和 } 组成,不能包含空格。 context使placeholdernamecontexta参数的值是一个关联
数组,键是占位符的名称(没有花括号),对应的值用于替换消息文本中的占位符。

如果想编写符合PSR-3规范的日志记录器,要创建一个实现 Psr\Log Vloggerinterfacef接
口的PHP类,而且要提供这个接口中每个方法的具体实现

使用PSR-3日志记录器

如果你正在编写自己的PSR-3日志记录器,停下来,想想自己是不是在浪费时间。我强
烈不建议你自己编写日志记录器。为什么呢? 因为已经有一些十分出色的PHP日志记录
器组件了。

如果需要符合PSR-3规范的日志记录器,使用 monolog/monolog(https://packagist.org
packages/monolog/monolog)即可,别浪费时间找其他组件了。 Monolog组件完全实现
了PSR-3接口,而且便于使用自定义的消息格式化程序和处理程序扩展功能。 Monolog
的消息处理程序可以把日志消息写入文本文件、系统日志和数据库,能通过电子邮件发
送,还能传给 Hip Chat、 Slack、网络中的服务器和远程AP。只要你能想到的日志处理
方式, Monolog几乎都提供了。如果非常不巧, Monolog没有提供你所需的处理程序、
自己编写消息处理程序并将其集成到 Monolog也特别容易。示例3-1展示了如何设量Monolog,把日志消息写入文本文件。你可以看出这个操作是多么简单。

示例3-1:使用 Monolog

<php 
require 'vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// 准备日志记录器
$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自动加载器

PHP-FIG发布的第四个推荐规范描述了一个标准的自动加裁器策略。自动加载器略是
载器标准的PHP组件和框架,使用同一个自动加载器就能找到相关代码,然后将其数入 指,在运行时按活查找PHP类、接口或性状、井将共载人PP解释器支特PSR4自动加
PHP解释器。这是了不起的功能,把现代PHP生态系统中很多可互操作的组件联系起来

为什么自动加载器很重要

在PHP文件的顶部你是不是经常看到类似下面的代码?

<?php

include 'path/to/file1.php';
include 'path/to/file2.php';
include 'path/to/file3.php';
 

太常见了,是吧? 你可能熟知 require()、 require_once()、 include()和 include_once()函数的作用:把外部PHP文件载入当前脚本。如果只需载入几个PHP脚本,使用
这些函数能很好地完成工作。可是,如果需要引入一百个PHP脚本,或者需要引入一千
个PHP脚本呢? 此时,require()函数和 include()函数无法胜任,这就是为什么PHP自
动加载器很重要的原因。有了自动加载器,我们无需像前面那样手动引入文件,自动加
载器策略能找到PHP类、接口或性状,然后在运行时按需将其载入PHP解释器。

在PHP-FIG发布PSR-4推荐规范之前,PHP组件和框架的作者使用 autoload()和sql
autoload_register()函数注册自定义的自动加载器策略。可是,每个PHP阻件和框架
都使用独特的自动加载器,而且每个自动加载器使用不同的逻辑査找并加载PHP类、
接口和性状。使用这些组件和框架的开发者,在引导PHP应用时必须调用每个组件各自
的自动加载器。我一直使用 Sensio Labs开发的Twig(http://twig.sensiolabs.org)模板组
件,这个组件很棒。可是,如果没有PSR-4,我要阅读Tvig的文档,弄清如何在应用的
引导文件中注册这个组件自定义的自动加载器,如下所示:

<?php
require_once “/path/to/lib/Twig/Autoloader.php”;
Twig_Autoloader::register();

试想一下,如果要研究每个PHP组件的自动加载器,然后在自己的应用中注册,那该有
多麻烦啊!PHP-FIG认识到了这个问题,推荐使用PSR-4自动加载器规范,促进组件实
现互操作性。得益于PSR-4,我们只需使用一个自动加載器就能自动加载应用中的所有
PHP组件。这太棒了!大多数现代的PHP组件和框架都符合PSR-4规范。如果你自己编写
并分发组件,确保你的组件也符合PSR-4规范。符合这一规范的组件包括: Symfony、
Doctrine、 Monolog、Twig、 Guzzle、 Swiftmailer、 PHPUnit和 Carbon等。

PSR-4自动加载器策略

与其他PHP自动加载器一样,PSR-4描述的策略用于在运行时査找并加载PHP类、接口和
性状。PSR-4推春规范不要求改变代码的实现方式,只建议如何使用文件系统目录结构
和PHP命名空间组织代码。PSR-4自动加载器策略依赖PHP命名空间和文件系统目录结本
査找并加载PHP类、接口和性状。

PSR-4的精髓是把命名空间的前缀和文件系统中的目录对应起来。例如,我可以告诉
PHP, \Orei1 lymmodernphp命名空间中的类、接口和性状在物理文件系统的src/目录
中,这样PHP就知道,前缀为 \Oreilly\ModernPHP的命名空间中的类、接口或性状对应
于src/目录里的目录和文件。例如,\Oreilly\ Modernphp\Chapter1命名空间对应于src/
Chapteri1目录, \Oreilly\Modernphp\Chapter1类对应于src/Example.php文件。

建议:PSR-4会把命名空间的前经和文件系统中的目录对应起来。命名空间的前缀可以是顶层命
名空间,也可以是顶层命名空间加上任意一个子命名空间,相当灵活。

还记得我们在第2章讨论的厂商命名空间吗? PSR-4自动加载器策略对开发组件和框架供
其他开发者使用的作者来说最有用。PHP组件的代码在唯一的厂商命名空间中,而且组
件的作者会指定厂商命名空间对应于文件系统中的哪个目录,这和我前面演示的做法完
全一样。我们在第4章会进一步探讨这个概念。

如何编写PSR-4自动加载器(以及为什么不该这么做)

我们知道,符合PSR-4规范的代码有个命名空间前缀对应于文件系统中的基目录;还知道,这个命名空间前缀中的子命名空间对应于这个基目录里的子目录。示例3-2实现了一个自动加载器,
这段代码摘自 PHP-FIG网站(http://bit.ly/php-fig)。 这个自动加载器会根据PSR-4自动加我器策路査找并加载类、接口和性状。

示例3-2:PSR-4自动加载器

<?php
/**
 * 举例说明如何实现项目专用的自动加载器。
 *
 * 使用SPL注册这个自动加载函数后, 遇到下述代码时这个函数
 * 会尝试从 /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;
    }
});

复制上述代码,将其粘贴到你的应用中,然后修改变量$prefix和 $base_dir的的值,这样
你就有一个可用的PSR-4自动加我器了。不过,如果你发现自己在编写PSR-4自动加载
器,请停下来,然后问自己有必要这么做吗。为什么呢?因为我们可以使用依赖管理器
Composer自动生成的PSR-4自动加载器。很巧,接下来在第4章我们就会讨论这个话题。

来源: Modern PHP 第三章 标准

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值