staabm / phpstan-dba

PHPStan based SQL static analysis and type inference for the database access layer
https://staabm.github.io/archive.html#phpstan-dba
MIT License
252 stars 17 forks source link

Using with doctrine/dbal #645

Open snapshotpl opened 8 months ago

snapshotpl commented 8 months ago

I try to configure in project with doctrine/dbal. I create a bootstrap:

<?php

declare(strict_types=1);

use Doctrine\DBAL\Connection;
use OndraM\CiDetector\CiDetector;
use Psr\Container\ContainerInterface;
use staabm\PHPStanDba\DbSchema\SchemaHasherMysql;
use staabm\PHPStanDba\QueryReflection\PdoMysqlQueryReflector;
use staabm\PHPStanDba\QueryReflection\QueryReflection;
use staabm\PHPStanDba\QueryReflection\ReflectionCache;
use staabm\PHPStanDba\QueryReflection\ReplayAndRecordingQueryReflector;
use staabm\PHPStanDba\QueryReflection\ReplayQueryReflector;
use staabm\PHPStanDba\QueryReflection\RuntimeConfiguration;

require_once __DIR__ . '/../vendor/autoload.php';

$cacheFile = __DIR__ . '/../data/cache/.phpstan-dba.cache';
$reflectionCache = ReflectionCache::create($cacheFile);

$isCi = ! (new CiDetector())->isCiDetected();
$runtimeConfiguration = new RuntimeConfiguration();

if (! $isCi) {
    $runtimeConfiguration->debugMode(true);
    $runtimeConfiguration->analyzeQueryPlans();
//    $runtimeConfiguration->utilizeSqlAst(true);

    /** @var ContainerInterface $container */
    $container = require __DIR__ . '/container.php';

    /** @var Connection $connection */
    $connection = $container->get(Connection::class);

    $nativeConnection = $connection->getNativeConnection();
} else {
    print PHP_EOL . PHP_EOL . 'CI detected' . PHP_EOL . PHP_EOL;
}

$queryReflector = $isCi
    ? new ReplayQueryReflector($reflectionCache)
    : new ReplayAndRecordingQueryReflector(
        $reflectionCache,
        new PdoMysqlQueryReflector($nativeConnection),
        new SchemaHasherMysql($nativeConnection),
    );

QueryReflection::setupReflector($queryReflector, $runtimeConfiguration);

An locally everything works fine. But when I run phpstan on CI, then I get many errors where I use Doctrine\DBAL\Connection::delete or Doctrine\DBAL\Connection::insert

Query error: Table "table_name" does not exist     
staabm commented 8 months ago

Did you setup a database server in CI and imported a db schema into a database?

Could you reproduce your problem in a small example repository?

snapshotpl commented 8 months ago

No, but I manage that I don't need do that because I use cache from https://github.com/staabm/phpstan-dba/blob/main/docs/record-and-replay.md

staabm commented 8 months ago

ok this means we can close here?

snapshotpl commented 8 months ago

So I need to setup db? What about this https://github.com/staabm/phpstan-dba/blob/main/docs/record-and-replay.md ?

staabm commented 8 months ago

ohh I missunderstood your previous comment.

record-and-replay can be used for cases when you really cannot setup a DB in CI. you would commit the recorded files into the repo and it should work then.

having a real db and a real schema might be more work, but is more accurate for the analysis - in case you managed a good dev process how you handle db migrations etc.

snapshotpl commented 8 months ago

For me works like you mentioned, but looks like just Doctrine\DBAL\Connection::delete or Doctrine\DBAL\Connection::insert didn't work when run on CI without DB with record-and-replay. There are not errors with missing table or connection when analyse raw SQL queries.

staabm commented 8 months ago

would be great if you would post a full example, which shows what does not work and what you expect

snapshotpl commented 8 months ago

I have simplified code.

final readonly class Db
{
    public function __construct(private \Doctrine\DBAL\Connection $connection)
    {
    }

    public function save(
        string $id,
    ): string {
        $this->connection->insert('table', [
            'id' => $id,
        ]);
        return $this->connection->executeQuery('SELECT id FROM id = :id', [
            'id' => $id,
        ])->fetchOne();
    }
}

When I run on local with db with all correct loaded schema, then I use ReplayAndRecordingQueryReflector and phpstan analyse pass.

When I run on CI without db, then I use ReplayQueryReflector and phpstan analyse fail only on insert (Query error: Table "table" does not exist). Analyse on executeQuery looks like working with cache. Cache from dev env is commited to be using on CI.

I always run with bootstrap file from first message. $isCi choose a correct QueryReflector.