Skip to content

Commit 07eb401

Browse files
committed
Add some code.
1 parent 6af6578 commit 07eb401

8 files changed

+751
-1
lines changed

composer.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,10 @@
99
"email": "[email protected]"
1010
}
1111
],
12-
"require": {}
12+
"require": {},
13+
"autoload": {
14+
"psr-4": {
15+
"Weebly\\PHPStan\\Laravel": "src/"
16+
}
17+
}
1318
}

src/BuilderMethodExtension.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Weebly\PHPStan\Laravel;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Database\Query\Builder as QueryBuilder;
8+
use PHPStan\Broker\Broker;
9+
use PHPStan\Reflection\BrokerAwareExtension;
10+
use PHPStan\Reflection\ClassReflection;
11+
use PHPStan\Reflection\MethodsClassReflectionExtension;
12+
use PHPStan\Reflection\MethodReflection;
13+
14+
class BuilderMethodExtension implements MethodsClassReflectionExtension, BrokerAwareExtension
15+
{
16+
/**
17+
* @var \PHPStan\Broker\Broker
18+
*/
19+
private $broker;
20+
21+
/**
22+
* @var \PHPStan\Reflection\MethodReflection[]
23+
*/
24+
private $methods = [];
25+
26+
/**
27+
* @var \Weebly\PHPStan\Laravel\MethodReflectionFactory
28+
*/
29+
private $methodReflectionFactory;
30+
31+
/**
32+
* BuilderMethodExtension constructor.
33+
*
34+
* @param \Weebly\PHPStan\Laravel\MethodReflectionFactory $methodReflectionFactory
35+
*/
36+
public function __construct(MethodReflectionFactory $methodReflectionFactory)
37+
{
38+
$this->methodReflectionFactory = $methodReflectionFactory;
39+
}
40+
41+
/**
42+
* @inheritdoc
43+
*/
44+
public function setBroker(Broker $broker)
45+
{
46+
$this->broker = $broker;
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
53+
{
54+
if ($classReflection->isSubclassOf(Model::class) && !isset($this->methods[$classReflection->getName()])) {
55+
$builder = $this->broker->getClass(Builder::class);
56+
$this->methods[$classReflection->getName()] = $this->createWrappedMethods($classReflection, $builder);
57+
58+
$queryBuilder = $this->broker->getClass(QueryBuilder::class);
59+
$this->methods[$classReflection->getName()] += $this->createMethods($classReflection, $queryBuilder);
60+
}
61+
62+
if ($classReflection->getName() === Builder::class && !isset($this->methods[Builder::class])) {
63+
$queryBuilder = $this->broker->getClass(QueryBuilder::class);
64+
$this->methods[Builder::class] = $this->createMethods($classReflection, $queryBuilder);
65+
}
66+
67+
return isset($this->methods[$classReflection->getName()][$methodName]);
68+
}
69+
70+
/**
71+
* @inheritdoc
72+
*/
73+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
74+
{
75+
return $this->methods[$classReflection->getName()][$methodName];
76+
}
77+
78+
/**
79+
* @param \PHPStan\Reflection\ClassReflection $classReflection
80+
* @param \PHPStan\Reflection\ClassReflection $queryBuilder
81+
*
82+
* @return \PHPStan\Reflection\MethodReflection[]
83+
*/
84+
private function createMethods(ClassReflection $classReflection, ClassReflection $queryBuilder): array
85+
{
86+
$methods = [];
87+
foreach ($queryBuilder->getNativeReflection()->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
88+
if ($method->isStatic()) {
89+
continue;
90+
}
91+
92+
$methods[$method->getName()] = $this->methodReflectionFactory->create($classReflection, $method);
93+
}
94+
95+
return $methods;
96+
}
97+
98+
/**
99+
* @param ClassReflection $classReflection
100+
* @param \PHPStan\Reflection\ClassReflection $builder
101+
*
102+
* @return \PHPStan\Reflection\MethodReflection[]
103+
*
104+
* @throws \PHPStan\ShouldNotHappenException
105+
*/
106+
private function createWrappedMethods(ClassReflection $classReflection, ClassReflection $builder): array
107+
{
108+
$methods = [];
109+
foreach ($builder->getNativeReflection()->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
110+
$methods[$method->getName()] = $this->methodReflectionFactory->create(
111+
$classReflection,
112+
$method,
113+
ReflectionMethodAlwaysStatic::class
114+
);
115+
}
116+
117+
return $methods;
118+
}
119+
}

src/FacadeMethodExtension.php

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Weebly\PHPStan\Laravel;
4+
5+
use Illuminate\Auth\AuthManager;
6+
use Illuminate\Broadcasting\BroadcastManager;
7+
use Illuminate\Support\Facades\Facade;
8+
use PHPStan\Broker\Broker;
9+
use PHPStan\Reflection\BrokerAwareExtension;
10+
use PHPStan\Reflection\ClassReflection;
11+
use PHPStan\Reflection\MethodsClassReflectionExtension;
12+
use PHPStan\Reflection\MethodReflection;
13+
14+
class FacadeMethodExtension implements MethodsClassReflectionExtension, BrokerAwareExtension
15+
{
16+
/**
17+
* @var \PHPStan\Broker\Broker
18+
*/
19+
private $broker;
20+
21+
/**
22+
* @var string[]
23+
*/
24+
private $extensions = [
25+
AuthManager::class => 'guard',
26+
BroadcastManager::class => 'driver',
27+
];
28+
29+
/**
30+
* @var \PHPStan\Reflection\MethodReflection[]
31+
*/
32+
private $methods = [];
33+
34+
/**
35+
* @var \Weebly\PHPStan\Laravel\MethodReflectionFactory
36+
*/
37+
private $methodReflectionFactory;
38+
39+
/**
40+
* FacadeMethodExtension constructor.
41+
*
42+
* @param \Weebly\PHPStan\Laravel\MethodReflectionFactory $methodReflectionFactory
43+
*/
44+
public function __construct(MethodReflectionFactory $methodReflectionFactory)
45+
{
46+
$this->methodReflectionFactory = $methodReflectionFactory;
47+
}
48+
49+
/**
50+
* @inheritdoc
51+
*/
52+
public function setBroker(Broker $broker)
53+
{
54+
$this->broker = $broker;
55+
}
56+
57+
/**
58+
* @inheritdoc
59+
*/
60+
public function hasMethod(ClassReflection $classReflection, string $methodName): bool
61+
{
62+
if ($classReflection->isSubclassOf(Facade::class)) {
63+
if (!isset($this->methods[$classReflection->getName()])) {
64+
65+
/** @var \Illuminate\Support\Facades\Facade $class */
66+
$class = $classReflection->getName();
67+
$instance = $class::getFacadeRoot();
68+
69+
$instanceReflection = $this->broker->getClass(get_class($instance));
70+
$this->methods[$classReflection->getName()] = $this->createMethods($classReflection, $instanceReflection);
71+
72+
if (isset($this->extensions[$instanceReflection->getName()])) {
73+
$extensionMethod = $this->extensions[$instanceReflection->getName()];
74+
$extensionReflection = $this->broker->getClass(get_class($instance->$extensionMethod()));
75+
$this->methods[$classReflection->getName()] += $this->createMethods($classReflection, $extensionReflection);
76+
}
77+
}
78+
}
79+
80+
return isset($this->methods[$classReflection->getName()][$methodName]);
81+
}
82+
83+
/**
84+
* @inheritdoc
85+
*/
86+
public function getMethod(ClassReflection $classReflection, string $methodName): MethodReflection
87+
{
88+
return $this->methods[$classReflection->getName()][$methodName];
89+
}
90+
91+
/**
92+
* @param \PHPStan\Reflection\ClassReflection $classReflection
93+
* @param \PHPStan\Reflection\ClassReflection $instance
94+
*
95+
* @return \PHPStan\Reflection\MethodReflection[]
96+
*
97+
* @throws \PHPStan\ShouldNotHappenException
98+
*/
99+
private function createMethods(ClassReflection $classReflection, ClassReflection $instance): array
100+
{
101+
$methods = [];
102+
foreach ($instance->getNativeReflection()->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
103+
$methods[$method->getName()] = $this->methodReflectionFactory->create(
104+
$classReflection,
105+
$method,
106+
ReflectionMethodAlwaysStatic::class
107+
);
108+
}
109+
110+
return $methods;
111+
}
112+
}

src/HelpersReturnTypeExtension.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Weebly\PHPStan\Laravel;
4+
5+
use PhpParser\Node\Expr\ClassConstFetch;
6+
use PhpParser\Node\Expr\FuncCall;
7+
use PhpParser\Node\Scalar\String_;
8+
use PHPStan\Analyser\Scope;
9+
use PHPStan\Reflection\FunctionReflection;
10+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
11+
use PHPStan\Type\MixedType;
12+
use PHPStan\Type\NullType;
13+
use PHPStan\Type\ObjectType;
14+
use PHPStan\Type\Type;
15+
16+
class HelpersReturnTypeExtension implements DynamicFunctionReturnTypeExtension
17+
{
18+
/**
19+
* @var string[]
20+
*/
21+
private $helpers = [
22+
'app',
23+
'validator',
24+
'view',
25+
];
26+
27+
/**
28+
* @inheritdoc
29+
*/
30+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
31+
{
32+
return in_array($functionReflection->getName(), $this->helpers);
33+
}
34+
35+
/**
36+
* @inheritdoc
37+
*/
38+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type
39+
{
40+
switch ($functionReflection->getName()) {
41+
case 'app':
42+
if (empty($functionCall->args) || $scope->getType($functionCall->args[0]->value) instanceof NullType) {
43+
return new ObjectType(\Illuminate\Foundation\Application::class);
44+
}
45+
46+
$arg1 = $functionCall->args[0];
47+
48+
if ($arg1->value instanceof ClassConstFetch) {
49+
return new ObjectType((string) $functionCall->args[0]->value->class);
50+
}
51+
52+
if ($arg1->value instanceof String_ && class_exists($functionCall->args[0]->value->value)) {
53+
return new ObjectType($functionCall->args[0]->value->value);
54+
}
55+
56+
return new MixedType();
57+
break;
58+
59+
case 'validator':
60+
if (empty($functionCall->args)) {
61+
return new ObjectType(\Illuminate\Contracts\Validation\Factory::class);
62+
}
63+
64+
return new ObjectType(\Illuminate\Contracts\Validation\Validator::class);
65+
break;
66+
67+
case 'view':
68+
if (empty($functionCall->args)) {
69+
return new ObjectType(\Illuminate\Contracts\View\Factory::class);
70+
}
71+
72+
return new ObjectType(\Illuminate\View\View::class);
73+
break;
74+
}
75+
76+
return new MixedType();
77+
}
78+
}

0 commit comments

Comments
 (0)