phpstan-dba icon indicating copy to clipboard operation
phpstan-dba copied to clipboard

Inconsistent result between run

Open noemi-salaun opened this issue 3 years ago • 8 comments

I can't reproduce it on demand, but it looks like sometime phpstan-dba can infer a query result type, and 5min later, it fails on the same query.

I have a phpstan error ignored in my phpstan.neon :

'#^Method .* should return array<int, array{nbTransactions: int, platform: string}> but returns array<int, array<string, mixed>>.$#'

After installing phpstan-dba and running it successfuly, nothing change for this error.

One day later, my CI fail telling me that this ignored error isn't triggered anymore. So I tried running phpstan locally and yes, phpstan know the proper type for this query. So I remove the ignored error from my phpstan.neon, commit everything, it works.

Then 5min later, my phpstan warn me about the exact same error I just remove from ignored because it wasn't necessary anymore...

I tried removing the phpstan and phpstan-dba entirely but it keeps throwing the same error and I cannot make it detect the correct return type. The generated cache for phpstan-dba seems to vary between calls :/

noemi-salaun avatar Oct 19 '22 15:10 noemi-salaun

the latest phpstan release fixed some result-cache issues. https://github.com/phpstan/phpstan/releases/tag/1.8.11

could you re-validate whether this might have fixed the underlying problem?

staabm avatar Oct 25 '22 07:10 staabm

I think the inconsistency came from phpstorm which autorun phpstan on the current file, so phpstan-dba cache only get info about this file and its dependencies, instead of the whole project.

But that does not explain why sometime phpstan-dba successfuly get my query return type, and sometime not. For example, my code below :

$dbal = $this->getEntityManager()->getConnection();

$sql = <<<SQL
SELECT COUNT(bt.id) AS nbTransactions, bm.key AS platform
FROM BankTransaction bt
    JOIN BankTransactionStatus bts ON bt.statusId = bts.id
    JOIN BankMethod bm ON bt.methodId = bm.id
WHERE userId = :userId
    AND bts.status = 'paymentback_done'
GROUP BY bm.id, bm.key
SQL;

return $dbal->fetchAllAssociative($sql, ['userId' => $userId]);

If I run phpstan on my whole project, I got an empty result cache, but if I run it on this specific file, or its direct parent directory it works fine.

CLICK TO EXPAND Empty result cache when running phpstan on whole project
<?php return array (
  'schemaVersion' => 'v9-put-null-when-valid',
  'schemaHash' => 'b7adec1533cb7674c8fc90d6d83e8804',
  'records' => 
  array (
    // ... other queries
    'SELECT COUNT(bt.id) AS nbTransactions, bm.key AS platform
FROM BankTransaction bt
    JOIN BankTransactionStatus bts ON bt.statusId = bts.id
    JOIN BankMethod bm ON bt.methodId = bm.id
WHERE userId = \'1\'
    AND bts.status = \'paymentback_done\'
GROUP BY bm.id, bm.key' => 
    array (
      'error' => NULL,
    ),
    // ... other queries
  ),
  'runtimeConfig' => 
  array (
    'errorMode' => 'default',
    'debugMode' => false,
    'stringifyTypes' => false,
  ),
);

Dumped type: array<int, array<string, mixed>>

CLICK TO EXPAND Good result cache when running phpstan on this specific file
<?php return array (
  'schemaVersion' => 'v9-put-null-when-valid',
  'schemaHash' => 'b7adec1533cb7674c8fc90d6d83e8804',
  'records' => 
  array (
    // ... other queries
    'SELECT COUNT(bt.id) AS nbTransactions, bm.key AS platform
FROM BankTransaction bt
    JOIN BankTransactionStatus bts ON bt.statusId = bts.id
    JOIN BankMethod bm ON bt.methodId = bm.id
WHERE userId = \'1\'
    AND bts.status = \'paymentback_done\'
GROUP BY bm.id, bm.key' => 
    array (
      'result' => 
      array (
        5 => 
        PHPStan\Type\Constant\ConstantArrayType::__set_state(array(
           'keyType' => 
          PHPStan\Type\UnionType::__set_state(array(
             'sortedTypes' => true,
             'types' => 
            array (
              0 => 
              PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
                 'value' => 0,
              )),
              1 => 
              PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
                 'value' => 1,
              )),
              2 => 
              PHPStan\Type\Constant\ConstantStringType::__set_state(array(
                 'objectType' => NULL,
                 'value' => 'nbTransactions',
                 'isClassString' => false,
              )),
              3 => 
              PHPStan\Type\Constant\ConstantStringType::__set_state(array(
                 'objectType' => NULL,
                 'value' => 'platform',
                 'isClassString' => false,
              )),
            ),
          )),
           'itemType' => 
          PHPStan\Type\UnionType::__set_state(array(
             'sortedTypes' => false,
             'types' => 
            array (
              0 => 
              PHPStan\Type\IntegerType::__set_state(array(
              )),
              1 => 
              PHPStan\Type\StringType::__set_state(array(
              )),
            ),
          )),
           'allArrays' => NULL,
           'nextAutoIndexes' => 
          array (
            0 => 2,
          ),
           'keyTypes' => 
          array (
            0 => 
            PHPStan\Type\Constant\ConstantStringType::__set_state(array(
               'objectType' => NULL,
               'value' => 'nbTransactions',
               'isClassString' => false,
            )),
            1 => 
            PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
               'value' => 0,
            )),
            2 => 
            PHPStan\Type\Constant\ConstantStringType::__set_state(array(
               'objectType' => NULL,
               'value' => 'platform',
               'isClassString' => false,
            )),
            3 => 
            PHPStan\Type\Constant\ConstantIntegerType::__set_state(array(
               'value' => 1,
            )),
          ),
           'valueTypes' => 
          array (
            0 => 
            PHPStan\Type\IntegerType::__set_state(array(
            )),
            1 => 
            PHPStan\Type\IntegerType::__set_state(array(
            )),
            2 => 
            PHPStan\Type\StringType::__set_state(array(
            )),
            3 => 
            PHPStan\Type\StringType::__set_state(array(
            )),
          ),
           'optionalKeys' => 
          array (
          ),
        )),
      ),
    ),
    // ... other queries
  ),
  'runtimeConfig' => 
  array (
    'errorMode' => 'default',
    'debugMode' => false,
    'stringifyTypes' => false,
  ),
);

Dumped type: array<int<0, max>, array{nbTransactions: int, platform: string}>

noemi-salaun avatar Oct 26 '22 13:10 noemi-salaun

The issue with PHPStorm is fixed

noemi-salaun avatar Nov 11 '22 17:11 noemi-salaun

the linked fix was not merged. is the issue still resolved?

staabm avatar Nov 12 '22 08:11 staabm

Oh no my bad, I wanted to clean my issues and I forgot this fix wasnt merge yet... sorry

noemi-salaun avatar Nov 12 '22 09:11 noemi-salaun

But that does not explain why sometime phpstan-dba successfuly get my query return type, and sometime not. For example, my code below :

@noemi-salaun could you try to put this into a small repo which reproduces this problem and a small readme describing how to reproduce? sounds like something we need to fix but I am currently not able to reproduce

staabm avatar Nov 21 '22 08:11 staabm

in the meantime I will merge the phpstorm workaround from https://github.com/staabm/phpstan-dba/pull/454, but I would love to dig into this more with a reproducer

staabm avatar Nov 21 '22 09:11 staabm

I think the issue was linked to https://github.com/staabm/phpstan-dba/issues/394#issuecomment-1312118095 because ReplayAndRecordingQueryReflector works for the first query it encounter but not the others, so when running PHPStan on the whole project, some queries works and some don't, and then when PHPStorm trigger phpstan on a single file, a different set of queries works.

noemi-salaun avatar Nov 21 '22 09:11 noemi-salaun