Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ services:
class: Syntro\SilverstripePHPStan\Type\DataObjectGetStaticReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
# This adds additional type info to `Versioned::get*()` so that it knows what class
# while be returned when iterating.
-
class: Syntro\SilverstripePHPStan\Type\VersionedGetStaticReturnTypeExtension
tags:
- phpstan.broker.dynamicStaticMethodReturnTypeExtension
# This allows `singleton("File")` calls to understand the exact classes being returned
# by using your configuration. (ie. uses Injector information if it's set)
-
Expand Down
3 changes: 2 additions & 1 deletion src/Type/DataListReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Syntro\SilverstripePHPStan\Type;

use Exception;
use PHPStan\Type\TypeCombinator;
use Syntro\SilverstripePHPStan\ClassHelper;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Reflection\MethodReflection;
Expand Down Expand Up @@ -122,7 +123,7 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method
case 'byID':
case 'first':
case 'last':
return $type->getIterableValueType();
return TypeCombinator::addNull($type->getIterableValueType());

default:
throw new Exception('Unhandled method call: '.$name);
Expand Down
7 changes: 4 additions & 3 deletions src/Type/DataObjectGetStaticReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Syntro\SilverstripePHPStan\Type;

use PHPStan\Type\TypeCombinator;
use Syntro\SilverstripePHPStan\ClassHelper;
use Syntro\SilverstripePHPStan\Utility;
use PhpParser\Node\Expr\StaticCall;
Expand Down Expand Up @@ -57,17 +58,17 @@ public function getTypeFromStaticMethodCall(MethodReflection $methodReflection,
// Handle DataObject::get_one('Page')
$arg = $methodCall->args[0];
$type = Utility::getTypeFromVariable($arg, $methodReflection);
return $type;
return TypeCombinator::addNull($type);
}
// Handle Page::get() / self::get()
$callerClass = $methodCall->class->toString();
if ($callerClass === 'static') {
return Utility::getMethodReturnType($methodReflection);
return TypeCombinator::addNull(Utility::getMethodReturnType($methodReflection));
}
if ($callerClass === 'self') {
$callerClass = $scope->getClassReflection()->getName();
}
return new ObjectType($callerClass);
return TypeCombinator::addNull(new ObjectType($callerClass));
}
// NOTE(mleutenegger): 2019-11-10
// taken from https://github.com/phpstan/phpstan#dynamic-return-type-extensions
Expand Down
25 changes: 12 additions & 13 deletions src/Type/DataObjectReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,20 @@
namespace Syntro\SilverstripePHPStan\Type;

use Exception;
use Syntro\SilverstripePHPStan\ClassHelper;
use Syntro\SilverstripePHPStan\ConfigHelper;
use Syntro\SilverstripePHPStan\Utility;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Broker\Broker;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Expr\ClassConstFetch;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Type;
use PHPStan\Type\ArrayType;
use PHPStan\Type\IntegerType;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StaticType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Syntro\SilverstripePHPStan\ClassHelper;
use Syntro\SilverstripePHPStan\ConfigHelper;
use Syntro\SilverstripePHPStan\Utility;

class DataObjectReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
Expand Down Expand Up @@ -74,11 +70,14 @@ public function getTypeFromMethodCall(MethodReflection $methodReflection, Method

case 'dbObject':
$className = '';
if ($type instanceof UnionType) {
$type = array_filter($type->getTypes(), fn ($subType) => $subType instanceof ObjectType)[0] ?? null;
}
if ($type instanceof StaticType) {
if (count($type->getReferencedClasses()) === 1) {
$className = $type->getReferencedClasses()[0];
}
} else if ($type instanceof ObjectType) {
} elseif ($type instanceof ObjectType) {
$className = $type->getClassName();
}
if (!$className) {
Expand Down
96 changes: 96 additions & 0 deletions src/Type/VersionedGetStaticReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php declare(strict_types = 1);

namespace Syntro\SilverstripePHPStan\Type;

use PHPStan\Type\TypeCombinator;
use Syntro\SilverstripePHPStan\ClassHelper;
use Syntro\SilverstripePHPStan\Utility;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Type;
use PHPStan\Type\ObjectType;

class VersionedGetStaticReturnTypeExtension implements \PHPStan\Type\DynamicStaticMethodReturnTypeExtension
{
public function getClass(): string
{
return ClassHelper::Versioned;
}

public function isStaticMethodSupported(MethodReflection $methodReflection): bool
{
$name = $methodReflection->getName();

return in_array($name, [
'get_including_deleted',
'get_by_stage',
'get_all_versions',

'get_version',
'get_one_by_stage',
'get_latest_version',
]);
}

public function getTypeFromStaticMethodCall(MethodReflection $methodReflection, StaticCall $methodCall, Scope $scope): Type
{
$name = $methodReflection->getName();
switch ($name) {
case 'get_including_deleted':
case 'get_by_stage':
case 'get_all_versions':
if (count($methodCall->args) > 0) {
// Handle DataObject::get('Page')
$arg = $methodCall->args[0];
$type = Utility::getTypeFromInjectorVariable($arg, new ObjectType('SilverStripe\ORM\DataObject'));
return new DataListType(ClassHelper::DataList, $type);
}
// Handle Page::get() / self::get()
$callerClass = $methodCall->class->toString();
if ($callerClass === 'static') {
return Utility::getMethodReturnType($methodReflection);
}
if ($callerClass === 'self') {
$callerClass = $scope->getClassReflection()->getName();
}
return new DataListType(ClassHelper::DataList, new ObjectType($callerClass));

case 'get_version':
case 'get_one_by_stage':
case 'get_latest_version':
if (count($methodCall->args) > 0) {
// Handle DataObject::get_one('Page')
$arg = $methodCall->args[0];
$type = Utility::getTypeFromVariable($arg, $methodReflection);
return TypeCombinator::addNull($type);
}
// Handle Page::get() / self::get()
$callerClass = $methodCall->class->toString();
if ($callerClass === 'static') {
return TypeCombinator::addNull(Utility::getMethodReturnType($methodReflection));
}
if ($callerClass === 'self') {
$callerClass = $scope->getClassReflection()->getName();
}
return TypeCombinator::addNull(new ObjectType($callerClass));
}
// NOTE(mleutenegger): 2019-11-10
// taken from https://github.com/phpstan/phpstan#dynamic-return-type-extensions
if (count($methodCall->args) === 0) {
return ParametersAcceptorSelector::selectFromArgs(
$scope,
$methodCall->args,
$methodReflection->getVariants()
)->getReturnType();
}
$arg = $methodCall->args[0]->value;

return $scope->getType($arg);
}
}