Skip to content

Commit

Permalink
implement entity return type for DbalMapper::toCollection() & ::toEnt…
Browse files Browse the repository at this point in the history
…ity() [closes #5]
  • Loading branch information
hrach committed Jul 12, 2019
1 parent 19c4d11 commit 3e12e9a
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
4 changes: 4 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ services:
class: Nextras\OrmPhpStan\Types\CollectionReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Nextras\OrmPhpStan\Types\MapperMethodReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: Nextras\OrmPhpStan\Types\RelationshipReturnTypeExtension
tags:
Expand Down
94 changes: 94 additions & 0 deletions src/Types/MapperMethodReturnTypeExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php declare(strict_types = 1);

namespace Nextras\OrmPhpStan\Types;

use Nextras\Orm\Collection\ICollection;
use Nextras\Orm\Mapper\Dbal\DbalMapper;
use Nextras\OrmPhpStan\Types\Helpers\RepositoryEntityTypeHelper;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IterableType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeWithClassName;


class MapperMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
/** @var RepositoryEntityTypeHelper */
private $repositoryEntityTypeHelper;


public function __construct(RepositoryEntityTypeHelper $repositoryEntityTypeHelper)
{
$this->repositoryEntityTypeHelper = $repositoryEntityTypeHelper;
}


public function getClass(): string
{
return DbalMapper::class;
}


public function isMethodSupported(MethodReflection $methodReflection): bool
{
static $methods = [
'toEntity',
'toCollection',
];
return in_array($methodReflection->getName(), $methods, true);
}


public function getTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): Type
{
$mapper = $scope->getType($methodCall->var);
\assert($mapper instanceof TypeWithClassName);

$defaultReturn = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();

if ($mapper->getClassName() === DbalMapper::class) {
return $defaultReturn;
}

$mapperClass = $mapper->getClassName();
do {
$repositoryClass = \str_replace('Mapper', 'Repository', $mapperClass);
$mapperClass = \get_parent_class($mapperClass);
} while (!\class_exists($repositoryClass) && $mapperClass !== DbalMapper::class);

try {
$repository = new \ReflectionClass($repositoryClass);
} catch (\ReflectionException $e) {
return $defaultReturn;
}

$entityType = $this->repositoryEntityTypeHelper->resolveFirst(
$repository,
$scope
);

$methodName = $methodReflection->getName();
if ($methodName === 'toEntity') {
return TypeCombinator::addNull($entityType);
} elseif ($methodName === 'toCollection') {
return TypeCombinator::intersect(
new ObjectType(ICollection::class),
new IterableType(new IntegerType(), $entityType)
);
}

throw new ShouldNotHappenException();
}
}
26 changes: 26 additions & 0 deletions tests/testbox/Types/MapperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);

namespace NextrasTests\OrmPhpStan\Types;


class MapperTest extends AuthorsMapper
{
public function testOk()
{
$this->takeAuthors($this->toCollection([]));
$this->takeAuthor($this->toEntity([]));
}


private function takeAuthor(?Author $author)
{
}


/**
* @param iterable<int, Author> $authors
*/
private function takeAuthors($authors)
{
}
}
10 changes: 10 additions & 0 deletions tests/testbox/Types/fixtures/AuthorsMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types = 1);

namespace NextrasTests\OrmPhpStan\Types;

use Nextras\Orm\Mapper\Mapper;


class AuthorsMapper extends Mapper
{
}

0 comments on commit 3e12e9a

Please sign in to comment.