Skip to content

PHP 标准规范[进行中] #66

@huliuqing

Description

@huliuqing

PHP 标准规范

PHP 标准规范

PSR: 是 PHP Standard Recommendations 缩写,有 PHP FIG 组织指定的PHP 开发实践规范。

PHP FIG: 是 Framework Interoperablility Group(框架可互用性小组)缩写,于 2009 年由几位开源框架开发者成立。

目前已通过表决六套方案,并得到大部分 PHP 框架的支持和认可。

一 基础编码规范

PSR-1 标准: 基础编码规范

1.1 名词解释

  • 必须(MUST): 绝对,严格无条件遵守
  • 一定不要(MUST NOT): 严格禁止
  • 应该(SHOULD): 强烈建议这样做,但不强求
  • 不应该(SHOULD NOT):强烈不建议这样做,但不强求
  • 可以(MAY)和 可选(OPTIONAL): 选择性高一点

1.2 文件

1.2.1 PHP 标签

必须 使用 长标签 或 <?= > 短标签,一定不可 使用任何自定义标签

1.2.2 PHP 字符编码

PHP 文件必须 采用 无 BOMUTF-8 字符编码

1.2.3 副作用

一个 PHP 文件 应该

  • 要么定义类声明,如类、函数、常量等不产生副作用的操作
  • 要么仅书写产生副作用的逻辑操作

不能兼而有之。

「副作用」是什么?

仅通过包含文件而执行的逻辑操作(不需要直接声明类、函数和常量)

「副作用」种类说明
  • 生成输出 echo "";
  • 直接 requireinclude
  • 设置 ini 配置 ini_set("error_reporting", E_ALL);
  • 连接外部服务
  • 抛出错误或异常
  • 修改全局或静态变量
  • 读或写文件

产生副作用编码示例

<?php

// 「副作用」: 修改 ini 配置
ini_set("error_reporting", E_ALL);

// 「副作用」: 引入文件
include "filename.php";

// 「副作用」:生成输出
echo "<html></html>";

//声明函数
function foo()
{
}

不产生「副作用」的代码

<?php
//声明函数
function foo()
{

}

// 条件声明 不 属于「副作用」
if (! function_exists("bar") )
{
    function bar()
    {

    }
}

1.3 命名空间与类

命名空间与类必须遵循 PSR-4 标准: 自动加载规范

  • 每个类必须使用独立的文件
  • 命名空间至少有一个层级,顶级层级是组织名称(Vendor name)
  • 必须使用 StudlyCaps 首字母大写的驼峰命名规范
  • PHP 5.3 之后 必须 使用正式的命名空间
  • PHP 5.2.x 及一下版本 应该 使用伪命名空间的写法,约定俗成使用的顶级的组织名称(Vendor name)如 Vendor_ 为类前缀

示例:

<?php
// PHP 5.3 及以后版本类命名
namespace Vendor\Model;

class Foo
{
}
<?php
// PHP 5.2.x 及以前版命名空间使用
class Vendor_Model_Foo
{
}

1.4 类的常量、属性和方法

类:指接口或可复用的代码块traits

1.4.1 常量

全部字母 必须 使用大写,单词以下划线分隔

1.4.2 属性

属性可以遵循:

  • 首字母大写的驼峰命名 $StudlyCaps
  • 首字母小写的驼峰命名 $studlyCpas
  • 下划线分隔 $under_score

1.4.3 方法

必须 符合 camelCase() 首字母小写的驼峰命名规范

二 编码风格规范

PSR-2 标准: 编码风格规范

编码风格规范是对基础编码规范的继承和扩展

2 个示例看规范

示例1:

<?php
namespace Vendor\Model;

use FooInterface;
use BarClass as Bar;
use OtherVendor\OtherModel\BazClass;

/**
* 编码风格规范:
* 1. 文件 **必须** 以一个空白行结尾
* 2. PHP 文件 **必须** 省略结束标签 **?>**
* 3. 一行代码 **必须** 限制在 120 个字符以内,**不应该** 超过 80 个字符
*    如果超过,**应该** 拆分成多行
* 4. PHP 所有 **关键词** 及 常量 true/false/null **必须**小写;关键词包括但不限于 private, public, 
*    class, final, static 等
* 5. namespace 声明完成后 **必须** 插入空白行,如本文第 151 行即示例第 3 行
* 6. use **必须** 在 namespace 声明后
* 7. 每条 use **必须** 有且只有一个 use  关键词
* 9. use 声明块后 **必须** 有一个空行,如本文第 155 行即示例第 7 行
* 10. extends 及 implements 关键词 **必须** 卸载类名称的同一行
* 11. 类开始 { 及结束花括号 } **必须** 独占一行
*/
class Foo extends Bar implements FooInterface
{
    /**
    * 12. 所有 方法 和 属性 **必须** 添加访问修饰符:private, protected, public
    * 13. **一定不可** 使用关键词 **var** 声明属性
    * 14. 每条语句 **一定不可** 定义超过一个属性
    * 15. **不该** 使用下划线作为前缀,区分 protected 或 private
    */
    private $name = '';

    // 参见 13
    // var $age = 18;

    // 参见 14
    // private $email;private $address;

    // 参见 15
    // private $_phone;    


    /**
    * 16. 方法名后 **一定不可** 有空格。如:sampleFunction($a, $b = null)
    * 17. 方法的开始花括号 { 和结束花括号 } **必须** 独占一行
    * 18. 参数左括号 ( 之后与右括号 ) 之前 **一定不可** 有空格
    * 19. 有默认值的参数 **必须** 放在参数列表结尾。如:$b
    * 20. 参数列表中每个逗号之后 **必须** 有一个空格,逗号之前 **一定不可以有空格**
    */
    public function sampleFunction($a, $b = null)
    {
    }

    /**
    * 21. 参数列表 **可以** 分列多行,包括第一个参数在内的所有参数 **必须** 单独成行
    * 22. 拆分为多行的参数列表的方法的结束括号 ) 和方法开始花括号 { **必须**卸载同一行,中间用空格分隔
    */
    public function multipleSampleFunction(
        $a,
        $b = null
    ) {

    }

    /**
    * 23. abstract, final 声明时,**必须** 写在访问操作符前
    * 24. static 关键词 **必须** 写在访问操作符之后
    */
    final public static function bar($a, $b = null)
    {
        /**
        * 25. 方法及函数调用是,方法名或函数名与参数左括号 ( 之间 **一定不可** 有空格
        * 26. 调用是参数可以分列多行,包括第一个参数在内的每个参数 **必须** 单独成行
        * 27. 控制结构关键词后 **必须** 有一个空格。如 if (
        * 28. 控制结构左括号 ( 之后及右括号 ) 之前 **一定不可** 有空格
        * 29. 控制结构 右括号 ) 与 方法左花括号 { 之间 **必须** 有一个空格。如 ) {
        * 30. 结构体 **必须** 有缩进
        * 31. 缩进 **必须** 使用 4 个空格符,而不是 「Tab 键」缩进
        * 32. 方法右括号 } **必须**  单独成行
        * 33. else if **应当** 替换为 elseif ,这样关键词都像单独的一个词
        */
        if ($a == $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar(
                $arg2,
                $arg3
            );
        }
    }
}

示例2:

<?php
namespace Vendor\Package;
 
use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

/**
* 34. implements 的继承列表也 **可以** 拆分称多行,但包括第一个在内的每个集成接口都 **必须** 单独成行
*/
class ClassName extends ParentClass implements
    \ArrayAccess,
    \Countable,
    \Serializable
{
    // 这里面是常量、属性、类方法
}

三 日志接口规范

PSR-3 标准: 日志接口规范

日志接口规范,定义了日志类库通用接口规范,如有日志记录需求的可以通过集成 Pst\Log\LoggerInterface 来记录日志信息

日志错误级别

  • debug: 详细调试信息
  • info: 关注的事件信息。示例:用户登录日志,SQL 语句日志
  • notice: 常见的消息事件
  • warning: 非致命错误的警告。示例: 弃用或不建议使用的 API
  • error: 运行时错误,不需要立即解决但是应该记录到日志
  • critical: 必要条件错误。示例:项目运行必要组件,或未知异常。
  • alert: 必须立即修复的错误,需要发送一个 SMS信息。示例:站点宕机,数据库无法连接等。
  • emergency: 紧急错误,系统不可用

接口方法

LoggerInterface 提供以上 8 个错误等级对应的日志接口

此外还提供 log() 方法,该方法第一个参数为日志错误级别标识,调用该方法结果 必须 与单独使用错误等级对应的方法结果相同

使用 monolog 日志库

TODO monolog: 是一款 PHP 符合 PSR 规范开发的日志记录库,可以将日志内容记录到 files(文件), sockets(套接字接口),inboxes(消息系统), databases(数据库)和 various web services (各种 web 服务)。

【翻译】Monolog使用说明 - 即Monolog中文文档

自动加载规范

PSR-4 标准: 自动加载规范

自动加载规范是对 TODO PHP 官方自动加载相关类 的规范

详细说明

  1. 此处的「类」泛指所有的「Class类」、「接口」、「traits 可复用代码块」以及其它类似结构。
  2. 一个完整的类名需具有以下结构:
 <code>\<命名空间>(\<子命名空间>)*\<类名></code>
  • 完整的类名 必须 要有一个顶级命名空间,被称为 "vendor namespace";
  • 完整的类名 可以 有一个或多个子命名空间;
  • 完整的类名 必须 有一个最终的类名;
  • 完整的类名中任意一部分中的下滑线都是没有特殊含义的;
  • 完整的类名 可以 由任意大小写字母组成;
  • 所有类名都 必须 是大小写敏感的。
  1. 当根据完整的类名载入相应的文件
  • 完整的类名中,去掉最前面的命名空间分隔符,前面连续的一个或多个命名空间和子命名空间,作为「命名空间前缀」,其必须与至少一个「文件基目录」相对应;
  • 紧接命名空间前缀后的子命名空间 必须 与相应的「文件基目录」相匹配,其中的命名空间分隔符将作为目录分隔符。
  • 末尾的类名 必须 与对应的以 .php 为后缀的文件同名。
  • 自动加载器(autoloader)的实现 一定不可 抛出异常、一定不可 触发任一级别的错误信息以及 不应该 有返回值。

TODO 自动加载规范 FIG 官方实例

缓存接口规范

PSR-6 标准: 缓存接口规范

目标

现在每个框架基本都有专属的功能多样的缓存库,但是这些库都有自己的实现 API,开发者在使用过程中都不得不学习每个框架的 API 接口。

缓存接口的目标:创建一套通用的接口规范,能够让开发人员整合到现有框架和系统,而不需要去 开发框架专属的适配器类

定义

  1. 调用类库
    调用者,使用缓存服务的类库,调用对象是本缓存接口规范的具体「实现类库」

  2. 实现类库

是「本缓存接口规范的」具体实现,实现类库必须实现 Cache\CacheItemPoolInterfaceCache\CacheItemInterface 接口

  1. 生存时间(TTL: Time To Live)

定义缓存存活时间,以秒为单位

  1. 过期时间

定义准确的过期时间点,通常为缓存存储时间点加上 TTL 时间值;
也可以是具体的 DateTime 对象

  1. 键(key)

长度为 1 的字符串,作为缓存项的唯一标识符。

必须 是:
A-Z, a-z, 0-9, _, . 的任意顺序的 UTF-8 编码,且长度小于 64

  1. 命中(Hit)

指在缓存系统中,调用类库使用「键」能够查找到对应的缓存项

调用类库应该先验证 isHit() 有命中后调用 get() 获取数据

  1. 未命中(Miss)

「命中」的反义

  1. 延迟(Dererred)

一个延迟的缓存,指的是这个缓存项可能不会立刻被存储到物理缓存池里

数据

实现类库 必须 支持所有 PHP 可序列化数据类型:

  • String
  • Integer
  • Float
  • Boolean
  • Null
  • Array
  • Object

且反序列化结果 必须 与原数据完全一致

主要概念

  1. 缓存池 Pool

缓存池包含缓存系统里所有缓存数据的集合

  1. 缓存项 Items

一条缓存项在缓存池里代表了一对「键/值」对应的数据,「键」被视为每一个缓存项主键,是缓存项的 唯一标识符,必须 是不可变更的,当然,「值」可以 任意变更

  1. 错误处理

接口

PSR-6 Cache Project

TODO HTTP 消息接口规范

PSR-7 标准: HTTP 消息接口规范

PHP 标准规范资源

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions