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
249 stars 17 forks source link

Argument expects a literal string, got literal-string #560

Open noemi-salaun opened 1 year ago

noemi-salaun commented 1 year ago

When calling Connection::insert() with the table name as a literal-string, I got this error message

Argument #0 expects a literal string, got literal-string

staabm commented 1 year ago

thanks for reporting. could you please provide a concrete code sample?

noemi-salaun commented 1 year ago

I have some code that clone a user with all its data.

  1. So I got an hardcoded list of known tables.
  2. Foreach on the list.
  3. SELECT * FROM each table
  4. Change some of the data (replace the userId used as a foreign key, ...)
  5. INSERT the new data in the same table

For the insert, I use a function that add backquote around column and table name.

/**
 * @param literal-string       $tableExpression
 * @param array<string, mixed> $data
 */
private function insert(string $tableExpression, array $data): void
{
    $backquotedTableExpression = "`$tableExpression`";

    $backquotedData = [];
    foreach ($data as $key => $value) {
        $backquotedData["`$key`"] = $value;
    }

    $this->dbal->insert($backquotedTableExpression, $backquotedData);
}

So my real error message is

Argument#0 expects a literal string, got literal-string&non-falsy-string

But the error stay the same if I remove the backquote and pass $tableExpression directly to dbal insert

Argument#0 expects a literal string, got literal-string

hemberger commented 1 year ago

What is being checked in the DoctrineKeyValueStyleRule is that the keys of the array passed to Doctrine\DBAL\Connection::insert correspond to columns in the database. In your insert function, PHPStan doesn't know the literal values of the keys of $data, and therefore it cannot check that they are columns in the database.

Similarly for the table name, if PHPStan doesn't know the literal value of the string, it can't check that the table exists in the database.

If you are wrapping calls to Doctrine\DBAL\Connection in your own class, e.g. My\Connection, then you should probably be declaring its function in your PHPStan configuration instead of the Doctrine ones. For example:

services:
    -
        class: staabm\PHPStanDba\Rules\DoctrineKeyValueStyleRule
        tags: [phpstan.rules.rule]
        arguments:
            classMethods:
                - 'My\Connection::insert#1'

(Note: technically this rule checks for a constant string, so maybe the error message should be revised to replace "literal" -> "constant"?)

staabm commented 1 year ago

(Note: technically this rule checks for a constant string, so maybe the error message should be revised to replace "literal" -> "constant"?)

ohh I did not see that. we should not talk about literal strings when we expect constant strings and vice versa. these are 2 different things - please fix that

staabm commented 1 year ago

@hemberger the code example mentioned in https://github.com/staabm/phpstan-dba/issues/560#issuecomment-1453492795 should be concluded as not analyzable and we should not error (as long as we are not in debug mode)