vkcom / modulite-phpstan
一个为纯PHP(非KPHP)项目提供Modulite支持的PHPStan插件
Requires
- php: ^7.4 || ^8.0
- phpstan/phpstan: ^1.8
- symfony/polyfill-php80: ^v1.27.0
- symfony/yaml: ^5.4 || ^6.0
Requires (Dev)
- phpstan/phpstan-phpunit: ^1.2
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-08-30 18:56:16 UTC
README
Modulite是一种将模块引入PHP语言的概念。PHP没有原生模块(内部类、私有命名空间、显式导出),而Modulite试图消除这一缺点。
实际上,所有的“模块化”都表示为.modulite.yaml
文件,该文件声明了导出和需求。有一个PHPStorm插件可以深度集成到IDE中,可视化yaml配置并提供修改它的操作。
此PHPStan插件读取.modulite.yaml
文件,如果您的代码破坏了模块化,则会引发错误。这允许您在Git钩子、CI等中对整个项目进行检查。
提示。如果您不熟悉Modulite,请考虑一个PHPStorm插件和一个着陆页(俄语)。
安装和配置
要安装,只需使用Composer
composer require --dev vkcom/modulite-phpstan
安装后,将插件包含在您的phpstan.neon
中,并传递两个参数
parameters:
modulite:
projectRoot: path # project root is where composer.json/composer.lock/vendor exist
srcRoot: path # typically, projectRoot/src ; .modulite.yaml files will be searched recursively
includes:
- vendor/vkcom/modulite-phpstan/extension.neon
示例项目
克隆 modulite-example-project,运行composer install
,运行vendor/bin/phpstan analyze
,并查看一些错误。
示例项目故意包含一些错误 :) 它还描述了将代码变为完美的几个步骤。
您也可以在开发Composer包时使用Modulite。此外,您还可以控制哪些符号从您的包中导出。这种能力在文档中有涵盖。
插件的工作原理
正如预期的那样,modulite-phpstan分析函数调用、类使用、PHPDocs等,并确保您的代码符合export
、require
和其他Modulite规则。
我们建议您在开发时使用PHPStorm,因为PHPStorm的插件可以顺畅地集成到IDE中,生成.modulite.yaml
文件,这些文件由modulite-phpstan分析。
通常,项目结构如下
my_project/ # projectRoot
src/ # srcRoot
tests/
vendor/
composer.json
当PHPStan开始分析您的代码时,此插件
- 在
{srcRoot}
内部查找所有.modulite.yaml
- 在
{projectRoot}/vendor
内部查找所有.modulite.yaml
和composer.json
文件
插件必须扫描vendor
,因为Composer包也需要进行检查等。此外,包也可以使用Modulite开发并包含私有符号,因此当嵌入到您的项目中时,插件也会检查它们的使用。
Modulite文件在PHPStan运行时解析、解析和验证一次。如果没有错误,将执行所有模块化检查。否则,yaml错误将被输出,不会执行任何检查。
vendor目录外的Composer包
在罕见情况下,您可能在同一存储库中本地开发Composer包。由于安装到vendor
,它们只是符号链接
my_project/ # projectRoot
src/ # srcRoot
packages/ # additionalPackagesRoot
utils-common/
src/
composer.json
tests/
vendor/
doctrine/
phpstan/
utils/
common/ (symlink to packages/utils-common)
composer.json (contains "repositories" type "path" into packages/*)
在这种情况下,PHPStan的内部反射发现了一些位于packages/
中的类,从Modulite的角度来看,它们应该在vendor/
中。如果没有额外的提示,Modulite将产生错误,认为vendor/
使用了范围之外的文件。
为了解决这个问题,除了srcRoot
外,还传递additionalPackagesRoot
参数。然后modulite-phpstan也将扫描该目录并创建必要的路径映射。
关于PHPStan缓存的遗憾之处
如果PHP文件未更改,PHPStan不会在其中执行分析。它缓存文件状态和错误,并直接输出它们,它只分析差异和更改的依赖关系。
如上所示,
- 当PHP代码未更改,但通过PHPStorm插件更改了
.modulite.yaml
文件时,不会运行检查 - 当部分PHP代码更改且
.modulite.yaml
文件也更改时,PHPStan将仅对更改的文件运行检查,并将之前(缓存的)错误输出给未更改的文件,尽管未更改的文件在yaml修改后可能不包含错误
目前,一个100%有效的方法是定期清除缓存
vendor/bin/phpstan clear-result-cache
这将使PHPStan分析整个项目,插件将按预期工作。
可能,可以通过对PHPStan缓存内部结构的深入了解来修复此问题,了解如何将其嵌入以及如何告知PHPStan关于PHP<->yaml互连。如果您熟悉编写插件,请在此问题中提出建议。
限制
- 如果您遇到意外行为,请尝试清除PHPStan缓存(如上所述的部分)。
- 每次运行都会递归扫描
vendor/
目录,这可能会花费明显的时间;这可以改进,请为此问题投票。 projectRoot
和srcRoot
参数是必需的,但可能它们可以自动计算;如果您熟悉编写插件,请在此问题中添加注释。- 仅分析静态方法调用,
$obj->method()
不被分析,这就是为什么实例方法不能添加到force-internal
。这是故意的,因为静态调用可以明确解析,而实例调用依赖于类型推断,这肯定会在IDE和PHPStan之间有所不同;实际上,静态调用和类使用已经足够了。
贡献说明
请注意,Modulite行为的所有逻辑必须在3个地方相同
- 在PHPStorm中的Modulite;检查以IDEA检查的形式表示,符号解析也依赖于PHPStorm内部。
- PHPStan中的Modulite(此存储库)。
- KPHP中的Modulite;KPHP是VK发明的一种PHP编译器;此外,模块的初始实现是为KPHP+PHPStorm组合制作的,后来相同的逻辑被公开为PHPStan插件,用于常规PHP项目;与yaml验证和规则检查相关的大量代码是从C++实现移植的,并且必须与KPHP保持同步,以保持可持续性。
如果您发现了一个错误,它可能是一个与PHPStan特性相关的特定错误,或者它可能是一个也存在于其他实现中的错误,应该同时修复。如果您有一个功能请求,它必须在三个存储库中同时实现,并使用相同的测试进行覆盖。因此,请随时提交问题,我们将找到管理它们的方法。
如果您熟悉PHPStan内部,并且有建议使此插件更符合标准,我们将很高兴看到您的评论和PR。
提问并提供反馈
此插件由VK.com的KPHP团队开发。
要与我们社区沟通,请使用GitHub问题或Telegram聊天。