🚥 如何用 PHP 编写简洁的代码 🐘
这是关于代码质量的两篇文章的第一部分。
这篇文章主要讨论PHP,不过等等,别急!如果你用一些“严肃的语言”写代码,你可能还是会感兴趣。
我们为什么要关心代码质量?
为什么不直接让我们的代码按预期工作呢?实际上,这取决于你的应用的用途和目标。如果你开发的是一个一次性应用,用于演示,或者只是供你使用,你显然不会花时间在质量上。但如果是更严肃的事情,质量就成了强制性的要求。
对于代码的可读性、可维护性、以及您和队友的健康来说,这都是必需的。
对于最没有耐心的人,我在 gitlab.com 上创建了一个模板项目,以便您可以使用现成的质量工具快速引导 PHP 项目。
哦,等等!你指的是质量?真的吗?用 PHP 吗?
PHP 的名声不太好,可能是因为它是一种非常宽松的语言,被缺乏经验的“开发人员”使用。它的动态类型机制经常被误用,而且在 PHP 5 和 OOP 范式引入之前,它非常混乱。除此之外,大多数用 PHP 开发的应用程序只是网站、博客、论坛或其他 CMS,例如 Drupal、WordPress、Joomla、Prestashop……
如今,PHP 生态系统已发生翻天覆地的变化。它的依赖管理器Composer已被大多数项目维护者广泛采用,并帮助 PHP 成为严肃后端应用程序的可靠选择。
此外,PHP7 通过增强操作码缓存和 Zend 引擎,为 PHP 的性能带来了突破性的变化。现在,可以定义函数参数和返回值的类型。结合严格类型模式,许多隐藏的运行时错误现在可以在 IDE 中检测到。
我不会在这里介绍代码质量的所有方面(测试、审查、TDD 等),但我会重点关注最基本的内容:编码标准/风格和静态分析。
当然,工具本身并不能决定任何软件的质量,但它们确实起到了一定的作用。我们可以说,工具让我们有动力不断改进代码。但这并不妨碍我们进行单元测试和代码审查!
✔️代码静态分析工具
PHP -FIG联盟发布了一系列建议,旨在标准化 PHP 的使用并促进其互操作性。其中三项建议侧重于编码风格以及诸如自动加载、编码等内容:
下面列出的工具被 PHP 社区广泛采用,并得到积极的支持/维护。
PHP_CodeSniffer(又名 phpcs)
幸运的是,我们不需要了解 PSR 规范的所有细节(即使了解了会更好),因为有些工具可以帮我们完成(部分)工作。我的意思是,美化打印代码,使其符合 PSR 规范并检测违规行为。随着时间的推移和经验的积累,这些对你来说会变得轻而易举。
PHP_CodeSniffer可以检测 PSR1/PSR2 违规行为,并且能够通过 Squiz 标准进行更多功能检测,Squiz 标准可检查 130 条规则(PSR1 规则 7 条,PSR2 规则 42 条),并且兼容 CSS 和 JS。因此,一个折衷方案是先从 PSR2 标准入手,然后基于 PSR2 创建自己的标准(又称规则集),再使用所需的 Squiz 规则进行扩展。
💡 phpcs 安装
这将添加 phpcs 作为项目依赖项:
composer require "squizlabs/php_codesniffer"
要在项目之外使用它,您应该全局安装它并添加vendor/bin
到您的PATH
:
composer global require "squizlabs/php_codesniffer"
export PATH=$PATH:~/path/to/composer/vendor/bin
🆘 phpcs 使用方法
现在,考虑以下包含一些违规行为的 PHP 示例...
<?php
class MaClasse{
var $monAttribut;
public function MaClasse ($monAttribut){
$this->setMonAttribut( $monAttribut );
}
function setMonAttribut($monAttribut){
$this->monAttribut=$monAttribut;
}
}
...看看如果我们使用符合 PSR2 标准的 phpcs 会发生什么:
该工具检测到 12 个错误,其中 6 个可以自动修复。
让我们看看如果我们使用 Squiz 标准重新运行该工具会发生什么:
该工具检测到 28 个错误,其中 18 个可以自动修复。我认为 Squiz 标准有点过于冗长。因此,我创建了自己的规则集,包含了所有 PSR2 规则,并添加了一些我认为合理的 Squiz 规则。
为了进一步了解,您可以查看phpcs_psr2和phpcs_squiz规则集。
PHP 代码美化器和修复器(又名 phpcbf)
嗯,能够检测到违规行为固然很好,但如果能够自动纠正就更好了,不是吗?幸运的是,PHP_CodeSniffer附带了另一个可以修复主要 PSR1 和 PSR2 违规的实用程序:PHP 代码美化修复器(又名 phpcbf)。只需在 PHP 文件或代码库上运行它,它就会扫描所有文件并尝试自动修复违规行为:
当与 PSR2 标准一起使用时MaClasse.php
,它修复了 12 个违规行为中的 6 个:
<?php
class MaClasse
{
var $monAttribut;
public function MaClasse($monAttribut)
{
$this->setMonAttribut($monAttribut);
}
function setMonAttribut($monAttribut)
{
$this->monAttribut=$monAttribut;
}
}
现在轮到你尝试一下,并创建符合你、你的团队成员以及你的项目需求的规则集。当然,这些规则集必须与其他开发者共享,以确保代码库的一致性,并能够协同工作。
PHP 混乱检测器(又名 phpmd)
与 Java 及其PMD类似,PHP 也拥有自己的混乱检测工具。通过规则集机制(例如 PHP_CodeSniffer),该工具可以检测到许多违反整洁代码理念的行为:
- 可能的错误
- 次优代码
- 过于复杂的表达
- 未使用的参数、方法、属性
- ...
💡 phpmd 安装
composer require phpmd/phpmd
然后,您需要向 phpmd 提供一个文件或目录,并指定输出格式(文本、XML 或 HTML)和一个或多个规则集。
🆘 phpmd 用法
让我们以之前修复的 phpcbf/PSR2 示例为例,并在其中添加一个未使用的变量:
<?php
class MaClasse
{
var $monAttribut;
public function MaClasse($monAttribut)
{
$this->setMonAttribut($monAttribut);
}
function setMonAttribut($monAttribut)
{
$test;
$this->monAttribut=$monAttribut;
}
}
现在,使用 phpmd 和所有可用的规则集进行检查:
phpmd MaClasse.php text cleancode,codesize,controversial,design,naming,unusedcode
phpmd 的输出:
MaClasse.php:6 The method MaClasse is not named in camelCase.
MaClasse.php:6 Classes should not have a constructor method with the same name as the class
MaClasse.php:12 Avoid unused local variables such as '$test'.
如您所见,该工具与 PHP_CodeSniffer 相辅相成,因为它发现了三个之前未检测到的有趣违规行为。当然,如果您在整个代码库中运行它,它也会发现许多或多或少有用的违规行为,这些违规行为可能会造成干扰,并阻止您查看有意义的信息。因此,正如我之前关于 PHP_CodeSniffer 所说,我建议您基于现有规则集创建自己的规则集。您需要在项目根目录下创建一个名为 的文件phpmd.xml.dist
,然后您就可以在终端、编辑器/IDE 和管道中使用它。
请在此处找到使用所有现有规则的规则集。
但是就像 phpcs 一样,我建议您从最小规则集开始迭代进行unusedcode
,并逐步包含对您和您的项目有意义的新规则。
请参阅下面可用于启动 phpmd 的自定义规则集:
<?xml version="1.0"?>
<ruleset name="Custom rule set used in pre-commit git hook"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>
Custom rule set used in pre-commit git hooks
</description>
<!-- Import the entire unused code rule set -->
<rule ref="rulesets/unusedcode.xml" />
<!-- Import a part of design code rule set -->
<rule ref="rulesets/design.xml">
<exclude name="NumberOfChildren" />
<exclude name="DepthOfInheritance" />
<exclude name="CouplingBetweenObjects" />
</rule>
</ruleset>
PHP 编码标准修复器(又名 php-cs-fixer)
如果我们最终选择另一种工具会怎么样?如果只能选择一个工具,那就是这个。
它的功能与phpcbf几乎相同,即修复 PSR 规则,但做得更好、更深入。与 phpcbf 一样,它也允许您从172 条规则列表中添加其他规则。它的另一个亮点是创建的规则集文件是一个 PHP 文件,而不是冗长的 XML 文件。
💡 php-cs-fixer 安装
composer require friendsofphp/php-cs-fixer
🆘 php-cs-fixer 用法
php-cs-fixer --verbose fix MaClasse.php
Loaded config default from "~/.php_cs.dist".
Using cache file ".php_cs.cache".
Paths from configuration file have been overridden by paths provided as command arguments.
F
Legend: ?-unknown, I-invalid file syntax, file ignored, S-Skipped, .-no changes, F-fixed, E-error
1) MaClasse.php (class_attributes_separation, no_spaces_inside_parenthesis, declare_strict_types, blank_line_after_opening_tag, array_syntax, class_definition,
function_declaration, visibility_required, declare_equal_normalize, binary_operator_spaces, braces)
Checked all files in 0.008 seconds, 8.000 MB memory used
以下是具有自定义规则集的 MaClasse.php 的输出:
declare(strict_types = 1);
class MaClasse
{
public $monAttribut = [];
public function __construct($monAttribut)
{
$this->setMonAttribut($monAttribut);
}
public function setMonAttribut($monAttribut)
{
$this->monAttribut = $monAttribut;
$test = 1;
}
}
这是我的自定义规则集(.php_cs.dist):
$finder = PhpCsFixer\Finder::create()
->exclude('vendor')
->exclude('output')
->exclude('releases')
->in(__DIR__)
;
return PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules([
'@PSR2' => true,
'declare_strict_types' => true,
'align_multiline_comment' => ['comment_type' => 'phpdocs_only'],
'binary_operator_spaces' => true,
'blank_line_after_opening_tag' => true,
'cast_spaces' => true,
'class_attributes_separation' => true,
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => ['space' => 'single'],
'function_typehint_space' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_extra_consecutive_blank_lines' => true,
'no_mixed_echo_print' => true,
'no_php4_constructor' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_whitespace_before_comma_in_array' => true,
'no_whitespace_in_blank_line' => true,
'object_operator_without_whitespace' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'phpdoc_align' => true,
'phpdoc_indent' => true,
'yoda_style' => true,
'protected_to_private' => true,
'return_type_declaration' => true,
'self_accessor' => true,
'single_blank_line_before_namespace' => true,
'single_quote' => true,
'ternary_operator_spaces' => true,
'ternary_to_null_coalescing' => true,
'whitespace_after_comma_in_array' => true,
'array_syntax' => ['syntax' => 'short'],
])
->setFinder($finder)
;
其中一些规则被称为“有风险”,因为它们可能会影响 PHP 的运行时,所以请谨慎使用!在我的项目中,我使用了其中两个:
declare_strict_types
(类型模式设置为strict
而不是weak
)no_php4_constructor
(构造函数必须被命名__construct
)
您可以使用 setter 禁用此功能setRiskyAllowed(false)
。
该declare_strict_types
规则特别有趣,因为它在每个 PHP 文件的标头中添加了以下行:declare(strict_types = 1);
此 PHP7 语句允许严格指定标量类型(int、array、string、bool 等)的函数参数和返回值类型。这意味着,如果您为函数传入了string
而不是预期的int
,它将引发错误,而不是将 强制转换string
为int
(例如“7 次”=> 7)。
最后,您必须将其视为应用程序不可或缺的一部分,也就是说,一方面,它们列在项目的依赖项中,另一方面,它们的配置文件和规则集也是代码库的一部分:
这不仅适用于 PHP,也适用于任何类型的语言。这是确保团队内部代码库长期保持一致的唯一方法。
🛠️ 更进一步
- phan:强大的代码静态分析器
- phpstan:PHP 静态分析工具
- PHP metrics:另一个 PHP 静态分析器,可输出一些用户友好的报告
- PHP 复制/粘贴检测器(又名 phpcpd):重复代码检测器
- SonarPHP:SonarQube 和 SonarLint 的静态 PHP 代码分析器
- phploc:一款提供 PHP 项目统计信息的工具
🌟 请随时问我有关这个话题的任何问题!
⏭️在下一部分中,我们将讨论如何在项目生命周期中集成这些工具(git、编辑器/IDE、CI)。
有用的链接:
文章来源:https://dev.to/biros/how-to-master-your-code-through-your-project-lifecycle-12-4ke3