MariaStan is a static analysis tool for MariaDB queries. Its primary purpose is to serve as a basis for PHPStan extensions.
Current status (24. 04. 2024):
MariaStan is very much incomplete. It covers probably ~90% of use-cases in a large code-base where I use it (hundreds of tables, thousands of queries). As a result there is not much activity. But it is actively maintained in the sense that if something breaks for me it will probably get fixed.
If you try to use it in your project, you are likely to run into use-cases which are not implemented (e.g. syntax/functions which my project doesn't use). If that happens, you should be prepared to fix things for yourself (most things should be easy).
There is no backwards-compatibility promise on anything, and there are no releases - I just use master.
MariaStan is tested with MariaDB 10.11 and PHP 8.1-8.3.
Install MariaStan using composer require --dev schlndh/maria-stan:dev-master. Then you'll need to add the following
to your phpstan.neon:
includes:
- ./vendor/schlndh/maria-stan/extension.neon
MariaStan needs access to the database schema. The easiest way to provide it is to let it connect directly to a database.
You'll need to add the following configuration to your phpstan.neon and set proper credentials:
parameters:
maria-stan:
db:
# Change these to match your database
host: 127.0.0.1
port: 3306
user: 'root'
password: ''
database: 'db'MariaStan needs access to a database to fetch the schema for query analysis. It only reads table schema and does not write anything. Nevertheless, DO NOT give it access to any database which contains any important data.
Alternatively, it is also possible to use MariaStan without access to the database during analysis. In that case you'll
need to first dump the schema using MariaDbFileDbReflection::dumpSchema and save it into a file. Here is an example
script that does that:
<?php
declare(strict_types=1);
use MariaStanDbReflectionMariaDbFileDbReflection;
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'));Then add the following to your phpstan.neon:
parameters:
maria-stan:
reflection:
file: %rootDir%/../../../maria-stan-schema.dump
services:
mariaDbReflection: @mariaDbFileDbReflectionNote that the automatic expansion of relative paths only works with PHPStan's own configuration (i.e. it's a hardcoded list of config keys). So you'll have to provide an absolute path to the dump file.
See extension.neon for a complete list of parameters.
MariaStan includes a sample PHPStan extension for MySQLi. However,
the purpose of this extension is simply to verify the integration with PHPStan. I do not expect anyone to actually use
MySQLi directly. Therefore, you are expected to write your own PHPStan extension
that integrates with MariaStan. If you want to use the MySQLi extension include ./vendor/schlndh/maria-stan/extension.mysqli.neon
in your phpstan.neon.
You can use the MySQLi extension as a starting point a modify it to match your needs. The basic idea is to get the query string from PHPStan, pass it to MariaStan for analysis and then report result types and errors back to PHPStan.
Before you start implementing your own extension to integrate MariaStan into your project, you can quickly try it out. You can start by checking out a simple example which uses the MySQLi extension. Then you can try to call queries from your codebase via MySQLi and analyze them with the MySQLi extension to make sure that MariaStan supports the features which your projects uses.
Here is a list of features that you could implement into your own PHPStan extension based on MariaStan (most of them should be demonstrated in the MySQLi extension):
mysqli_result::fetch_fields).
This is because MariaStan can (in simple cases) understand queries like SELECT col FROM tbl WHERE col IS NOT NULL
and remove the NULL from the result type, whereas MariaDB doesn't seem to do that.INSERT/REPLACESELECT COUNT(*) FROM tbl)
which can be used to narrow-down the return type of methods like mysqli_result::fetch_all (i.e. non-empty-array).function foo(mysqli $db, int $count) {
return $db->prepare("SELECT * FROM tbl WHERE id IN (" . implode(',', array_fill(0, $count, '?')) . ')');
}As far as I can tell, phpstan-dba works by executing the queries to get the information about result types, errors, ... MariaStan on the other hand analyzes the queries statically. Benefits of phpstan-dba include:
There are some minor downsides to phpstan-dba's approach:
information_schema, not necessarily at analysis time). But it is
possible to implement CREATE TABLE (etc.) parsing and implement a DB reflection on top of that.SELECT queries in several places, as well as the use of transactions. Therefore, I'm not sure
how well it supports INSERT etc. (there are some in tests at least).