Skip to content

Commit

Permalink
Hack new PHPStan 2.x compatible ReflectionCache (#716)
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm authored Nov 17, 2024
1 parent 1cb1435 commit 183d799
Show file tree
Hide file tree
Showing 20 changed files with 1,668 additions and 67,097 deletions.
640 changes: 14 additions & 626 deletions .phpstan-dba-mysqli.cache

Large diffs are not rendered by default.

638 changes: 13 additions & 625 deletions .phpstan-dba-pdo-mysql.cache

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions config/extensions.neon
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,5 @@ services:
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension

-
class: staabm\PHPStanDba\QueryReflection\DIContainerBridge
10 changes: 10 additions & 0 deletions src/Ast/PreviousConnectingVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use staabm\PHPStanDba\QueryReflection\DIContainerBridge;
use function array_pop;

final class PreviousConnectingVisitor extends NodeVisitorAbstract
Expand All @@ -21,6 +22,15 @@ final class PreviousConnectingVisitor extends NodeVisitorAbstract

private ?Node $previous;

// a dummy property to force instantiation of DIContainerBridge
// for use all over the phpstan-dba codebase
private DIContainerBridge $containerBridge; // @phpstan-ignore property.onlyWritten

public function __construct(DIContainerBridge $dummyParameter)
{
$this->containerBridge = $dummyParameter;
}

public function beforeTraverse(array $nodes)
{
$this->stack = [];
Expand Down
33 changes: 33 additions & 0 deletions src/QueryReflection/DIContainerBridge.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace staabm\PHPStanDba\QueryReflection;

use PHPStan\DependencyInjection\Container;

/**
* Utility class to access the PHPStan container from phpstan-dba internal classes which cannot access the DI because of BC.
*
* @internal
*/
final class DIContainerBridge
{
private static Container $container;

public function __construct(Container $container)
{
self::$container = $container;
}

/**
* @phpstan-template T of object
* @phpstan-param class-string<T> $className
* @phpstan-return T
* @return mixed
*/
public static function getByType(string $className): object
{
return self::$container->getByType($className);
}
}
10 changes: 6 additions & 4 deletions src/QueryReflection/ReflectionCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

final class ReflectionCache
{
private const SCHEMA_VERSION = 'v12-new-major';
private const SCHEMA_VERSION = 'v12-new-cache5';

private string $cacheFile;

Expand All @@ -38,9 +38,12 @@ final class ReflectionCache
*/
private static $lockHandle;

private TypeSerializer $typeSerializer;

private function __construct(string $cacheFile)
{
$this->cacheFile = $cacheFile;
$this->typeSerializer = new TypeSerializer();

if (null === self::$lockHandle) {
// prevent parallel phpstan-worker-process from writing into the cache file at the same time
Expand Down Expand Up @@ -169,7 +172,7 @@ private function readCachedRecords(bool $useReadLock): ?array
throw new ShouldNotHappenException();
}

return $cache['records']; // @phpstan-ignore-line
return $this->typeSerializer->unserialize($cache['records']); // @phpstan-ignore-line
}

public function persist(): void
Expand Down Expand Up @@ -203,10 +206,9 @@ public function persist(): void
$cacheContent = '<?php return ' . var_export([
'schemaVersion' => self::SCHEMA_VERSION,
'schemaHash' => $this->schemaHash,
'records' => $newRecords,
'records' => $this->typeSerializer->serialize($newRecords),
'runtimeConfig' => QueryReflection::getRuntimeConfiguration()->toArray(),
], true) . ';';

if (false === file_put_contents($this->cacheFile, $cacheContent, LOCK_EX)) {
throw new DbaException(sprintf('Unable to write cache file "%s"', $this->cacheFile));
}
Expand Down
85 changes: 85 additions & 0 deletions src/QueryReflection/TypeSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

declare(strict_types=1);

namespace staabm\PHPStanDba\QueryReflection;

use PHPStan\PhpDoc\TypeStringResolver;
use PHPStan\PhpDocParser\Printer\Printer;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Type;
use staabm\PHPStanDba\Error;

final class TypeSerializer
{
private ?Printer $printer = null;

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>>}>
*/
public function serialize(array $records): array
{
// serialize types, see https://github.com/phpstan/phpstan/discussions/12046
foreach ($records as &$record) {
if (! array_key_exists('result', $record)) {
continue;
}
$record['result'] = array_map(function (?Type $type) {
if ($type === null) {
return null;
}

return [
'type-description' => $this->getPhpdocPrinter()->print($type->toPhpDocNode()),
];
}, $record['result']);
}

return $records; // @phpstan-ignore return.type
}

/**
* @param array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?array<string>>}> $records
* @return array<string, array{error?: ?Error, result?: array<QueryReflector::FETCH_TYPE*, ?Type>}>
*/
public function unserialize(array $records): array
{
// serialize types, see https://github.com/phpstan/phpstan/discussions/12046
foreach ($records as &$record) {
if (! array_key_exists('result', $record)) {
continue;
}
$record['result'] = array_map(function ($serialized): Type {
if (is_array($serialized) && array_key_exists('type-description', $serialized)) {
try {
return $this->getTypeStringResolver()->resolve($serialized['type-description']);
} catch (\Throwable $e) {
throw new ShouldNotHappenException("unexpected type " . print_r($serialized, true) . ': ' . $e->getMessage());
}
}
throw new ShouldNotHappenException("unexpected type " . print_r($serialized, true));
}, $record['result']);
}

return $records; // @phpstan-ignore return.type
}

private function getPhpdocPrinter(): Printer
{
if ($this->printer === null) {
$this->printer = DIContainerBridge::getByType(Printer::class);
}
return $this->printer;
}

private function getTypeStringResolver(): TypeStringResolver
{
if ($this->typeStringResolver === null) {
$this->typeStringResolver = DIContainerBridge::getByType(TypeStringResolver::class);
}
return $this->typeStringResolver;
}
}
7 changes: 5 additions & 2 deletions src/TypeMapping/MysqliTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ final class MysqliTypeMapper
public function __construct(?DbaApi $dbaApi)
{
$constants = get_defined_constants(true);
if (! array_key_exists('mysqli', $constants) || ! is_array($constants['mysqli'])) {
if (
! array_key_exists('mysqli', $constants)
|| ! is_array($constants['mysqli']) // @phpstan-ignore-line
) {
$constants['mysqli'] = [];
}

Expand All @@ -34,7 +37,7 @@ public function __construct(?DbaApi $dbaApi)
continue;
}

if (! is_string($c)) {
if (! is_string($c)) { // @phpstan-ignore-line
continue;
}

Expand Down
Loading

0 comments on commit 183d799

Please sign in to comment.