在项目和团队之间共享 PHP-CS-Fixer 规则

Laravel

PHP-CS-Fixer 是一个开源工具,它可以强制执行和检测违反 PHP 编码风格的行为。 使用预定义的规则,它可以使您拥有严格的编码风格,该风格由工具强制执行,因此您可以将时间花在更重要的事情上。

规则案例

这是 PHP-CS-Fixer 可以对您的代码库执行的操作的一些示例:

Rule: is_null

is_null($var) 表达式替换为 $var === null.

// before...
if (is_null($account->closed_at)) {
  // 
}

// after...
if ($account->closed_at === null) {
  // 
}

Rule: mb_str_functions

用相应的 mb 函数替换多字节不安全函数。

// before...
$length = strlen($request->post('slug'));

// after...
$length = mb_strlen($request->post('slug'));

Rule: not_operator_with_successor_space

逻辑运算符 (!) 后必须紧跟一个空格。

// before...
- if (!$user->is_active) {
  //
}

// after...
if (! $user->is_active) {
  //
}

可用的规则非常全面,并且数量不断增加。 您可以在 项目的 redme 文件 中查看可用规则的完整列表。 您可能还想看看 PHP-CS-Fixer配置,语言无法描述清楚的话, 该站点还提供了每个规则的作用示例。

热门提示:除了固定样式外,它通常还可以用作升级工具。 PHPUnit 8 在几种方法中添加了一个 void 返回类型。 在 /tests 目录中运行 PHP-CS-Fixer 的 void_return 规则可以立即升级您的测试套件,使其与这些更改兼容。

共享规则

如果在整个项目中使用都 PHP-CS-Fixer ,并且有一个定义代码风格的规则集。到目前为止,我新建一个项目时,我一直在复制粘贴规则,如果出现新规则,我必须更新配置,这并不是一个理想的工作流程。因为很容易遗漏更新某个项目,毕竟这是一堆手动工作。

事实证明,我们可以在多个项目和团队中共享规则, 因此我们所有的项目 composer update 时都可以随时使用最新版本的规则集。

搭建代码库

我们将建立一个代码库来存储规则集。首先我们初始化本地代码库并创建所需文件:

$ mkdir php-styles

$ cd php-styles

$ git init

$ echo "/composer.lock
/vendor
/.php_cs.cache" >> .gitignore

$ mkdir src

$ touch src/rules.php src/helpers.php composer.json

定义规则

正如我之前提到的,可能有很多规则,新版本有时还会增加新规则,因此,我发现跟踪上一次可用规则的 PHP-CS-Fixer 版本是非常方便。这意味着,当您升级到较新的版本时,您将知道要查找的发行版,以查找可能要合并到共享规则集中的新规则。我想在rules.php文件顶部添加最新的审阅版本。

<?php

// last reviewed: v2.16.3 Yellow Bird

接下来,我们要使我们的 rules.php 文件返回一个包含规则集的数组。 将规则放在单独的文件中会比较方便,因为列表可能会很长,具体取决于您的规范。 它还允许其他开发人员引入您的规则集,并将其与自己的规则集合并,例如,如果您想引入 Laravel 编码标准,并将其与团队其他一些标准结合在一起。

<?php

// last reviewed: v2.16.3 Yellow Bird

return [
  '@PSR2' => true,
  'array_syntax' => ['syntax' => 'short'],
  'final_class' => false,
  'new_with_braces' => true,
  // ...
];

PHP-CS-Fixer 预定义了一些规则集。 与PSR-2标准有关的所有规则都捆绑在规则集@ PSR2中。 这使我们可以选择加入标准,而不必单独指定每个规则。

一些规则具有与之关联的选项。 使用 array_syntax 规则可以指定是短数组还是长数组语法。

其他规则在列表中通过使用它们的名称和布尔值来指定,如 new_with_braces 规则所示。 尽管您可以省略布尔值,但是为了保持一致性,我喜欢将其包括在内,因此数组中的每个项目都是 key ⇒ value 形式,因此我知道我明确选择了特定规则。

辅助方法

为了使您在整个项目中都能轻松使用共享规则,我们创建了一个辅助函数。 为什么会有这种想法呢?继续往下看你就会明白。

为确保该函数与项目中的任何其他全局函数或其依赖项不冲突,将函数放在命名空间中是一个好主意。 打开 helpers.php 文件并定义一个对您的上下文有意义的命名空间。

<?php

namespace TiMacDonald;

现在我们需要定义方法。 该方法接受 PhpCsFixer \ Finder 的实例以及一系列规则,这些规则将使项目可以逐个地识别任何其他强制性规则。 通常我们希望保持一致性,并且不允许任何项目更改共享规则集-但这不是本文讨论的话题。我们您这样做,如果您愿意也可以保留删除功能。

<?php

namespace TiMacDonald;

use PhpCsFixer\Config;
use PhpCsFixer\Finder; 

function styles(Finder $finder, array $rules = []): Config {
  //
}

很好,完成的差不多了。 现在的最后一件事是填写辅助函数的正文。

<?php

namespace TiMacDonald;

use PhpCsFixer\Config;
use PhpCsFixer\Finder; 

function styles(Finder $finder, array $rules = []): Config {
  $rules = array_merge(require __DIR__.'/rules.php', $rules);

  return Config::create()
    ->setFinder($finder)
    ->setRiskyAllowed(true)
    ->setRules($rules);
}

$finder 可以让 PHP-CS-Fixer 应该在哪里查找要修复的 PHP 文件。 我们将此 $finder 的定义放到单独项目中,因为每个项目都可能具有不同的目录结构,例如,Laravel 软件包和一个 Laravel 项目。

高风险规则

在 styles 函数的主体内,您可以看到我们通过调用 setRiskyAllowed(true) 告诉配置允许 risky 规则。 您必须阅读文档并了解高风险规则可能如何影响您的项目。 举例来说,我们来看看 implode_call 规则。 如果您阅读了 implode 相关的PHP文档,则会注意到:

由于历史原因,implode() 可以按任一顺序接受其参数。 为了与explode() 保持一致,不建议使用记录的参数顺序。

这以为这这两个 implode 语句会得到相同的结果:

<?php

implode($array, ',');

implode(',', $array);

implode_call 规则会重新排列参数的顺序,但是如果您的项目已经重新定义了 implode() 方法,则会通过某种方式期望参数以错误的顺序排列,例如 runkit, 更改顺序可能会破坏您的代码。

因此,在将其添加到规则集中之前,请仔细阅读每条有风险的规则的功能并了解其工作原理。

composer.json

如归需要安装新的扩展包, 我们需要修改 composer.json 文件。我建议在持续集成中运行代码风格检查,因此我在本地安装 PHP-CS-Fixer (如果扩展包有冲突的话,你也可以下载 PHP-CS-Fixer 的 Phar 文件).

打开 composer.json 文件并且确保设置一个唯一且有意的 name

{
  "name": "timacdonald/php-style-example",
  "description": "Tim's shared PHP style rules for PHP-CS-Fixer",
  "license": "MIT",
  "require": {
    "friendsofphp/php-cs-fixer": "^2.0"
  },
  "autoload": {
    "files": [
      "./src/helpers.php"
    ]
  }
}

将规则库托管

最后一步操作是将我们的本地规则仓库与远程仓库关联,然后将其推送到 GITHUB 托管。下面是操作命令:

$ git add .

$ git commit -m wip

$ git remote add origin git@github.com:timacdonald/php-style-example.git

$ git push -u origin master

至此,我们已经将我们的规则实现共享了!

使用共享规则库

现在我们只要做一些小的调整,就可以使用我们的共享规则库了。现在关闭共享规则库,然后在本地创建一个新的应用,初始化项目并执行 composer update

导入扩展包

Composer 允许我们直接通过 GIT 平台导入你需要的扩展包,而不用必须发布在 Packagist 上面。 考虑到会有这种只会在公司内部使用的扩展的情况,将它发布到 Packagist 意义不大。

为此,你需要将你的扩展添加到 "require-dev" 区块,这里,我并不需要锁定他的某个特定版本,所以我用 "dev-master" ,这样做意味着我的项目每次都会采用最新的版本的扩展。当然,这这个设置并不是必须的,你可以视你项目而定。

因为我们的扩展并未发布到 Packagist ,所以在 repositories 区块中,我们还需要告诉 Composer 去哪里寻找我的的扩展 repositories。具体请看代码:

"repositories": [
  {
    "type": "vcs",
    "url": "https://github.com/timacdonald/php-style-example"
  }
]

现在我们通过 composer 的 --dev 参数来加载依赖。

$ composer require timacdonald/php-style-example --dev

设置 PHP-CS-Fixer 查找器的配置

为了让 PHP-CS-Fixer 知道要定位的文件,您需要使用 PhpCsFixer\Finder的实例指定每个目录或文件。 这是 Symfony\Component\Finder\Finder 的继承版本,因此,有关要使用的所有约束的完整文档,请 查看文档

对于我们的示例,假定我们在 Laravel 应用程序中,设置查找程序来搜索需要遵守样式约定的目录。

PHP-CS-Fixer 的配置项位于 /.php_cs.dist 文件,因此我们创建一个:

$ touch .php_cs.dist

打开此文件,然后为您的 Laravel 应用添加以下查找程序设置。 您还可以包括要修复的其他任何文件夹,但是这些文件夹是合理的默认值。

<?php

$finder = PhpCsFixer\Finder::create()
  ->in([
    __DIR__.'/app',
    __DIR__.'/config',
    __DIR__.'/database',
    __DIR__.'/routes',
    __DIR__.'/tests',
  ]);

现在,我们准备将查找器传递给刚刚创建的辅助函数。 PHP-CS-Fixer 希望该文件返回 PhpCsFixer\Config 的实例,这正是我们的辅助函数返回的内容。

<?php

$finder = PhpCsFixer\Finder::create()
  ->in([
    __DIR__.'/app',
    __DIR__.'/config',
    __DIR__.'/database',
    __DIR__.'/routes',
    __DIR__.'/tests',
  ]);

return TiMacDonald\styles($finder);

现在,您将光标放在大括号位置,将自动修复所有这些目录中的编码样式! 跳至终端并运行以下命令以观看魔术的发生…

./vendor/bin/php-cs-fixer fix

CI 期间执行

在 C.I. 期间强制执行样式规则是一个好主意。 您可以通过以下两种方式执行此操作:您可以执行 “dry run”,如果它检测到代码样式违规,则将失败。

./vendor/bin/php-cs-fixer fix --dry-run

或者,您可能需要 C.I. 运行修复程序并将更改自动提交到您的代码库。 如果您正在使用 GitHub上 的动作,看看这篇很棒的文章 ,由 Stefan Zweifel 写的 关于如何实现上述操作的文章。

总结

感谢您的旅程。 PHP-CS-Fixer 是一个很棒的工具,如果您希望运行多个共享标准编码样式的项目,这种方法可能会派上用场。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://laravel-news.com/sharing-php-cs-...

译文地址:https://learnku.com/laravel/t/44009

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 4
<?php
$header = <<<EOF
What php team of samego is that is 'one thing, a team, work together'
EOF;

$finder = PhpCsFixer\Finder::create()
    ->files()
    ->name('*.php')
    ->exclude('vendor')
    ->in(__DIR__)
    ->ignoreDotFiles(true)
    ->ignoreVCS(true);
$rules = array(
    '@Symfony'                                   => true,
    'header_comment'                             => array('header' => $header),
    'array_syntax'                               => array('syntax' => 'short'),
    'ordered_imports'                            => true, // 按顺序use导入
    'no_useless_else'                            => true, // 删除没有使用的else节点
    'no_useless_return'                          => true, // 删除没有使用的return语句
    'self_accessor'                              => true, //在当前类中使用 self 代替类名
    'php_unit_construct'                         => true,
    'single_quote'                               => true, //简单字符串应该使用单引号代替双引号
    'no_unused_imports'                          => true, //删除没用到的use
    'no_singleline_whitespace_before_semicolons' => true, //禁止只有单行空格和分号的写法
    'no_empty_statement'                         => true, //多余的分号
    'no_whitespace_in_blank_line'                => true, //删除空行中的空格
    'standardize_not_equals'                     => true, //使用 <> 代替 !=
    'combine_consecutive_unsets'                 => true, // 当多个 unset 使用的时候,合并处理
    'concat_space'                               => ['spacing' => 'one'], // .拼接必须有空格分割
    'array_indentation'                          => true, // 数组的每个元素必须缩进一次
    'blank_line_before_statement'                => [
        'statements' => [
            'break',
            'continue',
            'declare',
            'return',
            'throw',
            'try'
        ]
    ],// 空行换行必须在任何已配置的语句之前
    'binary_operator_spaces'                     => [
        'default' => 'align_single_space'
    ], //等号对齐、数字箭头符号对齐
    'align_multiline_comment'                    => [
        'comment_type' => 'phpdocs_only'
    ],
    'lowercase_cast'                             => true,// 类型强制小写
    'lowercase_constants'                        => true,// 常量为小写
    'lowercase_static_reference'                 => true,// 静态调用为小写
    'no_blank_lines_after_class_opening'         => true,
    'phpdoc_separation'                          => false,// 不同注释部分按照单空行隔开
    'phpdoc_single_line_var_spacing'             => true,
    'phpdoc_indent'                              => true,
    'phpdoc_align'=>[
        'align'=>'vertical',
        'tags'=>[
            'param', 'throws', 'type', 'var', 'property'
        ]
    ]
);
return PhpCsFixer\Config::create()
    ->setRiskyAllowed(true)
    ->setRules($rules)
    ->setFinder($finder);
3年前 评论

谁给共享一个比较全面对 php_cs.dist 哈

3年前 评论
犯二青年 3年前
sunny123456 3年前
犯二青年 3年前
sunny123456 3年前
<?php

use PhpCsFixer\Config;
use PhpCsFixer\Finder;

$rules = [
    'list_syntax' => ['syntax' => 'short'],
    'array_syntax' => ['syntax' => 'short'],
    'binary_operator_spaces' => [
        'default' => 'single_space',
        'operators' => ['=>' => null]
    ],
    'blank_line_after_namespace' => true,
    'blank_line_after_opening_tag' => true,
    'blank_line_before_statement' => [
        'statements' => ['return']
    ],
    'braces' => true,
    'cast_spaces' => true,
    'class_attributes_separation' => [
        'elements' => ['method']
    ],
    'class_definition' => true,
    'concat_space' => [
        'spacing' => 'none'
    ],
    'declare_equal_normalize' => true,
    'elseif' => true,
    'encoding' => true,
    'full_opening_tag' => true,
    'fully_qualified_strict_types' => true, // added by Shift
    'function_declaration' => true,
    'function_typehint_space' => true,
    'heredoc_to_nowdoc' => true,
    'include' => true,
    'increment_style' => ['style' => 'post'],
    'indentation_type' => true,
    'linebreak_after_opening_tag' => true,
    'line_ending' => true,
    'lowercase_cast' => true,
    'lowercase_constants' => true,
    'lowercase_keywords' => true,
    'lowercase_static_reference' => true, // added from Symfony
    'magic_method_casing' => true, // added from Symfony
    'magic_constant_casing' => true,
    'method_argument_space' => true,
    'native_function_casing' => true,
    'no_alias_functions' => true,
    'no_extra_blank_lines' => [
        'tokens' => [
            'extra',
            'throw',
            'use',
            'use_trait',
        ]
    ],
    'no_blank_lines_after_class_opening' => true,
    'no_blank_lines_after_phpdoc' => true,
    'no_closing_tag' => true,
    'no_empty_phpdoc' => true,
    'no_empty_statement' => true,
    'no_leading_import_slash' => true,
    'no_leading_namespace_whitespace' => true,
    'no_mixed_echo_print' => [
        'use' => 'echo'
    ],
    'no_multiline_whitespace_around_double_arrow' => true,
    'multiline_whitespace_before_semicolons' => [
        'strategy' => 'no_multi_line'
    ],
    'no_short_bool_cast' => true,
    'no_singleline_whitespace_before_semicolons' => true,
    'no_spaces_after_function_name' => true,
    'no_spaces_around_offset' => true,
    'no_spaces_inside_parenthesis' => true,
    'no_trailing_comma_in_list_call' => true,
    'no_trailing_comma_in_singleline_array' => true,
    'no_trailing_whitespace' => true,
    'no_trailing_whitespace_in_comment' => true,
    'no_unneeded_control_parentheses' => true,
    'no_unreachable_default_argument_value' => true,
    'no_useless_return' => true,
    'no_whitespace_before_comma_in_array' => true,
    'no_whitespace_in_blank_line' => true,
    'normalize_index_brace' => true,
    'not_operator_with_successor_space' => true,
    'object_operator_without_whitespace' => true,
    'ordered_imports' => ['sortAlgorithm' => 'alpha'],
    'phpdoc_indent' => true,
    'phpdoc_inline_tag' => true,
    'phpdoc_no_access' => true,
    'phpdoc_no_package' => true,
    'phpdoc_no_useless_inheritdoc' => true,
    'phpdoc_scalar' => true,
    'phpdoc_single_line_var_spacing' => true,
    'phpdoc_summary' => true,
    'phpdoc_to_comment' => true,
    'phpdoc_trim' => true,
    'phpdoc_types' => true,
    'phpdoc_var_without_name' => true,
    'psr4' => true,
    'self_accessor' => true,
    'short_scalar_cast' => true,
    'simplified_null_return' => true,
    'single_blank_line_at_eof' => true,
    'single_blank_line_before_namespace' => true,
    'single_class_element_per_statement' => true,
    'single_import_per_statement' => true,
    'single_line_after_imports' => true,
    'single_line_comment_style' => [
        'comment_types' => ['hash']
    ],
    'single_quote' => true,
    'space_after_semicolon' => true,
    'standardize_not_equals' => true,
    'switch_case_semicolon_to_colon' => true,
    'switch_case_space' => true,
    'ternary_operator_spaces' => true,
    'trailing_comma_in_multiline_array' => true,
    'trim_array_spaces' => true,
    'unary_operator_spaces' => true,
    'visibility_required' => [
        'elements' => ['method', 'property']
    ],
    'whitespace_after_comma_in_array' => true,
];

$finder = Finder::create()
    ->notPath('bootstrap/cache')
    ->notPath('storage')
    ->notPath('node_modules')
    ->notPath('vendor')
    ->in(getcwd())
    ->name('*.php')
    ->notName('*.blade.php')
    ->notName('index.php')
    ->notName('server.php')
    ->notName('_ide_helper.php')
    ->ignoreDotFiles(true)
    ->ignoreVCS(true);

return Config::create()
    ->setFinder($finder)
    ->setRules($rules)
    ->setRiskyAllowed(true)
    ->setUsingCache(true);
3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!