在 PHP 中收集指标

在本节中,我们将介绍 PHP 世界中用于收集代码质量指标的工具。正如你即将看到的,这些指标不仅仅是数字,它们还能让你对重构代码所需的工作量做出有根据的猜测。它们还能帮助你确定代码中最需要关注的部分。

我们再次为你精选了一些工具:

  • phploc

  • PHP Depend

  • PhpMetrics

phploc

正如我们在上一节中了解到的,缩写 LOC 代表代码行数,因此这个名称已经揭示了这个工具的主要用途。作为一个基本指标,它已经告诉了我们关于代码库的很多信息。phploc 还提供了更多指标,如 CC,因此值得仔细研究一下。

安装和使用

该工具的作者 Sebastian Bergmann 因 phpunit 而闻名,phpunit 是 PHP 世界自动测试的事实标准。他建议不要使用 Composer 安装,而是直接使用 phar。我们将在下一章讨论这种方法的利弊。现在,让我们听从作者的建议,直接下载 phar

$ wget https://phar.phpunit.de/phploc.phar

这将把最新版本的 phploc 下载到当前目录。下载完成后,我们就可以直接使用它来扫描项目了:

$ php phploc.phar src
扫描单个文件

虽然 phploc 适用于整个项目,但也可以指定扫描单个文件。虽然平均测量值没有意义,因为它们是针对整个项目使用的,但如果需要找出 LOC 指标或某个班级的 CC,它仍然很有用。

前面的命令将扫描 src 文件夹及其所有子文件夹并收集相关信息,这些信息将直接显示在命令行中,如图 8.1 所示:

image 2023 11 12 11 23 06 564
Figure 1. Figure 8.1: An example output of phploc (an excerpt)

这比 LOC 的信息要多得多。这些信息分为以下几类:

  • 规模:显然,该工具存在的主要原因是通过计算代码行数来衡量项目的规模,我们在上一节介绍了几种计算方法。重点在于 LLOC,你将得到每个类、类方法和函数的平均值。

  • CC:phploc 将计算每个 LLOC、类和方法的平均 CC 值。

  • 依赖关系:这部分将告诉你有多少次对全局状态的访问,以及有多少次对属性和方法的静态访问。全局访问和静态访问都被认为是应避免的做法,因此这些数字能为你提供有关代码质量的更多提示。

  • 结构:在最后一个输出部分(前面的截图中没有),phploc 会返回代码结构的更多细节。如何解释它们并没有明确的规则;不过,你可以从中得出一些结论。例如,请看下面的内容:

    • 关于整体代码大小,使用了多少命名空间?如果代码量很大,但命名空间却很少,说明项目结构不完善。

    • 是否使用了接口,与项目规模相比使用了多少接口?接口的使用增加了类之间的互换性,表明代码结构良好。

以上就是我们目前需要了解的 phploc 功能。它是一个简单易用但很有帮助的工具,可以帮助你快速掌握项目的整体代码质量和结构,因此应该成为你工具包的一部分。不过,它不会告诉你如何解释这些数字,这需要一些经验。

PHP Depend

如果要评选在一个工具中集成最多指标的奖项,那么 PHP Depend (PDepend) 肯定会榜上有名。它涵盖了我们在上一节中讨论过的所有指标,还有更多。不过,它并不是最方便用户使用的工具。另外,网站和资源库文档也不完善。尽管如此,你还是应该看看它。

安装和使用

和以前一样,可以使用 Composer 或直接下载 phar 安装该工具。我们将暂时采用基于 Composer 的安装方式:

$ composer require pdepend/pdepend --dev

如果没有不愉快的意外,就可以直接执行:

$ vendor/bin/pdepend --summary-xml=pdepend_summary.xml src

在这里,我们已经可以看出 PDepend 的祖先是 Java 代码质量工具 JDepend,因为输出结果被写入了一个 XML 文件。文件名使用 --summary-xml 选项指定。此外,我们还必须指定要扫描的文件夹作为参数。

不过,PDepend 确实会输出一些数字,下面的输出示例就说明了这一点:

PDepend 2.10.3
Parsing source files:
............................................... 47

Calculating Cyclomatic Complexity metrics:
................. 355

Calculating Node Loc metrics:
............. 279

Calculating NPath Complexity metrics:
................. 355

Calculating Inheritance metrics:
..... 101

这里我们跳过了一些行。这些数字只会告诉你每个指标在给定文件夹中的计算频率,因此直接输出并没有什么特别的帮助。要查看实际指标,我们需要打开 XML 报告。在我们的案例中,生成的文件名为 pdepend_summary.xml

XML 报告非常庞大,无法在本书中打印出来,所以你最好自己尝试一下,看看它的全貌。不过,我们可以向你展示它的结构:

<?xml version="1.0" encoding="UTF-8"?>
<metrics>
    <files>
        <file name="/path/to/Namespace/Classname.php"/>
        <!-- ... -->
    </files>
    <package name="Namespace">
        <class name="Classname" fqname="Namespace\Classname">
            <file name="/path/to/Namespace/Classname.php"/>
            <method name="methodName"/>
            <!-- ... -->
        </class>
        <!-- ... -->
    </package>
</metrics>

<metrics> 节点表示完整扫描过的目录。它有以下子节点:

  • <files>,列出使用 <file> 子节点扫描的所有文件。

  • <package>,列出所有命名空间。在这个节点中,还有 <class> 子节点。每个类都有一个 <method> 节点列表,类中的每个方法都有一个节点。最后,在另一个 <file> 节点中提到了类的文件名。

当然,这并不是 PDepend 生成的全部输出。对于每个节点,它都会添加数十个属性,其中包含计算指标的名称和值。这是根据 PDepend 源代码生成的 XML 报告中的一个节点示例:

<method name="setConfigurationFile" start="80" end="89"
ccn="2" ccn2="2" loc="10" cloc="0" eloc="8" lloc="3"
ncloc="10" npath="2" hnt="15" hnd="21"
hv="65.884761341681" hd="7.3125" hl="0.13675213675214"
he="481.78231731105" ht="26.765684295058"
hb="0.020485472371812" hi="9.0098818928795"
mi="67.295865328327"/>

你应该已经能够识别一些指标,如 lloc(LOC)或 ccn(CC 编号)。至于其它指标,你可以在在线文档 https://pdepend.org/documentation/softwaremetrics/index.html 中找到 XML 报告中缩写的解释或至少是长名称。

更多选项

PDepend 有两个你应该了解的选项:

  • --exclude:这将从扫描中排除一个命名空间(在此术语中为软件包)。可以使用多个命名空间,以逗号分隔。确保在命名空间周围加上引号:

    $ vendor/bin/pdepend --summary-xml=pdepend_summary.xml --exclude="Your\Namespace,Another\Namespace" src
  • --ignore:允许你忽略一个或多个文件夹。再次强调,不要忘记引号:

    $ vendor/bin/pdepend --summary-xml=pdepend_summary.xml --ignore="path/to/folder,path/to/other/folder" src

它还可以生成 SVG 格式的图像,并提供更多信息。不过我们不会在本书中介绍这些内容,因为有一个更好的工具可以实现这一功能,你将在下一节中找到它。

PDepend 功能强大,但同时也很难监督。生成的输出很难阅读,一旦项目稍大,就无法使用,除非使用其它工具解析 XML 文件。不过,也许有一天你会需要它提供的高级度量指标,或者你可能在某个项目中已经使用了它。所以,至少你现在已经做好了准备。

PHP指标

到目前为止,PHP 质量指标的世界还只是基于文本的。现在,这种情况将有所改变,因为我们现在将看到 PhpMetrics,它生成的报告更适合人眼,甚至可以交互。

安装和使用

让我们使用 Composer 将 PhpMetrics 添加到你的项目中:

$ composer require phpmetrics/phpmetrics --dev

下载完所有文件后,你就可以立即开始生成第一份报告了:

$ vendor/bin/phpmetrics --report-html=phpmetrics_report src

--report-html 选项指定了创建报告的文件夹。你可以用逗号分隔的列表指定多个要扫描的文件夹。但在我们的示例中,我们将只使用 src 文件夹。

因此,PhpMetrics 会列出一些统计数据,让你对代码有一些了解。图 8.2 显示了输出结果的摘录,这可能会让你想起 phploc 生成的输出结果:

image 2023 11 12 11 47 11 989
Figure 2. Figure 8.2: The PhpMetrics console output (an excerpt)

要打开刚刚生成的 HTML 报告,只需在浏览器中打开该文件夹中的 index.html 文件即可。在仔细查看生成的报告之前,让我们先看看 PhpMetrics 还提供了哪些有用的选项:

  • --metrics:该选项将返回可用度量的列表。它有助于破译缩写,如 mIwoC。

  • --exclude:使用该选项,你可以指定一个或多个目录不被扫描。

  • --report-[csv|json|summary-json|violations]:允许以 HTML 以外的不同报告格式保存结果,例如:--report-json

从命令行打开浏览器

如果你使用的是基于 Linux 的操作系统,例如 Ubuntu,你可以从命令行快速打开 HTML 文件,如下所示:

$ firefox phpmetrics_report/index.html

或者,请参阅以下内容:

$ chromium phpmetrics_report/index.html

理解报告

如果你是第一次打开 PhpMetrics 报告,你会发现里面有各种各样的信息。我们不会深入研究每一个小细节,但会向你展示我们认为报告中最有价值的部分。

为了更好地说明 PhpMetrics 的用法,我们随机选择了一个名为 thephpleague/container 的现有开源软件包作为代码库。它是一个优秀的符合 PSR-11 标准的依赖注入容器,大小正好适合作为示例。图 8.3 显示了我们为其生成的示例报告的概览页面:

image 2023 11 12 11 54 32 293
Figure 3. Figure 8.3: A PhpMetrics report overview

关键指标

左侧是菜单,可以进入报告的其它页面。页面上部有几个关键指标,其中最有趣的是:

  • 通过代码行数可以进一步了解项目的规模。点击标签,你将进入另一个页面,那里有所有类的详细列表及其相关的大小指标,如 LOC。

  • 违规显示了 PhpMetrics 发现的违规数量。同样,点击标签,你将进入另一个页面,那里有一个类及其违规行为的列表—​例如,如果它们太复杂(方法代码太复杂),有很高的错误概率(可能有错误),或者使用了太多其它类或其它依赖(太依赖)。

  • 按类划分的平均循环复杂度会告诉你它的真正含义。详细视图可为你提供更多有关类级别复杂性的信息。

其它方框也提供了有趣的信息,但前面的方框已经非常适合快速查看代码中最有问题的部分。

可维护性或复杂性

在关键指标下方,PhpMetrics 还显示了一个图表,你在第一次打开报告时肯定已经发现了:可维护性/复杂性 图表。它由项目中每个命名空间的彩色圆圈组成,圆圈的大小代表类的 CC。圆圈越大,复杂度越高。颜色表示可维护性指数,从绿色(高)到红色(低)不等。

如果将鼠标悬停在圆圈上,就可以看到圆圈所代表的命名空间以及这两个指标的详细信息:

image 2023 11 12 11 59 00 042
Figure 4. 图 8.4:带有弹出窗口的可维护性/复杂性图

这张图对于快速掌握整体代码质量非常有用—​大红圈越少越好。这样,你就能轻松看到代码中存在问题的部分。

对象关系

从左侧菜单中选择 "对象关系" 时,会出现一个显示每个命名空间之间关系的图表。将鼠标指针悬停在文本标签上会突出显示其关系。该图表非常庞大,因此我们无法在本书中向你展示它的全貌,但至少可以给你一个初步印象:

image 2023 11 12 12 17 10 037
Figure 5. Figure 8.5: An Object relations graph

耦合

类的耦合说明了它们如何相互依赖。有两个主要指标:

  • 传入耦合(Ca)告诉你依赖于该类的类的数量。过多的依赖关系表明类对项目的重要性。

  • 传出耦合(Ce)可让你了解一个类使用了多少个依赖项。该值越高,说明该类对其它类的依赖性越强。

面向包的指标

我们要向大家展示的最后一张图表是 "抽象性与不稳定性关系图"。顾名思义,它显示了软件包的抽象性与不稳定性之间的关系。它是由罗伯特-马丁(Robert Martin)根据他在面向对象度量方面的研究成果提出的。图 8.6 展示了一个示例:

image 2023 11 12 12 31 05 304
Figure 6. Figure 8.6: An Abstractness vs. Instability graph

但在软件开发中,这两个术语究竟是什么意思呢?让我们看看以下定义:

  • 抽象性 (A) 是指抽象基类和接口与命名空间或软件包中所有类的数量之比。包中包含的抽象类型越多,变更就越容易,风险也越小。A 的范围从 0(具体)到 1(抽象)。

  • 不稳定性 (I) 通过 Ce 与总 Ce 和 Ca 的比值(Ce + Ca)来表示一个软件包在变化面前的脆弱程度。换句话说,依赖关系越多,稳定性越差。I 的范围从 0(稳定)到 1(不稳定)。

马丁指出,稳定的、因此高度独立于其它类的软件包也应该具有较高的 A 值,反之亦然,不稳定的软件包应该由具体的类组成。因此,从理论上讲,一个类的 A 与它的 I 相加,即理想情况下,A 加 I 应为 1(A + I = 1)。这个等式也画出了从图表左上角到右下角的斜线。你应该努力使你的数据包靠近这条线。

在实际报告中,你会发现图表下方有一个表格,你可以在其中找到更详细的值。如果将鼠标指针悬停在圆圈上,会弹出一个窗口,告诉你圆圈代表的类的名称,以及 A(第一位数字)和 I(第二位数字)。

其它信息

我们的 PhpMetrics 之旅到此结束。我们还可以发现更多内容,例如 ClassRank,它采用了 Google 著名的 PageRank 算法,根据类的重要性(即类与其它代码部分的交互次数)对类进行排序。我们无法在本书中涵盖所有内容—​不过,到现在为止,你已经知道了许多指标。它的文档对你很有帮助。你可以在每一页的右上角找到指向它的链接。