glhd/laralint

Laravel 代码格式化框架

0.0.17 2024-07-30 20:33 UTC

This package is auto-updated.

Last update: 2024-08-30 20:45:14 UTC


README

GitHub Workflow Status Test Coverage

这是一个 非常早期 的 Laravel 项目代码格式化工具。它与其他 PHP 格式化工具的不同之处在于,它专注于构建符合您特定需求的自定义规则。

目标是让 编写自定义格式化工具 变得简单流畅,就像编写 Laravel 后端代码一样。虽然该项目在底层使用了 AST/tokenizers 等技术,但对于大多数常见场景,您不需要深入了解其工作原理。

入门指南

使用以下命令将此工具安装到您的项目中:

composer require glhd/laralint --dev

安装完成后,您可以通过运行以下命令来使用 LaraLint 的默认规则对项目进行代码格式化:

php artisan laralint:lint

如果您只想格式化未提交的 Git 修改,可以将 --diff 参数传递给命令

php artisan laralint:lint --diff

或者,如果您只想格式化特定文件,可以将文件名作为第一个参数传递

php artisan laralint:lint app/Http/Controllers/HomeController.php

配置和预设

LaraLint 随带了一个 非常明确的 预设。如果您觉得这个预设适用,那很好,但您可能需要对其进行自定义,以符合您团队的代码标准。

首先,发布 LaraLint 配置文件:

php artisan vendor:publish --tag=laralint-config

这将安装一个 laralint.php 文件到您的项目 config/ 目录中,并包含默认的 LaraLint 配置。

在某些情况下,您可能只需调整配置即可满足需求。但更有可能的是,您需要创建自己的预设。

请查看 Preset 合同或默认的 LaraLint 预设 以开始。从那里,您可以创建一个适用于项目的自定义预设,使用对团队有意义的 linters。尽情地混合和匹配预构建的 linters 和您自己的 自定义 linters 吧!

自定义 linters

如果您对 LaraLint 感兴趣,您可能也对自定义 linters 感兴趣。每个团队的需求都不同,LaraLint 尝试使快速添加强制执行您约定的约定的逻辑尽可能简单。

尽管如此,LaraLint 确实 依赖于像抽象语法树这样的概念,一开始可能会让人感到有些难以理解。但掌握一些关键概念后,您应该很快就能像高手一样遍历这个树了!

快速了解:LaraLint 的工作原理

用尽可能少的词语描述

LaraLint 使用 Microsoft 的 Tolerant PHP Parser 将您的 PHP 代码解析成抽象语法树(AST)。我们之所以使用 Microsoft 的解析器,是因为它速度快,能够很好地处理部分编写的代码(例如您在 IDE 中输入的代码)。它也是 VS Code 的 IntelliSense 的基础,因此有着良好的记录。

想象一下,这个树就是您代码的结构化视图。给定以下 PHP 代码

class Foo
{
  public function bar()
  {
    return 'baz';
  }
}

AST 将看起来像这样(为了清晰起见进行了简化)

  • ClassDeclaration (class Foo)
    • MethodDeclaration (public function bar())
      • CompoundStatementNode (return 'baz';)
        • ReturnStatement (return)
          • StringLiteral ('baz')

LaraLint 从上到下遍历这棵树,并将每个节点传递给每个适用的 Linter 进行检查。linters 是接收 AST 节点并可选地返回检查结果的简单对象。在完全遍历树之后,linters 才会返回检查结果。

这意味着您可以编写极其复杂和定制的代码检查工具,但希望您不需要这样做。

如何编写 LaraLint 代码检查工具

大多数 LaraLint 代码检查工具的核心是一个 Matcher 对象。这些对象被设计用来轻松地匹配一般的抽象语法树(AST)“形状”,并在找到整个“形状”时标记代码的一部分。例如,如果您想要标记任何名为 bar 的方法,该方法返回字符串 'baz',您可以使用以下匹配器

(new TreeMatcher())
    // (1) Find a method declaration where the method name is "bar"
    ->withChild(function(MethodDeclaration $node) {
        return 'bar' === $node->getName();
    })
    // (2) Find any return statement
    ->withChild(ReturnStatement::class)
    // (3) Find a string literal that matches the string "baz"
    ->withChild(function(StringLiteral $node) {
        return 'baz' === $node->getStringContentsText();
    })
    ->onMatch(function(Collection $all_matched_nodes) {
        // Create a linting result using the matched nodes.
        // LaraLint will automatically map the AST nodes to line
        // numbers when printing the results.
    });

一旦 LaraLint 找到一个与第一个规则匹配的节点,它就会开始寻找一个与第二个规则匹配的子节点。如果它找到一个与第二个规则匹配的子节点,它就会移动到第三个规则。当它匹配所有规则时,匹配器将触发 onMatch 回调,您可以在其中执行任何您选择的附加逻辑。

LaraLint 随带了一些适用于常见用例的“策略”。这些策略抽象出更多的逻辑,是开始的最佳地点。查看一些现有的代码检查工具,以了解如何最好地使用每个策略。

但是等等…我该如何编写 LaraLint 代码检查工具呢?

好吧,您可能正在查看 ReturnStatementStringLiteral,并想,“我不会说抽象语法树。”

其他人也不会。

这就是 laralint:dump 命令发挥作用的地方。比如说,您正在尝试编写上面示例中的愚蠢的 bar/baz 代码检查工具。只需创建一个应该失败的 PHP 文件,并转储其树

php artisan laralint:dump barbaz_source.php

这将输出类似以下的内容

┏━━ ClassDeclaration ━━━━━━┓
┃                          ┃
┃ class Foo                ┃
┃ {                        ┃
┃   public function bar()  ┃
┃   {                      ┃
┃     return 'baz';        ┃
┃   }                      ┃
┃ }                        ┃
┃                          ┃
┃   ┏━━ ClassMembersNode ━━━━━━┓
┃   ┃                          ┃
┃   ┃ {                        ┃
┃   ┃   public function bar()  ┃
┃   ┃   {                      ┃
┃   ┃     return 'baz';        ┃
┃   ┃   }                      ┃
┃   ┃ }                        ┃
┃   ┃                          ┃
┃   ┃   ┏━━ MethodDeclaration ━━━┓
┃   ┃   ┃                        ┃
┃   ┃   ┃ public function bar()  ┃
┃   ┃   ┃   {                    ┃
┃   ┃   ┃     return 'baz';      ┃
┃   ┃   ┃   }                    ┃
┃   ┃   ┃                        ┃
┃   ┃   ┃   ┏━━ CompoundStatementNode ━━┓
┃   ┃   ┃   ┃                           ┃
┃   ┃   ┃   ┃ {                         ┃
┃   ┃   ┃   ┃     return 'baz';         ┃
┃   ┃   ┃   ┃   }                       ┃
┃   ┃   ┃   ┃                           ┃
┃   ┃   ┃   ┃   ┏━━ ReturnStatement ━━┓
┃   ┃   ┃   ┃   ┃                     ┃
┃   ┃   ┃   ┃   ┃ return 'baz';       ┃
┃   ┃   ┃   ┃   ┃                     ┃
┃   ┃   ┃   ┃   ┃   ┏━━ StringLiteral ━━┓
┃   ┃   ┃   ┃   ┃   ┃                   ┃
┃   ┃   ┃   ┃   ┃   ┃ 'baz'             ┃
┃   ┃   ┃   ┃   ┃   ┃                   ┃
┃   ┃   ┃   ┃   ┃   ┗━━━━━━━━━━━━━━━━━━━┛
┃   ┃   ┃   ┃   ┗━━━━━━━━━━━━━━━━━━━━━┛
┃   ┃   ┃   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┃   ┃   ┗━━━━━━━━━━━━━━━━━━━━━━━━━┛
┃   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

dump 命令的输出和现有代码检查工具的示例之间,您会惊讶于开始编写自己的规则有多容易。

IDE 集成

代码检查结果的最佳位置就在您的 IDE 中。我们不需要发布自己的 IDE 插件,LaraLint 可以简单地假装是 PHP_CodeSniffer

php artisan laralint:lint --printer=phpcs

这将为您提供与任何可以解析 PHP_CodeSniffer 的 XML 输出的插件兼容的 XML 输出(如 PhpStorm)。

运行 php artisan laralint:install 将安装一个漂亮的辅助文件,这使得整个过程变得更容易。只需配置您的 IDE 以指向该文件,您的 LaraLint 代码检查应该会在 IDE 中被标记。