Skip to content

Commit 6da4186

Browse files
committed
fix(phpstan): added custom pgsql enhancements to phpstan rule engine
# Conflicts: # phpstan-extension.neon
1 parent bea4d6d commit 6da4186

8 files changed

+357
-0
lines changed

phpstan-extension.neon

+12
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
11
services:
2+
- class: Tpetry\PostgresqlEnhanced\Support\Phpstan\QueryBuilderExtension
3+
tags:
4+
- phpstan.broker.methodsClassReflectionExtension
5+
- class: Tpetry\PostgresqlEnhanced\Support\Phpstan\SchemaBlueprintExtension
6+
tags:
7+
- phpstan.broker.methodsClassReflectionExtension
8+
- class: Tpetry\PostgresqlEnhanced\Support\Phpstan\SchemaColumnDefinitionExtension
9+
tags:
10+
- phpstan.broker.methodsClassReflectionExtension
11+
- class: Tpetry\PostgresqlEnhanced\Support\Phpstan\SchemaIndexDefinitionExtension
12+
tags:
13+
- phpstan.broker.methodsClassReflectionExtension

src/Schema/BlueprintIndex.php

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Illuminate\Support\Str;
99
use RuntimeException;
1010

11+
/**
12+
* @method \Illuminate\Database\Schema\IndexDefinition uniqueIndex(string|string[] $columns, ?string $name = null, ?string $algorithm = null)
13+
*/
1114
trait BlueprintIndex
1215
{
1316
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\PostgresqlEnhanced\Support\Phpstan;
6+
7+
use Illuminate\Contracts\Database\Query\Builder as BuilderContract;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Reflection\MethodsClassReflectionExtension;
11+
use PHPStan\Reflection\ReflectionProvider;
12+
use Tpetry\PostgresqlEnhanced\Query\Builder;
13+
14+
class QueryBuilderExtension implements MethodsClassReflectionExtension
15+
{
16+
public function __construct(
17+
private ReflectionProvider $reflectionProvider,
18+
) {
19+
}
20+
21+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
22+
{
23+
return $this->reflectionProvider->getClass(Builder::class)->getNativeMethod($methodName);
24+
}
25+
26+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
27+
{
28+
if (BuilderContract::class !== $classReflection->getName() && !$classReflection->implementsInterface(BuilderContract::class)) {
29+
return false;
30+
}
31+
32+
return $this->reflectionProvider->getClass(Builder::class)->hasNativeMethod($methodName);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\PostgresqlEnhanced\Support\Phpstan;
6+
7+
use Illuminate\Database\Schema\Blueprint as BaseBlueprint;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\Reflection\MethodsClassReflectionExtension;
11+
use PHPStan\Reflection\ReflectionProvider;
12+
use Tpetry\PostgresqlEnhanced\Schema\Blueprint;
13+
14+
class SchemaBlueprintExtension implements MethodsClassReflectionExtension
15+
{
16+
public function __construct(
17+
private ReflectionProvider $reflectionProvider,
18+
) {
19+
}
20+
21+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
22+
{
23+
return $this->reflectionProvider->getClass(Blueprint::class)->getNativeMethod($methodName);
24+
}
25+
26+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
27+
{
28+
if (BaseBlueprint::class !== $classReflection->getName()) {
29+
return false;
30+
}
31+
32+
return $this->reflectionProvider->getClass(Blueprint::class)->hasNativeMethod($methodName);
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\PostgresqlEnhanced\Support\Phpstan;
6+
7+
use Illuminate\Database\Schema\ColumnDefinition;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Reflection\FunctionVariant;
10+
use PHPStan\Reflection\MethodReflection;
11+
use PHPStan\Reflection\MethodsClassReflectionExtension;
12+
use PHPStan\Type\Generic\TemplateTypeMap;
13+
use PHPStan\Type\ObjectType;
14+
use PHPStan\Type\StringType;
15+
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedMethod;
16+
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedParameter;
17+
18+
class SchemaColumnDefinitionExtension implements MethodsClassReflectionExtension
19+
{
20+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
21+
{
22+
return $this->getCompressionMethod($classReflection);
23+
}
24+
25+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
26+
{
27+
if (ColumnDefinition::class !== $classReflection->getName()) {
28+
return false;
29+
}
30+
31+
return \in_array($methodName, ['compression']);
32+
}
33+
34+
private function getCompressionMethod(ClassReflection $classReflection): MethodReflection
35+
{
36+
$parameters = [new ReflectedParameter('algorithm', new StringType())];
37+
$returnType = new ObjectType(ColumnDefinition::class);
38+
39+
return new ReflectedMethod(
40+
classReflection: $classReflection,
41+
name: 'compression',
42+
variants: [
43+
new FunctionVariant(TemplateTypeMap::createEmpty(), null, $parameters, false, $returnType),
44+
],
45+
);
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\PostgresqlEnhanced\Support\Phpstan;
6+
7+
use Illuminate\Contracts\Database\Query\Builder as BuilderContract;
8+
use Illuminate\Database\Query\Builder as BuilderQuery;
9+
use Illuminate\Database\Schema\IndexDefinition;
10+
use PHPStan\Reflection\ClassReflection;
11+
use PHPStan\Reflection\FunctionVariant;
12+
use PHPStan\Reflection\MethodReflection;
13+
use PHPStan\Reflection\MethodsClassReflectionExtension;
14+
use PHPStan\Type\ArrayType;
15+
use PHPStan\Type\BooleanType;
16+
use PHPStan\Type\CallableType;
17+
use PHPStan\Type\FloatType;
18+
use PHPStan\Type\Generic\TemplateTypeMap;
19+
use PHPStan\Type\IntegerType;
20+
use PHPStan\Type\ObjectType;
21+
use PHPStan\Type\StringType;
22+
use PHPStan\Type\TypeCombinator;
23+
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedMethod;
24+
use Tpetry\PostgresqlEnhanced\Support\Phpstan\Values\ReflectedParameter;
25+
use UnexpectedValueException;
26+
27+
class SchemaIndexDefinitionExtension implements MethodsClassReflectionExtension
28+
{
29+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
30+
{
31+
return match ($methodName) {
32+
'include' => new ReflectedMethod($classReflection, $methodName, [
33+
$this->createFunctionVariant([new ReflectedParameter('columns', new StringType())]),
34+
$this->createFunctionVariant([new ReflectedParameter('columns', new ArrayType(new IntegerType(), new StringType()))]),
35+
]),
36+
'weight' => new ReflectedMethod($classReflection, $methodName, [
37+
$this->createFunctionVariant([new ReflectedParameter('labels', new ArrayType(new IntegerType(), new StringType()))]),
38+
]),
39+
'where' => new ReflectedMethod($classReflection, $methodName, [
40+
$this->createFunctionVariant([new ReflectedParameter('columns', new StringType())]),
41+
$this->createFunctionVariant([new ReflectedParameter('columns', new CallableType([new ReflectedParameter('builder', new ObjectType(BuilderContract::class))], new ObjectType(BuilderContract::class), false))]),
42+
$this->createFunctionVariant([new ReflectedParameter('columns', new CallableType([new ReflectedParameter('builder', new ObjectType(BuilderQuery::class))], new ObjectType(BuilderContract::class), false))]),
43+
]),
44+
'with' => new ReflectedMethod($classReflection, $methodName, [
45+
$this->createFunctionVariant([new ReflectedParameter('options', new ArrayType(new StringType(), TypeCombinator::union(new BooleanType(), new FloatType(), new IntegerType(), new StringType())))]),
46+
]),
47+
default => throw new UnexpectedValueException("'{$methodName}' is not defined for IndexDefinition."),
48+
};
49+
}
50+
51+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
52+
{
53+
if (IndexDefinition::class !== $classReflection->getName()) {
54+
return false;
55+
}
56+
57+
return \in_array($methodName, ['include', 'weight', 'where', 'with']);
58+
}
59+
60+
/**
61+
* @param array<int, \PHPStan\Reflection\ParameterReflection> $parameters
62+
*/
63+
private function createFunctionVariant(array $parameters): FunctionVariant
64+
{
65+
return new FunctionVariant(
66+
templateTypeMap: TemplateTypeMap::createEmpty(),
67+
resolvedTemplateTypeMap: null,
68+
parameters: $parameters,
69+
isVariadic: false,
70+
returnType: new ObjectType(IndexDefinition::class),
71+
);
72+
}
73+
74+
private function createReflectedMethod(ClassReflection $classReflection, string $methodName, array $variants): ReflectedMethod
75+
{
76+
return new ReflectedMethod(
77+
classReflection: $classReflection,
78+
name: $methodName,
79+
variants: [
80+
$this->createFunctionVariant([new ReflectedParameter('columns', new StringType())]),
81+
$this->createFunctionVariant([new ReflectedParameter('columns', new ArrayType(new IntegerType(), new StringType()))]),
82+
],
83+
);
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\PostgresqlEnhanced\Support\Phpstan\Values;
6+
7+
use PHPStan\Reflection\ClassMemberReflection;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Reflection\MethodReflection;
10+
use PHPStan\TrinaryLogic;
11+
use PHPStan\Type\Type;
12+
13+
class ReflectedMethod implements MethodReflection
14+
{
15+
/**
16+
* @param \PHPStan\Reflection\FunctionVariant[] $variants
17+
*/
18+
public function __construct(
19+
private ClassReflection $classReflection,
20+
private string $name,
21+
private array $variants,
22+
) {
23+
}
24+
25+
public function getDeclaringClass(): ClassReflection
26+
{
27+
return $this->classReflection;
28+
}
29+
30+
public function getDeprecatedDescription(): ?string
31+
{
32+
return null;
33+
}
34+
35+
public function getDocComment(): ?string
36+
{
37+
return null;
38+
}
39+
40+
public function getName(): string
41+
{
42+
return $this->name;
43+
}
44+
45+
public function getPrototype(): ClassMemberReflection
46+
{
47+
return $this;
48+
}
49+
50+
public function getThrowType(): ?Type
51+
{
52+
return null;
53+
}
54+
55+
public function getVariants(): array
56+
{
57+
return $this->variants;
58+
}
59+
60+
public function hasSideEffects(): TrinaryLogic
61+
{
62+
return TrinaryLogic::createMaybe();
63+
}
64+
65+
public function isDeprecated(): TrinaryLogic
66+
{
67+
return TrinaryLogic::createNo();
68+
}
69+
70+
public function isFinal(): TrinaryLogic
71+
{
72+
return TrinaryLogic::createNo();
73+
}
74+
75+
public function isInternal(): TrinaryLogic
76+
{
77+
return TrinaryLogic::createNo();
78+
}
79+
80+
public function isPrivate(): bool
81+
{
82+
return false;
83+
}
84+
85+
public function isPublic(): bool
86+
{
87+
return true;
88+
}
89+
90+
public function isStatic(): bool
91+
{
92+
return false;
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tpetry\PostgresqlEnhanced\Support\Phpstan\Values;
6+
7+
use PHPStan\Reflection\ParameterReflection;
8+
use PHPStan\Reflection\PassedByReference;
9+
use PHPStan\Type\Type;
10+
11+
class ReflectedParameter implements ParameterReflection
12+
{
13+
public function __construct(
14+
private string $name,
15+
private Type $type,
16+
) {
17+
}
18+
19+
public function getDefaultValue(): ?Type
20+
{
21+
return null;
22+
}
23+
24+
public function getName(): string
25+
{
26+
return $this->name;
27+
}
28+
29+
public function getType(): Type
30+
{
31+
return $this->type;
32+
}
33+
34+
public function isOptional(): bool
35+
{
36+
return false;
37+
}
38+
39+
public function isVariadic(): bool
40+
{
41+
return false;
42+
}
43+
44+
public function passedByReference(): PassedByReference
45+
{
46+
return PassedByReference::createNo();
47+
}
48+
}

0 commit comments

Comments
 (0)