PHP 编码规范 旨在提供一套指导原则和最佳实践,以确保 PHP 代码的一致性、可读性、可维护性团队协作效率。在 PHP 社区中,PSR (PHP Standards Recommendations) 是最广泛接受和遵循的编码规范。遵循这些规范不仅能让你的代码更容易被其他 PHP 开发者理解,也能提高代码本身的质量和减少潜在错误,同时促进不同框架和库之间的互操作性。

核心思想:一致性至关重要。代码是写给人看的,不是机器。清晰、简洁、可读的代码能够极大地提高开发效率和项目成功率。遵循 PSR 规范,让你的代码更具通用性和专业性。


一、PHP 编码哲学与 PSR

PHP 语言虽然以其灵活性和“快速启动”而闻名,但其社区也逐渐形成了一套成熟的编码约定,以解决早期版本中常见的代码风格混乱问题。PSR (PHP Standards Recommendations) 正是这些约定的核心。

PSR 是什么?
PSR 是由 PHP 框架互操作性组 (PHP Framework Interoperability Group, FIG) 制定和推荐的一系列规范。它并非强制性标准,但被绝大多数现代 PHP 框架、库和开发者广泛接受和遵循。

主要相关的 PSR 规范:

  • PSR-1 (Basic Coding Standard):基本编码标准,涵盖了最基础的命名、文件结构等。
  • PSR-2 (Coding Style Guide):编码风格指南,扩展了 PSR-1,定义了更详细的格式化规则(如缩进、空格、括号位置等)。
  • PSR-12 (Extended Coding Style Guide):扩展编码风格指南,取代了 PSR-2,并与 PHP 7.0+ 的新特性保持一致,提供了更全面的风格规则。
  • PSR-4 (Autoloader):自动加载规范,定义了文件路径到类名的映射规则,是现代 PHP 项目类自动加载的基础。

为什么它如此重要?

  • 统一性:遵循 PSR 的 PHP 代码在风格上高度一致,降低了阅读和理解他人代码的认知负担。
  • 互操作性:PSR-4 等规范使得不同的 PHP 库和框架可以无缝地集成和协作。
  • 专业性:遵循社区约定是成为一名合格的 PHP 开发者和构建高质量项目的重要标志。
  • 可维护性:一致且清晰的代码更容易维护和调试。

二、自动化工具

在现代 PHP 开发中,自动化工具是强制执行编码规范的得力助手,可以大幅减少手动审查的工作量。

2.1 PHP-CS-Fixer (代码风格修复工具)

  • 独断专行 (Opinionated)PHP-CS-Fixer 是一款功能强大的代码风格修复工具,它可以自动检测并修复代码中不符合 PSR 规范(或其他配置的风格)的问题。
  • 优点:解决了团队内部关于代码格式的争论,所有代码都将拥有统一的风格。
  • 安装composer require friendsofphp/php-cs-fixer --dev
  • 配置 (.php-cs-fixer.dist.php 示例)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php

    $finder = (new PhpCsFixer\Finder())
    ->in(__DIR__)
    ->exclude('var')
    ->exclude('vendor')
    ;

    return (new PhpCsFixer\Config())
    ->setRules([
    '@PSR12' => true, // 遵循 PSR-12 规范
    'array_syntax' => ['syntax' => 'short'], // 短数组语法 `[]`
    'cast_spaces' => ['space' => 'none'], // 强制类型转换时不加空格 (e.g., (string)$var)
    'concat_space' => ['spacing' => 'none'], // 连接符 `.` 两侧不加空格
    'declare_strict_types' => true, // 声明 strict_types=1
    'global_namespace_import' => ['import_classes' => true, 'import_constants' => true, 'import_functions' => true],
    'ordered_imports' => ['sort_algorithm' => 'alpha'], // 导入语句按字母排序
    'phpdoc_add_missing_param_annotation' => ['only_untyped' => false], // 补充缺少的 @param
    'phpdoc_order' => true, // PHP Doc Tags 顺序
    'phpdoc_to_comment' => false, // 不将 PHP Doc 转换为单行注释
    'single_line_comment_style' => ['comment_types' => ['hash']], // 单行注释用 `//`
    'trailing_comma_in_multiline' => ['elements' => ['arrays', 'parameters', 'arguments']], // 多行数组、参数、调用添加尾部逗号
    'semicolon_after_instruction' => true, // 语句后必须有分号
    'no_unused_imports' => true, // 移除未使用的导入
    ])
    ->setFinder($finder)
    ;
  • 使用
    1
    2
    vendor/bin/php-cs-fixer fix # 修复所有文件
    vendor/bin/php-cs-fixer fix src/Controller/MyController.php # 修复单个文件

2.2 PHP_CodeSniffer (代码风格检查工具)

  • 静态分析PHP_CodeSniffer (包括 phpcsphpcbf) 是一个用于检查 PHP 代码是否符合预设编码标准(如 PSR-12)以及发现常见编程错误的工具。
  • 优点:在代码提交前发现风格和潜在的运行时问题。phpcbf 可以自动修复一些问题。
  • 安装composer require squizlabs/php_codesniffer --dev
  • 配置 (phpcs.xml 示例)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0"?>
    <ruleset name="MyProject">
    <description>My custom coding standard based on PSR12.</description>
    <rule ref="PSR12"/> <!-- 遵循 PSR-12 规范 -->
    <!-- 可以添加或排除特定的规则 -->
    <exclude name="PSR1.Classes.ClassDeclaration.MultipleClasses"/>
    <file>src</file>
    <file>tests</file>
    </ruleset>
  • 使用
    1
    2
    3
    vendor/bin/phpcs # 检查所有文件
    vendor/bin/phpcs src/Controller/MyController.php # 检查单个文件
    vendor/bin/phpcbf # 自动修复部分问题

三、基本编码标准 (PSR-1, PSR-12 Essentials)

这些是 PHP 代码最基础且最常自动化的格式和结构规则。

3.1 PHP 标签

  • <?php<?=:永远使用标准的 <?php<?=(用于 echo)标签。

  • 避免短标签:禁止使用短开标签 <?

    1
    2
    3
    4
    5
    6
    <?php // Good
    echo 'Hello';

    <?= 'World'; // Good

    // <? echo 'Bad'; // Bad

3.2 文件编码

  • UTF-8 (无 BOM):所有 PHP 文件都必须使用 UTF-8 编码,并且不带字节顺序标记 (BOM)。

3.3 文件结尾和类文件

  • ?> 标签:仅包含 PHP 代码的文件,文件末尾的 ?> 关闭标签可以省略。强烈建议省略,以防止意外输出空白字符导致 HTTP 头发送问题。

  • 每文件一类:每个文件应该只包含一个类、接口、trait 或枚举。

    1
    2
    3
    4
    5
    6
    7
    8
    <?php // Good - 没有关闭标签

    namespace App\Entity;

    class User
    {
    // ...
    }

四、格式化 (PSR-12 Essentials)

这些规则通常由 PHP-CS-FixerPHP_CodeSniffer 自动处理。

4.1 缩进 (Indentation)

  • 4 个空格:每个缩进级别使用 4 个空格。绝不允许使用 Tab 字符

4.2 行长度 (Line Length)

  • 软限制 120 字符:虽然没有硬性限制,但建议每行不超过 120 个字符。超过 120 字符的行应拆分。
  • 硬限制 80 字符 (推荐):强烈建议将行长度限制在 80 个字符以内,这在代码审查和分屏显示时效果最佳。

4.3 空行 (Blank Lines)

  • 函数/方法之间:类中的方法定义之间用一个空行分隔。

  • 逻辑块之间:在函数或方法内部,可以使用空行来分隔逻辑相关的代码块,提高可读性。

  • 文件末尾:文件必须以单个非空白行结束。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?php

    namespace App\Service;

    class UserService
    {
    public function getUser(int $id): ?User
    {
    // Some logic here

    $user = $this->repository->find($id);

    // More logic or validation

    return $user;
    }

    public function createUser(array $data): User
    {
    // ...
    }
    }

4.4 声明块 (use, const, property)

  • use 声明

    • use 声明必须在命名空间声明之后,类、接口、trait 声明之前。
    • 每条 use 声明语句必须独立成行。
    • use 块必须在内部保持一行空白。
    • 推荐按字母顺序排序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php

    namespace App\Controller;

    use App\Entity\User;
    use App\Service\UserService;
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Response;

    class UserController extends AbstractController
    {
    // ...
    }
  • 属性声明

    • 每个类属性必须单独声明。
    • 属性的类型声明和默认值必须与属性名在同一行。
    • 属性的可见性(public, protected, private)必须在 var 关键字(如果使用)或类型声明之前。
    1
    2
    3
    4
    5
    6
    7
    8
    <?php

    class Product
    {
    public int $id;
    protected string $name = '';
    private float $price = 0.0;
    }

4.5 结构体 (Control Structures)

  • if, elseif, else

    • 关键字后跟一个空格。
    • 左括号 ( 和右括号 ) 之间没有空格。
    • 代码块的左花括号 { 必须在条件语句的同一行,并与 if 关键字用一个空格分隔。
    • 右花括号 } 必须在新行。
    • elseelseif 必须与上一个代码块的右花括号在同一行。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?php

    if ($condition) {
    // code
    } elseif ($otherCondition) {
    // code
    } else {
    // code
    }
  • for, foreach, while, do-while:遵循与 if 结构相同的格式化规则。

  • switch

    • 关键字后跟一个空格,左括号 ( 和右括号 ) 之间没有空格。
    • 左花括号 { 在同一行,右花括号 } 在新行。
    • casedefault 关键字必须缩进一层。
    • breakcontinue 关键字必须与 case 内部的代码块在同一缩进级别。
    • case 内部的代码块必须缩进一层。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php

    switch ($expression) {
    case 0:
    $message = 'Zero';
    break;
    case 1:
    $message = 'One';
    break;
    default:
    $message = 'Other';
    break;
    }

4.6 函数和方法

  • 可见性:必须声明方法的所有可见性(public, protected, private)。abstractfinal 必须在可见性之前。static 必须在可见性之后。

  • 函数声明

    • 函数名与左括号 ( 之间没有空格。
    • 参数列表的左括号 ( 和右括号 ) 之间没有空格。
    • 每个参数前应有类型提示。
    • 参数之间用逗号 , 分隔,逗号后跟一个空格。
    • 参数列表过长时,可以在每个参数后换行,并进行适当缩进。
    • 返回类型声明 :<type> 必须紧跟右括号 ),之间没有空格。
    • 左花括号 { 必须在函数签名的同一行,并用一个空格分隔。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <?php

    class MyClass
    {
    public static function staticMethod(
    string $param1,
    ?int $param2 = null
    ): string {
    // ...
    return 'result';
    }

    private function calculateValue(int $a, int $b): int
    {
    return $a + $b;
    }
    }

五、命名规范 (Naming Conventions)

PSR 规范对命名有严格的规定。

5.1 类、接口、Trait、枚举

  • PascalCase (大驼峰命名法):每个单词的首字母大写,不使用下划线。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php

    namespace App\Entity;

    class UserProfile
    {
    // ...
    }

    interface PaymentGateway
    {
    // ...
    }

5.2 类常量

  • ALL_CAPS (全大写,下划线分隔):所有字母大写,单词之间用下划线 _ 分隔。
    1
    2
    3
    4
    5
    6
    7
    <?php

    class Foo
    {
    public const VERSION = '1.0';
    public const STATUS_ACTIVE = 1;
    }

5.3 属性 (Properties)

  • camelCase (小驼峰命名法):第一个单词的首字母小写,后续单词的首字母大写。
    • 可见性:必须声明属性的可见性(public, protected, private)。
    1
    2
    3
    4
    5
    6
    7
    8
    <?php

    class User
    {
    public int $id;
    protected string $firstName;
    private string $emailAddress;
    }

5.4 方法 (Methods)

  • camelCase (小驼峰命名法):第一个单词的首字母小写,后续单词的首字母大写。
  • 可见性:必须声明方法的所有可见性。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php

    class UserService
    {
    public function getUserById(int $id): User
    {
    // ...
    }

    protected function hashPassword(string $password): string
    {
    // ...
    }
    }

5.5 函数 (Functions)

  • 全局函数:虽然现代 PHP 开发中应尽量避免全局函数,但如果使用,也应遵循 camelCase

5.6 局部变量

  • camelCase (小驼峰命名法):与属性和方法类似。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <?php

    function calculateTotal(array $items): float
    {
    $totalAmount = 0.0;
    foreach ($items as $itemPrice) {
    $totalAmount += $itemPrice;
    }

    return $totalAmount;
    }

六、PHPDoc (文档块)

  • PHPDoc (PHP 文档块):所有类、方法、函数、属性都应该有 PHPDoc 文档块。它不仅提供文档,还能帮助 IDE 进行代码提示和静态分析。

  • 标准:遵循 PSR-5 (PHPDoc Standard) 规范 (尽管它目前仍是 Draft 状态,但被广泛接受)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <?php

    /**
    * Represents a user in the application.
    *
    * @property int $id The user's unique identifier.
    * @property string $name The user's full name.
    */
    class User
    {
    /**
    * User ID.
    *
    * @var int
    */
    public int $id;

    /**
    * Returns the user's full name.
    *
    * @param string $greeting An optional greeting prefix.
    * @return string The full name with greeting.
    * @throws \InvalidArgumentException If the greeting is empty.
    */
    public function getName(string $greeting = ''): string
    {
    if (empty($greeting)) {
    throw new \InvalidArgumentException('Greeting cannot be empty.');
    }

    return $greeting . ' ' . $this->name;
    }
    }
  • 常用标签@param, @return, @throws, @var, @deprecated, @see, @link 等。

七、类型提示 (Type Hints) (PHP 7.0+)

  • 强制使用:尽可能为函数参数、返回值和类属性添加类型提示。这能显著提高代码的清晰度、健壮性和可维护性。

  • 标量类型int, float, string, bool, array

  • 复合类型object, iterable, callable

  • 特殊类型void, null (PHP 7.1+), mixed (PHP 8.0+), static, self, parent

  • 联合类型int|string (PHP 8.0+)。

  • 属性类型public string $name; (PHP 7.4+)。

  • 严格类型:在文件顶部声明 declare(strict_types=1);,确保类型提示严格执行。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?php declare(strict_types=1);

    namespace App\Utils;

    class Calculator
    {
    public function add(int $a, int $b): int
    {
    return $a + $b;
    }

    public function divide(float $numerator, float $denominator): float|null
    {
    if ($denominator === 0.0) {
    return null;
    }

    return $numerator / $denominator;
    }

    private string $status; // PHP 7.4+ 属性类型

    public function __construct(string $initialStatus)
    {
    $this->status = $initialStatus;
    }
    }

八、总结

PHP 编码规范,尤其是以 PSR 为核心的规范,是构建高质量、可维护和专业 PHP 应用的基础。它促进了整个社区的合作和代码互操作性。

  • 自动化是基石:充分利用 PHP-CS-FixerPHP_CodeSniffer 等工具,让它们自动化处理代码风格和大部分潜在错误。
  • 遵循 PSR:将 PSR 规范视为你的 PHP 开发圣经,尤其关注 PSR-12 和 PSR-4。
  • 类型提示:积极使用 PHP 7+ 提供的类型提示功能,并结合 declare(strict_types=1); 提高代码的健壮性。
  • 编写 PHPDoc:为所有公共 API 编写清晰的 PHPDoc,提高代码的可理解性和 IDE 支持。
  • 清晰与简洁:始终以提高代码可读性为目标,编写简洁、明确的代码。

通过持续学习和实践这些规范,你的 PHP 代码将更加专业、易于协作,并能更好地融入现代 PHP 生态系统。