diff --git a/.travis.yml b/.travis.yml index 1e403bf..8d7f0fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: php php: - - 7.0 - 7.1 - 7.2 diff --git a/bootstrap.php b/bootstrap.php index 04d9f90..aa96b75 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -1,6 +1,8 @@ broker = $broker; } @@ -70,6 +70,10 @@ public function hasMethod(ClassReflection $classReflection, string $methodName): $this->methods[$classReflection->getName()] += $this->createMethods($classReflection, $queryBuilder); } + if ($classReflection->isSubclassOf(Model::class)) { + $this->methods[$classReflection->getName()] += $this->createModelMethod($classReflection, $methodName); + } + if ($classReflection->getName() === Builder::class && !isset($this->methods[Builder::class])) { $queryBuilder = $this->broker->getClass(QueryBuilder::class); $this->methods[Builder::class] = $this->createMethods($classReflection, $queryBuilder); @@ -127,4 +131,29 @@ private function createWrappedMethods(ClassReflection $classReflection, ClassRef return $methods; } + + /** + * @param ClassReflection $classReflection + * + * @return \PHPStan\Reflection\MethodReflection + */ + private function createModelMethod(ClassReflection $classReflection, string $methodName): array + { + if (strpos($methodName, 'where') !== 0) { + return []; + } + + $propertyName = strtolower(substr($methodName, 5, 1)) . substr($methodName, 6); + if (!$classReflection->hasProperty($propertyName)) { + return []; + } + + return [ + $methodName => new CustomMethod( + $classReflection, + $methodName, + $classReflection->getName() + ) + ]; + } } diff --git a/src/CustomMethod.php b/src/CustomMethod.php new file mode 100644 index 0000000..3461675 --- /dev/null +++ b/src/CustomMethod.php @@ -0,0 +1,78 @@ +class = $class; + $this->name = $name; + $this->returnType = $returnType; + } + + public function getDeclaringClass(): ClassReflection + { + return $this->class; + } + + public function getPrototype(): MethodReflection + { + return $this; + } + + public function isStatic(): bool + { + return true; + } + + public function isPrivate(): bool + { + return false; + } + + public function isPublic(): bool + { + return true; + } + + public function getName(): string + { + return $this->name; + } + + /** + * @return \PHPStan\Reflection\ParameterReflection[] + */ + public function getParameters(): array + { + return []; + } + + public function isVariadic(): bool + { + return true; + } + + public function getReturnType(): Type + { + if (is_array($this->returnType)) { + return new UnionType($this->returnType); + } + + return new ObjectType($this->returnType); + } +} diff --git a/src/FacadeMethodExtension.php b/src/FacadeMethodExtension.php index 5397a9d..0e36d37 100644 --- a/src/FacadeMethodExtension.php +++ b/src/FacadeMethodExtension.php @@ -58,7 +58,7 @@ public function __construct(MethodReflectionFactory $methodReflectionFactory, An /** * @inheritdoc */ - public function setBroker(Broker $broker) + public function setBroker(Broker $broker): void { $this->broker = $broker; } diff --git a/src/MacroMethodExtension.php b/src/MacroMethodExtension.php index 4ec45a6..4e8b007 100644 --- a/src/MacroMethodExtension.php +++ b/src/MacroMethodExtension.php @@ -50,7 +50,7 @@ public function __construct(PhpMethodReflectionFactory $methodReflectionFactory, /** * @inheritdoc */ - public function setBroker(Broker $broker) + public function setBroker(Broker $broker): void { $this->broker = $broker; } diff --git a/src/MethodReflectionFactory.php b/src/MethodReflectionFactory.php index f946b67..6fbd143 100644 --- a/src/MethodReflectionFactory.php +++ b/src/MethodReflectionFactory.php @@ -8,6 +8,7 @@ use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\Php\PhpMethodReflectionFactory; +use PHPStan\Reflection\Php\NativeBuiltinMethodReflection; use PHPStan\Type\FileTypeMapper; use PHPStan\Type\Type; @@ -48,6 +49,10 @@ public function create(ClassReflection $classReflection, \ReflectionMethod $meth { $phpDocParameterTypes = []; $phpDocReturnType = null; + $phpDocThrowType = null; + $phpDocIsDeprecated = false; + $phpDocIsInternal = false; + $phpDocIsFinal = false; if ($methodReflection->getDocComment() !== false) { $phpDocBlock = PhpDocBlock::resolvePhpDocBlockForMethod( Broker::getInstance(), @@ -60,23 +65,36 @@ public function create(ClassReflection $classReflection, \ReflectionMethod $meth $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc( $phpDocBlock->getFile(), $phpDocBlock->getClass(), + null, $phpDocBlock->getDocComment() ); + $phpDocParameterTypes = array_map(function (ParamTag $tag): Type { return $tag->getType(); }, $resolvedPhpDoc->getParamTags()); $phpDocReturnType = $resolvedPhpDoc->getReturnTag() !== null ? $resolvedPhpDoc->getReturnTag()->getType() : null; + $phpDocThrowType = $resolvedPhpDoc->getThrowsTag() !== null ? $resolvedPhpDoc->getThrowsTag()->getType() : null; + $phpDocIsDeprecated = $resolvedPhpDoc->isDeprecated(); + $phpDocIsInternal = $resolvedPhpDoc->isInternal(); + $phpDocIsFinal = $resolvedPhpDoc->isFinal(); } if ($methodWrapper) { $methodReflection = new $methodWrapper($methodReflection); + } else { + $methodReflection = new NativeBuiltinMethodReflection($methodReflection); } return $this->methodReflectionFactory->create( $classReflection, + null, $methodReflection, $phpDocParameterTypes, - $phpDocReturnType + $phpDocReturnType, + $phpDocThrowType, + $phpDocIsDeprecated, + $phpDocIsInternal, + $phpDocIsFinal ); } } diff --git a/src/ReflectionMethodAlwaysStatic.php b/src/ReflectionMethodAlwaysStatic.php index ab05f2d..98a33a9 100644 --- a/src/ReflectionMethodAlwaysStatic.php +++ b/src/ReflectionMethodAlwaysStatic.php @@ -2,19 +2,11 @@ namespace Weebly\PHPStan\Laravel; -final class ReflectionMethodAlwaysStatic extends \ReflectionMethod -{ - /** - * @param \ReflectionMethod $reflectionMethod - * - * @throws \ReflectionException - */ - public function __construct(\ReflectionMethod $reflectionMethod) - { - parent::__construct($reflectionMethod->getDeclaringClass()->getName(), $reflectionMethod->getName()); - } +use PHPStan\Reflection\Php\NativeBuiltinMethodReflection; - public function isStatic() +final class ReflectionMethodAlwaysStatic extends NativeBuiltinMethodReflection +{ + public function isStatic(): bool { return true; }