Skip to content

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

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 标准规范资源

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions