{"id":25990075,"url":"https://github.com/schlndh/maria-stan","last_synced_at":"2025-03-05T13:24:28.926Z","repository":{"id":57806666,"uuid":"509963985","full_name":"schlndh/maria-stan","owner":"schlndh","description":"Static analyser for MariaDB queries","archived":false,"fork":false,"pushed_at":"2025-02-07T13:37:03.000Z","size":1109,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-07T14:36:10.866Z","etag":null,"topics":["mariadb","php","phpstan-extension","static-analysis"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/schlndh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-03T08:23:40.000Z","updated_at":"2025-02-07T13:37:08.000Z","dependencies_parsed_at":"2023-01-27T01:31:31.501Z","dependency_job_id":"0f16382a-c0a8-409b-917d-e8f12381a707","html_url":"https://github.com/schlndh/maria-stan","commit_stats":{"total_commits":248,"total_committers":1,"mean_commits":248.0,"dds":0.0,"last_synced_commit":"f96c2e798f22d6709748ca02f5b6ad0b5ab60bd2"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schlndh%2Fmaria-stan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schlndh%2Fmaria-stan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schlndh%2Fmaria-stan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schlndh%2Fmaria-stan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/schlndh","download_url":"https://codeload.github.com/schlndh/maria-stan/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241813578,"owners_count":20024632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mariadb","php","phpstan-extension","static-analysis"],"created_at":"2025-03-05T13:24:28.236Z","updated_at":"2025-03-05T13:24:28.916Z","avatar_url":"https://github.com/schlndh.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MariaStan\n\nMariaStan is a static analysis tool for MariaDB queries. Its primary purpose is to serve as a basis for\n[PHPStan](https://phpstan.org/) extensions.\n\n**Current status** (31. 01. 2025):\n\nMariaStan is very much incomplete. It covers probably ~90% of use-cases in a large code-base where I use it\n(hundreds of tables, thousands of queries). As a result there is not much activity. But it is actively maintained in\nthe sense that if something breaks for me, it will probably get fixed.\n\nIf you try to use it in your project, you are likely to run into use-cases which are not implemented\n(e.g. syntax/functions which my project doesn't use). If that happens, you should be prepared to fix things for yourself\n(most things should be easy).\n\nThere is no backwards-compatibility promise on anything, and there are no releases - I just use master.\n\nMariaStan is tested with MariaDB 10.11 and PHP 8.1-8.4.\n\n## Installation\n\nInstall MariaStan using `composer require --dev schlndh/maria-stan:dev-master`. Then you'll need to add the following\nto your `phpstan.neon`:\n\n```neon\nincludes:\n    - ./vendor/schlndh/maria-stan/extension.neon\n\n\n```\n\n## Configuration\n\nMariaStan needs access to the database schema. The easiest way to provide it is to let it connect directly to a database.\nYou'll need to add the following configuration to your `phpstan.neon` and set proper credentials:\n\n```neon\nparameters: \n    maria-stan:\n        db:\n            # Change these to match your database\n            host: 127.0.0.1\n            port: 3306\n            user: 'root'\n            password: ''\n            database: 'db'\n```\n\nMariaStan needs access to a database to fetch the schema for query analysis. It only reads table schema and does not\nwrite anything. Nevertheless, **DO NOT** give it access to any database which contains any important data.\n\nAlternatively, it is also possible to use MariaStan without access to the database during analysis. In that case you'll\nneed to first dump the schema using `MariaDbFileDbReflection::dumpSchema` and save it into a file. Here is an example\nscript that does that:\n\n```php\n\u003c?php\n\ndeclare(strict_types=1);\n\nuse MariaStan\\DbReflection\\MariaDbFileDbReflection;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n$mysqli = new mysqli('127.0.0.1', 'root', '');\nfile_put_contents(__DIR__ . '/maria-stan-schema.dump', MariaDbFileDbReflection::dumpSchema($mysqli, 'database'));\n```\n\nThen add the following to your `phpstan.neon`:\n\n```neon\nparameters:\n    maria-stan:\n        reflection:\n            file: %rootDir%/../../../maria-stan-schema.dump\nservices:\n    mariaDbReflection: @mariaDbFileDbReflection\n```\n\nNote that the [automatic expansion of relative paths](https://phpstan.org/config-reference#expanding-paths) only works\nwith PHPStan's own configuration (i.e. it's a hardcoded list of config keys). So you'll have to provide an absolute path\nto the dump file.\n\nSee `extension.neon` for a complete list of parameters.\n\n## Usage\n\nMariaStan includes a sample PHPStan extension for [MySQLi](https://www.php.net/manual/en/book.mysqli.php). However,\nthe purpose of this extension is simply to verify the integration with PHPStan. I do not expect anyone to actually use\nMySQLi directly. Therefore, you are expected to write your own [PHPStan extension](https://phpstan.org/developing-extensions/extension-types)\nthat integrates with MariaStan. If you want to use the MySQLi extension include `./vendor/schlndh/maria-stan/extension.mysqli.neon`\nin your `phpstan.neon`.\n\nYou can use the MySQLi extension as a starting point a modify it to match your needs. The basic idea is to get the query\nstring from PHPStan, pass it to MariaStan for analysis and then report result types and errors back to PHPStan.\n\nBefore 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](examples/MySQLi/README.md) 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.\n\n## Features\n\nHere is a list of features that you could implement into your own PHPStan extension based on MariaStan\n(most of them should be demonstrated in the MySQLi extension):\n\n- Type inference for query results. This alone is invaluable when using PHPStan with code that works with DB data.\n  - MariaStan can sometimes infer narrower types than the types returned by the database (e.g. `mysqli_result::fetch_fields`).\n    This is because MariaStan can (in simple cases) understand queries like `SELECT col FROM tbl WHERE col IS NOT NULL`\n    and remove the `NULL` from the result type, whereas MariaDB doesn't seem to do that.\n- Basic error detection. For example:\n    - Unknown tables, columns, ...\n    - ambiguous column names,\n    - mismatched number of placeholders,\n    - missing data for mandatory columns in `INSERT`/`REPLACE`\n    - ...\n- Report usage of deprecated tables/columns.\n- Row count range: in simple cases MariaStan can determine the number of returned rows (e.g. `SELECT COUNT(*) FROM tbl`)\n  which can be used to narrow-down the return type of methods like `mysqli_result::fetch_all` (i.e. `non-empty-array`).\n- Column type overrides: You can override the type of a column (e.g. because the table is \"static\") and even propagate it\n  automatically via foreign keys.\n- Custom PHPDoc types: e.g. [MySQLiTableAssocRowType](https://github.com/schlndh/maria-stan/blob/master/src/PHPStan/Type/MySQLi/MySQLiTableAssocRowType.php).\n- PHPStan result cache is invalidated when DB schema changes via [ResultCacheMetaExtension](https://phpstan.org/developing-extensions/result-cache-meta-extensions).\n\n## Limitations\n\n- There is no support for query builders. The analyser expects a query as a string on the input.\n- The query has to be fully known statically. If you have a code like this:\n    ```php\n    function foo(mysqli $db, int $count) {\n        return $db-\u003eprepare(\"SELECT * FROM tbl WHERE id IN (\" . implode(',', array_fill(0, $count, '?')) . ')');\n    }\n    ```\n  PHPStan will not be able to evaluate it statically and thus MariaStan has nothing to analyse.\n- There is no support for temporary tables.\n- There is no support for multiple databases.\n- The limitations above are the main ones long term. But besides them, everything is only partially implemented.\n\n## Similar projects\n\n### [staabm/phpstan-dba](https://github.com/staabm/phpstan-dba)\n\nAs far as I can tell, phpstan-dba works by executing the queries to get the information about result types, errors, ...\nMariaStan on the other hand analyzes the queries statically. Benefits of phpstan-dba include:\n\n- Easy support for multiple databases. With MariaStan this is impossible (with the possible exception of MySQL).\n- More complete and reliable error checking thanks to the database doing the heavy lifting. On the other hand,\n the database only returns one error at a time, whereas MariaStan may be able to discover multiple issues at once.\n- Query plan analysis. This seems infeasible to do statically. On the other hand, I'm not sure how useful this is in\n practice especially if you don't want to give phpstan-dba access to production data.\n- It appears to be easier to get started with it, as it has extensions for multiple database abstractions.\n\nThere are some minor downsides to phpstan-dba's approach:\n\n- There is no path to full static analysis (i.e. not requiring a running database at any point). MariaStan currently\n also requires a running database (to get data from `information_schema`, not necessarily at analysis time). But it is\n possible to implement `CREATE TABLE` (etc.) parsing and implement a DB reflection on top of that.\n- Because the queries are executed, it has to be careful with data/schema modification queries. I saw some conditions\n that restrict it to `SELECT` queries in several places, as well as the use of transactions. Therefore, I'm not sure\n how well it supports `INSERT` etc. (there are some in tests at least).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschlndh%2Fmaria-stan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschlndh%2Fmaria-stan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschlndh%2Fmaria-stan/lists"}