Skip to content
119 changes: 6 additions & 113 deletions .phpstan-dba-mysqli.cache

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions .phpstan-dba-pdo-mysql.cache

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/QueryReflection/RecordingQueryReflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public function validateQueryString(string $queryString): ?Error
$queryString,
$error
);
} else {
$this->reflectionCache->putValidationSuccess(
$queryString,
);
}

return $error;
Expand Down
49 changes: 43 additions & 6 deletions src/QueryReflection/ReflectionCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@
use staabm\PHPStanDba\CacheNotPopulatedException;
use staabm\PHPStanDba\DbaException;
use staabm\PHPStanDba\Error;
use staabm\PHPStanDba\Valid;
use const LOCK_EX;

final class ReflectionCache
{
private const SCHEMA_VERSION = 'v12-new-cache5';
private const SCHEMA_VERSION = 'v13-valid-query';

private string $cacheFile;

/**
* @var array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
* @var array<string, array{error?: null|Error|Valid, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
*/
private array $records = [];

/**
* @var array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
* @var array<string, array{error?: null|Error|Valid, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
*/
private array $changes = [];

Expand Down Expand Up @@ -99,7 +100,7 @@ public function setSchemaHash(string $hash): void
}

/**
* @return array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
* @return array<string, array{error?: null|Valid|Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
*/
Comment thread
staabm marked this conversation as resolved.
private function lazyReadRecords()
{
Expand Down Expand Up @@ -225,6 +226,17 @@ public function persist(): void
}
}

public function contains(string $queryString): bool
{
$records = $this->lazyReadRecords();

if (! \array_key_exists($queryString, $records)) {
return false;
}

return true;
}

public function hasValidationError(string $queryString): bool
{
$records = $this->lazyReadRecords();
Expand All @@ -234,7 +246,10 @@ public function hasValidationError(string $queryString): bool
}

$cacheEntry = $this->records[$queryString];
return \array_key_exists('error', $cacheEntry);

return \array_key_exists('error', $cacheEntry)
&& $cacheEntry['error'] instanceof Error
;
}

/**
Expand All @@ -257,6 +272,10 @@ public function getValidationError(string $queryString): ?Error
return null;
}

if (! $cacheEntry['error'] instanceof Error) {
return null;
}

return $cacheEntry['error'];
}

Expand All @@ -277,6 +296,24 @@ public function putValidationError(string $queryString, Error $error): void
unset($this->records[$queryString]['result']);
}

public function putValidationSuccess(string $queryString): void
{
$records = $this->lazyReadRecords();

if (! \array_key_exists($queryString, $records)) {
$this->changes[$queryString] = $this->records[$queryString] = [];
$this->cacheIsDirty = true;
}

if (
! \array_key_exists('error', $this->records[$queryString])
|| ! $this->records[$queryString]['error'] instanceof Valid
) {
$this->changes[$queryString]['error'] = $this->records[$queryString]['error'] = new Valid();
$this->cacheIsDirty = true;
}
}

/**
* @param QueryReflector::FETCH_TYPE* $fetchType
*/
Expand Down Expand Up @@ -310,7 +347,7 @@ public function getResultType(string $queryString, int $fetchType): ?Type
}

$cacheEntry = $this->records[$queryString];
if (\array_key_exists('error', $cacheEntry)) {
if (\array_key_exists('error', $cacheEntry) && $cacheEntry['error'] instanceof Error) {
return null;
}

Expand Down
5 changes: 2 additions & 3 deletions src/QueryReflection/ReplayAndRecordingQueryReflector.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@ public function validateQueryString(string $queryString): ?Error
return $this->createRecordingReflector()->validateQueryString($queryString);
}

$error = $this->replayReflector->validateQueryString($queryString);
if (null !== $error) {
return $error;
if ($this->reflectionCache->contains($queryString)) {
return $this->replayReflector->validateQueryString($queryString);
}

return $this->createRecordingReflector()->validateQueryString($queryString);
Expand Down
5 changes: 3 additions & 2 deletions src/QueryReflection/TypeSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Type;
use staabm\PHPStanDba\Error;
use staabm\PHPStanDba\Valid;

final class TypeSerializer
{
Expand All @@ -17,8 +18,8 @@ final class TypeSerializer
private ?TypeStringResolver $typeStringResolver = null;

/**
* @param array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}> $records
* @return array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?array<string>>}>
* @param array<string, array{error?: null|Valid|Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}> $records
* @return array<string, array{error?: null|Valid|Error, result?: array<QueryReflector::FETCH_TYPE*, ?array<string>>}>
*/
public function serialize(array $records): array
{
Expand Down
16 changes: 16 additions & 0 deletions src/Valid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace staabm\PHPStanDba;

final class Valid
{
/**
* @param array{} $array
*/
public static function __set_state(array $array): self
{
return new self();
}
}
26 changes: 26 additions & 0 deletions tests/QueryReflection/ReflectionCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ public function testPutResultType(): void
$cache = ReflectionCache::create(self::CACHE_FILE);
self::assertFalse($cache->hasValidationError($query));
self::assertFalse($cache->hasResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::assertFalse($cache->contains($query));

$cache->putResultType($query, QueryReflector::FETCH_TYPE_BOTH, new IntegerType());
self::assertTrue($cache->hasResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::assertInstanceOf(IntegerType::class, $cache->getResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::assertTrue($cache->contains($query));

self::assertFalse($cache->hasValidationError($query));
self::assertNull($cache->getValidationError($query));
Expand All @@ -39,16 +41,40 @@ public function testPutValidationError(): void
$cache = ReflectionCache::create(self::CACHE_FILE);
self::assertFalse($cache->hasValidationError($query));
self::assertFalse($cache->hasResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::assertFalse($cache->contains($query));

$error = new Error('some error', 123);
$cache->putValidationError($query, $error);
self::assertTrue($cache->hasValidationError($query));
self::assertSame($error, $cache->getValidationError($query));
self::assertTrue($cache->contains($query));

self::assertFalse($cache->hasResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::assertNull($cache->getResultType($query, QueryReflector::FETCH_TYPE_BOTH));
}

public function testPutValidationSuccess(): void
{
$query = 'SELECT 1 FROM abc WHERE x = 6';

$cache = ReflectionCache::create(self::CACHE_FILE);
self::assertFalse($cache->hasValidationError($query));
self::assertFalse($cache->hasResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::assertFalse($cache->contains($query));

$cache->putValidationSuccess($query);
self::assertFalse($cache->hasValidationError($query));
self::assertNull($cache->getValidationError($query));
self::assertTrue($cache->contains($query));

self::assertFalse($cache->hasResultType($query, QueryReflector::FETCH_TYPE_BOTH));
try {
self::assertNull($cache->getResultType($query, QueryReflector::FETCH_TYPE_BOTH));
self::fail();
} catch (CacheNotPopulatedException $e) {
}
}

public function testCacheNotPopulated(): void
{
$query = 'SELECT 1 FROM abc WHERE x = 6';
Expand Down
Loading