-
-
Notifications
You must be signed in to change notification settings - Fork 23
phpstan-dba not picking up array shape on doctrine/dbal result #394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
thanks for the report. since you are running phpstan-dba with a custom api it seems (not PDO, Doctrine, mysqli or similar), you need to configure the rules for your API, see https://github.com/staabm/phpstan-dba/blob/main/docs/rules.md
how is your database schema looking for the table involved? |
Just to confirm, I am using This is my bootstrap file: <?php
use staabm\PHPStanDba\DbSchema\SchemaHasherMysql;
use staabm\PHPStanDba\QueryReflection\PdoMysqlQueryReflector;
use staabm\PHPStanDba\QueryReflection\RuntimeConfiguration;
use staabm\PHPStanDba\QueryReflection\QueryReflection;
use staabm\PHPStanDba\QueryReflection\ReplayAndRecordingQueryReflector;
use staabm\PHPStanDba\QueryReflection\ReflectionCache;
require_once __DIR__ . '/vendor/autoload.php';
$params = parse_url(getenv('DB_URL_LOGIN'));
$dsn = sprintf('mysql:dbname=%s;host=%s', ltrim($params['path'], '/'), $params['host']);
$pdo = new PDO($dsn, $params['user'], $params['pass']);
QueryReflection::setupReflector(
new ReplayAndRecordingQueryReflector(
ReflectionCache::create(
__DIR__.'/.phpstan-dba.cache'
),
new PdoMysqlQueryReflector($pdo),
new SchemaHasherMysql($pdo)
),
new RuntimeConfiguration()
); The DB schema is CREATE TABLE `user` (
`id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '(DC2Type:uuid)',
`company_id` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '(DC2Type:uuid)',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`timezone` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`language` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`require_sso` tinyint(1) NOT NULL,
`require_two_factor` tinyint(1) NOT NULL,
`two_factor_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`password_never_expires` tinyint(1) NOT NULL,
`password_edited_on` datetime DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)',
`last_login` datetime DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)',
`super_admin` tinyint(1) NOT NULL,
`disabled_at` datetime DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)',
`deleted_at` datetime DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)',
`active_session` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_8D93D649E7927C74` (`email`),
KEY `IDX_8D93D649979B1AD6` (`company_id`),
CONSTRAINT `FK_8D93D649979B1AD6` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; I got recommended this package by @ondrejmirtes, but maybe I was expecting something it's not meant to do. My main issue was that I was code hinting the shape of the array returned by /**
* @return array{
* id: string,
* name: string,
* email:string,
* timezone: string,
* language: string,
* company_id: string,
* roles: string
* }
*/
private function fetchData(AccessTokenEntityInterface $accessTokenEntity): array; My hope was that this package would infer that shape from the SQL query and get rid of the "nah, $data ain't that shape, it's |
thats exactly the use-case and should work for doctrine DBAL and mysql. you could try enabling debugMode to see whether there are some other problems when your objects are properly typed to the doctrine/dbal interfaces it should work. maybe you can bootstrap a small example reproducing repo, so I can have a look what is missing exactly in your case. |
just did a bit more research... even runnig if you can provide a repository with reproducible minimal failling test-case I would love to fiddle it out. |
Hello, I get the same error with PHPStan 1.8.8 and phpstan-dba 0.2.48. /**
* @return array{id: int, name: string}
*/
private function fetchPlayer(): array
{
$player = $this->connection->fetchAssociative('SELECT id, name FROM players WHERE id = ?', [1]);
if (!$player) {
throw new \RuntimeException('Player not found');
}
return $player;
}
See cached result below[
'SELECT id, name FROM players WHERE id = \'1\'' =>
[
'result' =>
[
5 =>
PHPStan\Type\Constant\ConstantArrayType::__set_state(
[
'keyType' =>
PHPStan\Type\UnionType::__set_state(
[
'sortedTypes' => true,
'types' =>
[
0 =>
PHPStan\Type\Constant\ConstantIntegerType::__set_state(
[
'value' => 0,
]
),
1 =>
PHPStan\Type\Constant\ConstantIntegerType::__set_state(
[
'value' => 1,
]
),
2 =>
PHPStan\Type\Constant\ConstantStringType::__set_state(
[
'objectType' => null,
'value' => 'id',
'isClassString' => false,
]
),
3 =>
PHPStan\Type\Constant\ConstantStringType::__set_state(
[
'objectType' => null,
'value' => 'name',
'isClassString' => false,
]
),
],
]
),
'itemType' =>
PHPStan\Type\UnionType::__set_state(
[
'sortedTypes' => false,
'types' =>
[
0 =>
PHPStan\Type\IntegerRangeType::__set_state(
[
'min' => 0,
'max' => 4294967295,
]
),
1 =>
PHPStan\Type\StringType::__set_state(
[
]
),
],
]
),
'allArrays' => null,
'nextAutoIndexes' =>
[
0 => 2,
],
'keyTypes' =>
[
0 =>
PHPStan\Type\Constant\ConstantStringType::__set_state([
'objectType' => null,
'value' => 'id',
'isClassString' => false,
]),
1 =>
PHPStan\Type\Constant\ConstantIntegerType::__set_state([
'value' => 0,
]),
2 =>
PHPStan\Type\Constant\ConstantStringType::__set_state([
'objectType' => null,
'value' => 'name',
'isClassString' => false,
]),
3 =>
PHPStan\Type\Constant\ConstantIntegerType::__set_state([
'value' => 1,
]),
],
'valueTypes' =>
[
0 =>
PHPStan\Type\IntegerRangeType::__set_state([
'min' => 0,
'max' => 4294967295,
]),
1 =>
PHPStan\Type\IntegerRangeType::__set_state([
'min' => 0,
'max' => 4294967295,
]),
2 =>
PHPStan\Type\StringType::__set_state([]),
3 =>
PHPStan\Type\StringType::__set_state([]),
],
'optionalKeys' => [],
]
),
],
],
]; |
thank you. if it is reproducible, could you provide a failling test-case? |
I'm not sure I fully understand how it works. Depending on in what class I put my query, sometime I got all the informations in the cache (like the result I put in my previous comment) and sometime it only contains [
'SELECT * FROM conditions WHERE id = \'1\'' =>
array (
'error' => NULL,
)
] Is it because my table does not contains data for ID 1, so the result is empty? |
In some case, I think the wrong placeholder is used like here if (null !== $sequence['conditionId']) {
$condition = $this->dbal->fetchAssociative(
'SELECT * FROM conditions WHERE id = ?',
[$sequence['conditionId']]
);
} In the cache, I get 'SELECT * FROM conditions WHERE id = NULL' =>
array (
'error' => NULL,
), but its not accurate, |
whats exact the type of whats exact the type of |
I tried reproducing your case in #446 maybe you can fiddle with it, until it looks more like your real world case (and actually fails). |
public function cloneSequence(int $sequenceId): int
{
$sequence = $this->dbal->fetchAssociative('SELECT * FROM dialog_sequences WHERE id = ?', [$sequenceId]);
// ...
}
I tried narrowing the type of the parameter. It seems that the private function foo(mixed $conditionId): void
{
if (null !== $conditionId) {
$condition = $this->dbal->fetchAssociative(
'SELECT id, argument3 FROM conditions WHERE id = ?',
[$conditionId]
);
}
// ...
} In the phpstan-dba.cache I got <? [
'SELECT id, argument3 FROM conditions WHERE id = NULL' =>
array (
'error' => NULL,
),
] |
even with could you try to build a repo with a minimal reproducer? |
I try editing your test case, but it's marked as skipped by phpunit and I cant find what is causing the skip public function bug394(mixed $conditionId)
{
if ($conditionId !== null) {
$query = 'SELECT email, adaid FROM ada WHERE adaid = ?';
$fetchResult = $this->conn->fetchAssociative($query, [$conditionId]);
assertType('array{email: string, adaid: int<-32768, 32767>}|false', $fetchResult);
}
} |
@staabm The issue seems to come from BONUS : You can switch the type of the parameter 'SELECT id, name, \'request2\' FROM my_table WHERE id = NULL' |
In I wanted to make a PR, but I can't get XDebug to work with PHPStan. I tried with |
thx for the investigation. I am fine with dropping |
I think this one was fixed with recent releases. can you confirm? |
I just tried it and it looks good |
thx |
I ran into a PHPStan error on the following code: https://phpstan.org/r/0a244b72-7cea-4746-8d69-26b17871fd62
After installing this package, I was still getting the same error, but noticed the cache file also wasn't being generated, so I figured something must not be getting recognized. After poking around a little bit, I changed my
fetchData
method to use the connection to immediately fetch:Now I did notice the cache file got generated, but I'm still getting the original error.
I'm guessing this package does not get triggered when fetching from the Statement result?
Secondly, am I doing something wrong that my DB isn't getting properly analyzed? I find it a little strange that a date is being used as the example email.
The text was updated successfully, but these errors were encountered: