跳到内容

编码标准

编辑此页

Symfony 代码由世界各地数千名开发者贡献。为了使每段代码看起来和感觉起来都熟悉,Symfony 定义了一些所有贡献都必须遵循的编码标准。

这些 Symfony 编码标准基于 PSR-1PSR-2PSR-4PSR-12 标准,因此您可能已经了解其中大部分。

使你的代码遵循编码标准

Symfony 使确保你贡献的代码与预期的代码语法匹配变得简单,而无需手动审查代码。首先,安装 PHP CS Fixer 工具,然后运行此命令来修复任何问题

1
2
$ cd your-project/
$ php php-cs-fixer.phar fix -v

如果您忘记运行此命令并提交包含任何语法问题的拉取请求,我们的自动化工具会警告您并提供解决方案。

Symfony 编码标准详解

如果您想详细了解 Symfony 编码标准,这里有一个简短的示例,其中包含下面描述的大多数特性

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Acme;

use Other\Qux;

/**
 * Coding standards demonstration.
 */
class FooBar
{
    public const SOME_CONST = 42;

    private string $fooBar;

    /**
     * @param $dummy some argument description
     */
    public function __construct(
        string $dummy,
        private Qux $qux,
    ) {
        $this->fooBar = $this->transformText($dummy);
    }

    /**
     * @deprecated
     */
    public function someDeprecatedMethod(): string
    {
        trigger_deprecation('symfony/package-name', '5.1', 'The %s() method is deprecated, use Acme\Baz::someMethod() instead.', __METHOD__);

        return Baz::someMethod();
    }

    /**
     * Transforms the input given as the first argument.
     *
     * @param $options an options collection to be used within the transformation
     *
     * @throws \RuntimeException when an invalid option is provided
     */
    private function transformText(bool|string $dummy, array $options = []): ?string
    {
        $defaultOptions = [
            'some_default' => 'values',
            'another_default' => 'more values',
        ];

        foreach ($options as $name => $value) {
            if (!array_key_exists($name, $defaultOptions)) {
                throw new \RuntimeException(sprintf('Unrecognized option "%s"', $name));
            }
        }

        $mergedOptions = array_merge($defaultOptions, $options);

        if (true === $dummy) {
            return 'something';
        }

        if (\is_string($dummy)) {
            if ('values' === $mergedOptions['some_default']) {
                return substr($dummy, 0, 5);
            }

            return ucwords($dummy);
        }

        return null;
    }

    /**
     * Performs some basic operations for a given value.
     */
    private function performOperations(mixed $value = null, bool $theSwitch = false): void
    {
        if (!$theSwitch) {
            return;
        }

        $this->qux->doFoo($value);
        $this->qux->doBar($value);
    }
}

结构

  • 在每个逗号分隔符后添加一个空格;
  • 在二元运算符(==&&、...)周围添加一个空格,连接(.)运算符除外;
  • 将一元运算符(!--、...)放置在受影响变量的旁边;
  • 始终使用完全比较,除非您需要类型转换;
  • 在针对表达式检查变量时使用 Yoda 条件,以避免在条件语句中意外赋值(这适用于 ==!====!==);
  • 在多行数组中,即使在最后一个项目之后,也要在每个数组项目后添加逗号;
  • return 语句之前添加一个空行,除非 return 语句单独位于语句组内(如 if 语句);
  • 当函数显式返回 null 值时使用 return null;,当函数返回 void 值时使用 return;
  • 不要在测试方法中添加 void 返回类型;
  • 使用花括号指示控制结构体,无论它包含多少条语句;
  • 每个文件定义一个类 - 这不适用于不打算从外部实例化,因此不受 PSR-0PSR-4 自动加载标准约束的私有辅助类;
  • 在与类名相同的行上声明类继承和所有实现的接口;
  • 在方法之前声明类属性;
  • 首先声明公共方法,然后是受保护的方法,最后是私有方法。此规则的例外是类构造函数和 PHPUnit 测试的 setUp()tearDown() 方法,它们必须始终是第一个方法,以提高可读性;
  • 将所有参数声明在与方法/函数名称相同的行上,无论有多少个参数。唯一的例外是使用构造函数属性提升的构造函数方法,其中每个参数必须在新行上,并带有尾随逗号
  • 实例化类时使用括号,无论构造函数有多少个参数;
  • 异常和错误消息字符串必须使用 sprintf 连接;
  • 异常和错误消息不得包含反引号,即使在引用技术元素(如方法或变量名)时也是如此。任何时候都必须使用双引号

    1
    2
    - Expected `foo` option to be one of ...
    + Expected "foo" option to be one of ...
  • 异常和错误消息必须以大写字母开头,并以句点 . 结尾;
  • 包含类名的异常、错误和弃用消息必须使用 get_debug_type() 而不是 ::class 来检索它

    1
    2
    - throw new \Exception(sprintf('Command "%s" failed.', $command::class));
    + throw new \Exception(sprintf('Command "%s" failed.', get_debug_type($command)));
  • 在返回或抛出某些内容的 ifcase 条件之后,不要使用 elseelseifbreak
  • 不要在 [ 偏移访问器周围和 ] 偏移访问器之前使用空格;
  • 为每个不属于全局命名空间的类添加 use 语句;
  • 当 PHPDoc 标签(如 @param@return)包含 null 和其他类型时,始终将 null 放在类型列表的末尾。

命名约定

  • 对 PHP 变量、函数和方法名、参数使用 camelCase(例如,$acceptableContentTypeshasSession());
对配置参数、路由名称和 Twig 模板使用 snake_case
变量(例如,framework.csrf_protectionhttp_status_code);
  • 对常量使用 SCREAMING_SNAKE_CASE(例如,InputArgument::IS_ARRAY);
  • 对枚举情况使用 UpperCamelCase(例如,InputArgumentMode::IsArray);
  • 对所有 PHP 类、接口、trait 和枚举使用命名空间,并对其名称使用 UpperCamelCase(例如,ConsoleLogger);
  • 所有抽象类都以 Abstract 为前缀,PHPUnit *TestCase 除外。请注意,一些早期的 Symfony 类不遵循此约定,并且出于向后兼容性的原因而未重命名。但是,所有新的抽象类都必须遵循此命名约定;
  • 接口以 Interface 为后缀;
  • Trait 以 Trait 为后缀;
  • 不要为类或枚举使用专用后缀(例如 ClassEnum),除非在下面列出的情况下。
  • 异常以 Exception 为后缀;
  • 与服务配置相关的 PHP 属性以 As 为前缀(例如,#[AsCommand]#[AsEventListener] 等);
  • 与控制器参数相关的 PHP 属性以 Map 为前缀(例如,#[MapEntity]#[MapCurrentUser] 等);
  • 对 PHP 文件命名使用 UpperCamelCase(例如,EnvVarProcessor.php),对 Twig 模板和 Web 资源命名使用 snake_case(section_layout.html.twigindex.scss);
  • 对于 PHPDocs 和类型转换中的类型提示,请使用 bool(而不是 booleanBoolean)、int(而不是 integer)、float(而不是 doublereal);
  • 不要忘记查看更详细的 Conventions 文档,以了解更多主观的命名注意事项。

服务命名约定

  • 服务名称必须与其类的完全限定类名 (FQCN) 相同(例如,App\EventSubscriber\UserSubscriber);
  • 如果同一类有多个服务,则主服务使用 FQCN,其余服务使用小写和下划线名称。可以选择用点分隔成组(例如,something.service_namefos_user.something.service_name);
  • 参数名称使用小写字母(除非在使用 %env(VARIABLE_NAME)% 语法引用环境变量时);
  • 为公共服务添加类别名(例如,将 Symfony\Component\Something\ClassName 别名为 something.service_name)。

文档

  • 仅当 PHPDoc 块添加不重复名称、原生类型声明或上下文(例如 instanceof 检查)的相关信息时,才为类、方法和函数添加 PHPDoc 块;
  • 仅使用 PHPDoc 参考中定义的注解和类型。为了改进静态分析的类型,也允许使用以下注解

  • 将相同类型的注解分组在一起,以便相同类型的注解紧随其后,不同类型的注解用一个空行分隔;
  • 如果方法不返回任何内容,则省略 @return 注解;
  • 不要在类、方法和函数上使用单行 PHPDoc 块,即使它们只包含一个注解(例如,不要将 /** {@inheritdoc} */ 放在一行中);
  • 当添加新类或对现有类进行重大更改时,可以添加或扩展包含个人联系信息的 @author 标记。请注意,可以根据向核心团队的请求更新或删除个人联系信息。

许可协议

  • Symfony 在 MIT 许可证下发布,并且许可证块必须存在于每个 PHP 文件的顶部,命名空间之前。
这项工作,包括代码示例,均根据 Creative Commons BY-SA 3.0 许可证获得许可。
目录
    版本