schlndh / maria-stan
MariaDB查询的静态分析器
Requires
- php: ^8.1
- ext-mbstring: *
- phpstan/phpstan: ^1.10.36
Requires (Dev)
- ext-mysqli: *
- nikic/php-parser: ^4.19
- phpstan/phpstan-deprecation-rules: ^1.1
- phpstan/phpstan-phpunit: ^1.1
- phpstan/phpstan-strict-rules: ^1.5
- phpunit/phpunit: ^9.5
- slevomat/coding-standard: ~8.15.0
- squizlabs/php_codesniffer: ^3.7
This package is auto-updated.
Last update: 2024-09-27 13:07:16 UTC
README
MariaStan是MariaDB查询的静态分析工具。其主要目的是作为PHPStan扩展的基础。
当前状态 (24. 04. 2024):
MariaStan尚未完成。它覆盖了我使用的大型代码库中的大约90%用例(数百张表,数千个查询)。因此,活动不多。但是,它正在积极维护,即如果它对我造成问题,它可能会得到修复。
如果您尝试在自己的项目中使用它,您可能会遇到未实现的用例(例如,我的项目中不使用的语法/函数)。如果发生这种情况,您应该准备好自己解决问题(大多数事情应该都很简单)。
对于任何内容,都没有向后兼容性的承诺,也没有发布 - 我只是使用master。
MariaStan已在MariaDB 10.11和PHP 8.1-8.3上进行了测试。
安装
使用composer require --dev schlndh/maria-stan:dev-master
安装MariaStan。然后您需要在您的phpstan.neon
中添加以下内容
includes: - ./vendor/schlndh/maria-stan/extension.neon
配置
MariaStan需要访问数据库模式。最简单的方法是让它直接连接到数据库。您需要在您的phpstan.neon
中添加以下配置并设置正确的凭证
parameters: maria-stan: db: # Change these to match your database host: 127.0.0.1 port: 3306 user: 'root' password: '' database: 'db'
MariaStan需要访问数据库以获取查询分析的模式。它只读取表模式,不写入任何内容。尽管如此,不要给它访问包含任何重要数据的任何数据库的权限。
或者,在分析期间也可以不访问数据库使用MariaStan。在这种情况下,您需要首先使用MariaDbFileDbReflection::dumpSchema
导出模式并将其保存到文件中。以下是一个示例脚本,它执行此操作
<?php declare(strict_types=1); use MariaStan\DbReflection\MariaDbFileDbReflection; require_once __DIR__ . '/vendor/autoload.php'; $mysqli = new mysqli('127.0.0.1', 'root', ''); file_put_contents(__DIR__ . '/maria-stan-schema.dump', MariaDbFileDbReflection::dumpSchema($mysqli, 'database'));
然后,将以下内容添加到您的phpstan.neon
parameters: maria-stan: reflection: file: %rootDir%/../../../maria-stan-schema.dump services: mariaDbReflection: @mariaDbFileDbReflection
请注意,相对路径的自动扩展仅在PHPStan自己的配置中工作(即它是硬编码的配置键列表)。因此,您必须提供到导出文件的绝对路径。
有关参数的完整列表,请参阅extension.neon
。
用法
MariaStan包括一个用于MySQLi的示例PHPStan扩展。然而,此扩展的目的是简单地验证与PHPStan的集成。我不期望任何人实际直接使用MySQLi。因此,您应该编写自己的PHPStan扩展,并将其与MariaStan集成。如果想要使用MySQLi扩展,请在phpstan.neon
中包含./vendor/schlndh/maria-stan/extension.mysqli.neon
。
您可以将MySQLi扩展作为起点进行修改,以适应您的需求。基本思想是从PHPStan获取查询字符串,将其传递给MariaStan进行分析,然后将结果类型和错误报告回PHPStan。
在您开始实现将 MariaStan 集成到项目中的自定义扩展之前,您可以快速尝试它。您可以从检查一个简单的示例开始,该示例使用 MySQLi 扩展。然后您可以通过 MySQLi 从您的代码库中调用查询,并使用 MySQLi 扩展来分析它们,以确保 MariaStan 支持您的项目使用的功能。
功能
以下是一个功能列表,您可以根据 MariaStan 实现 PHPStan 扩展(其中大部分应在 MySQLi 扩展中演示):
- 查询结果的类型推断。当使用 PHPStan 与处理数据库数据的代码一起使用时,这一点非常有价值。
- 有时 MariaStan 可以推断比数据库返回的类型更窄的类型(例如
mysqli_result::fetch_fields
)。这是因为 MariaStan 可以(在简单情况下)理解像SELECT col FROM tbl WHERE col IS NOT NULL
这样的查询,并从结果类型中删除NULL
,而 MariaDB 似乎不会这样做。
- 有时 MariaStan 可以推断比数据库返回的类型更窄的类型(例如
- 基本的错误检测。例如
- 未知表、列...
- 模糊的列名,
- 占位符数量不匹配,
INSERT
/REPLACE
中缺少必需列的数据- ...
- 报告已弃用表/列的使用。
- 行数范围:在简单情况下,MariaStan 可以确定返回的行数(例如
SELECT COUNT(*) FROM tbl
),这可以用来缩小像mysqli_result::fetch_all
(即non-empty-array
)这样的方法的返回类型。 - 列类型覆盖:您可以为列覆盖类型(例如,因为表是“静态的”),甚至可以通过外键自动传播它。
- 自定义 PHPDoc 类型:例如 MySQLiTableAssocRowType。
限制
- 目前尚未实现 PHPStan 的缓存失效。不清楚这样的事情是否容易实现。请参阅 此讨论。然而,这仅与模式更改(即如果您在 PHP 文件中更改查询,PHPStan 将自动使其无效)有关。
- 不支持查询构建器。分析器期望输入为字符串的查询。
- 查询必须完全在静态中已知。如果您有如下代码
function foo(mysqli $db, int $count) { return $db->prepare("SELECT * FROM tbl WHERE id IN (" . implode(',', array_fill(0, $count, '?')) . ')'); }
PHPStan 将无法进行静态评估,因此 MariaStan 没有可分析的内容。 - 不支持临时表。
- 不支持多数据库。
- 上述限制是长期的主要限制。但除此之外,其他功能只是部分实现。
类似项目
staabm/phpstan-dba
据我所知,phpstan-dba 通过执行查询来获取有关结果类型、错误等信息。另一方面,MariaStan 分析查询是静态的。phpstan-dba 的好处包括
- 轻松支持多数据库。使用 MariaStan 这是不可能的(可能除 MySQL 之外)。
- 由于数据库执行繁重的工作,因此具有更完整和可靠的错误检查。另一方面,数据库一次只返回一个错误,而 MariaStan 可能一次能够发现多个问题。
- 查询计划分析。这在静态中似乎不可行。另一方面,我不确定这在实践中有多有用,尤其是如果您不想让 phpstan-dba 访问生产数据。
- 它似乎更容易入门,因为它有多个数据库抽象的扩展。
phpstan-dba 的方法有一些小缺点
- 没有通往完全静态分析的道路(即在任何时刻都不需要运行的数据库)。目前,MariaStan 也需要一个运行的数据库(从
information_schema
获取数据,不一定在分析时间)。但可以实现CREATE TABLE
(等)解析,并在其上实现数据库反射。 - 由于查询被执行,因此在数据/模式修改查询上必须小心。我在几个地方看到一些条件,将其限制为
SELECT
查询,以及事务的使用。因此,我不确定它对INSERT
等操作的支持程度如何。(至少在测试中有一些)。