🚥 如何用 PHP 编写简洁的代码 🐘

2025-05-24

🚥 如何用 PHP 编写简洁的代码 🐘

这是关于代码质量的两篇文章的第一部分。
这篇文章主要讨论PHP,不过等等,别急!如果你用一些“严肃的语言”写代码,你可能还是会感兴趣。


好代码 vs 坏代码

我们为什么要关心代码质量?

为什么不直接让我们的代码按预期工作呢?实际上,这取决于你的应用的用途和目标。如果你开发的是一个一次性应用,用于演示,或者只是供你使用,你显然不会花时间在质量上。但如果是更严肃的事情,质量就成了强制性的要求。

对于代码的可读性、可维护性、以及您和队友的健康来说,这都是必需的。

对于最没有耐心的人,我在 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"
Enter fullscreen mode Exit fullscreen mode

要在项目之外使用它,您应该全局安装它并添加vendor/bin到您的PATH

composer global require "squizlabs/php_codesniffer"
export PATH=$PATH:~/path/to/composer/vendor/bin
Enter fullscreen mode Exit fullscreen mode

🆘 phpcs 使用方法

现在,考虑以下包含一些违规行为的 PHP 示例...

<?php
class MaClasse{
    var $monAttribut;

    public function MaClasse ($monAttribut){
        $this->setMonAttribut( $monAttribut );
    }

    function setMonAttribut($monAttribut){
        $this->monAttribut=$monAttribut;
    }
}
Enter fullscreen mode Exit fullscreen mode

...看看如果我们使用符合 PSR2 标准的 phpcs 会发生什么:

phpcs psr2

该工具检测到 12 个错误,其中 6 个可以自动修复。

让我们看看如果我们使用 Squiz 标准重新运行该工具会发生什么:

phpcs squiz

该工具检测到 28 个错误,其中 18 个可以自动修复。我认为 Squiz 标准有点过于冗长。因此,我创建了自己的规则集,包含了所有 PSR2 规则,并添加了一些我认为合理的 Squiz 规则。

为了进一步了解,您可以查看phpcs_psr2phpcs_squiz规则集。

PHP 代码美化器和修复器(又名 phpcbf)

嗯,能够检测到违规行为固然很好,但如果能够自动纠正就更好了,不是吗?幸运的是,PHP_CodeSniffer附带了另一个可以修复主要 PSR1 和 PSR2 违规的实用程序:PHP 代码美化修复器(又名 phpcbf)。只需在 PHP 文件或代码库上运行它,它就会扫描所有文件并尝试自动修复违规行为:

phpcbf psr2

当与 PSR2 标准一起使用时MaClasse.php,它修复了 12 个违规行为中的 6 个:

<?php

class MaClasse
{
    var $monAttribut;

    public function MaClasse($monAttribut)
    {
        $this->setMonAttribut($monAttribut);
    }

    function setMonAttribut($monAttribut)
    {
        $this->monAttribut=$monAttribut;
    }
}
Enter fullscreen mode Exit fullscreen mode

现在轮到你尝试一下,并创建符合你、你的团队成员以及你的项目需求的规则集。当然,这些规则集必须与其他开发者共享,以确保代码库的一致性,并能够协同工作。

PHP 混乱检测器(又名 phpmd)

与 Java 及其PMD类似,PHP 也拥有自己的混乱检测工具。通过规则集机制(例如 PHP_CodeSniffer),该工具可以检测到许多违反整洁代码理念的行为:

  • 可能的错误
  • 次优代码
  • 过于复杂的表达
  • 未使用的参数、方法、属性
  • ...

💡 phpmd 安装

composer require phpmd/phpmd
Enter fullscreen mode Exit fullscreen mode

然后,您需要向 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

现在,使用 phpmd 和所有可用的规则集进行检查:

phpmd MaClasse.php text cleancode,codesize,controversial,design,naming,unusedcode
Enter fullscreen mode Exit fullscreen mode

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'.
Enter fullscreen mode Exit fullscreen mode

如您所见,该工具与 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>

Enter fullscreen mode Exit fullscreen mode

PHP 编码标准修复器(又名 php-cs-fixer)

如果我们最终选择另一种工具会怎么样?如果只能选择一个工具,那就是这个。

它的功能与phpcbf几乎相同,即修复 PSR 规则,但做得更好、更深入。与 phpcbf 一样,它也允许您从172 条规则列表中添加其他规则。它的另一个亮点是创建的规则集文件是一个 PHP 文件,而不是冗长的 XML 文件。

💡 php-cs-fixer 安装

composer require friendsofphp/php-cs-fixer
Enter fullscreen mode Exit fullscreen mode

🆘 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
Enter fullscreen mode Exit fullscreen mode

以下是具有自定义规则集的 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;
    }
}
Enter fullscreen mode Exit fullscreen mode

这是我的自定义规则集(.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)
;
Enter fullscreen mode Exit fullscreen mode

其中一些规则被称为“有风险”,因为它们可能会影响 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,它将引发错误,而不是将 强制转换stringint(例如“7 次”=> 7)。


最后,您必须将其视为应用程序不可或缺的一部分,也就是说,一方面,它们列在项目的依赖项中,另一方面,它们的配置文件和规则集也是代码库的一部分:

php_repo

这不仅适用于 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
PREV
停止根据用户位置设置网站语言 原因 解决方案
NEXT
DEV 上的精彩 PHP 资源 🚀 🎉 🎢 🏗 引导✅ 简洁代码🚥 质量☑️ 测试🔒 安全🛠 工具🚀 其他🌠 值得关注的人