phpstan dynamic return type

This commit is contained in:
Yuri Kuznetsov
2021-11-08 15:23:34 +02:00
parent ab9f78d33f
commit 9aaab7d726
3 changed files with 161 additions and 1 deletions

View File

@@ -59,7 +59,8 @@
},
"autoload-dev": {
"psr-4": {
"tests\\": "tests/"
"tests\\": "tests/",
"EspoDev\\": "dev/"
}
},
"repositories": [

View File

@@ -0,0 +1,154 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM - Open Source CRM application.
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
* Website: https://www.espocrm.com
*
* EspoCRM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* EspoCRM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU General Public License version 3.
*
* In accordance with Section 7(b) of the GNU General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace EspoDev\PHPStan\Extensions;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Type;
use PHPStan\Type\ObjectType;
use PHPStan\Type\UnionType;
use PHPStan\Type\NullType;
use PHPStan\Type\Generic\GenericObjectType;
use PhpParser\Node\Scalar\String_;
use RuntimeException;
use Espo\ORM\Entity;
use Espo\ORM\EntityManager;
use Espo\Core\Utils\Util;
use Espo\ORM\Repository\RDBRepository;
class EntityManagerReturnType implements DynamicMethodReturnTypeExtension
{
private $supportedMethodNameList = [
'getEntity',
'getRDBRepository',
];
private $entityNamespaceList = [
'\\Espo\\Modules\\Crm\\Entities',
'\\Espo\\Entities',
];
public function getClass(): string
{
return EntityManager::class;
}
public function isMethodSupported(MethodReflection $methodReflection): bool
{
return in_array($methodReflection->getName(), $this->supportedMethodNameList);
}
public function getTypeFromMethodCall(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): Type {
$methodName = $methodReflection->getName();
if ($methodName === 'getEntity') {
return $this->getGetEntity($methodReflection, $methodCall, $scope);
}
if ($methodName === 'getRDBRepository') {
return $this->getGetRDBRepository($methodReflection, $methodCall, $scope);
}
throw new RuntimeException("Not supported method.");
}
private function getGetEntity(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): Type {
$value = $methodCall->args[0]->value;
if (!$value instanceof String_) {
return new UnionType([
new ObjectType(Entity::class),
new NullType(),
]);
}
$entityType = $value->value;
$className = $this->findEntityClassName($entityType) ?? Entity::class;
return new UnionType([
new ObjectType($className),
new NullType(),
]);
}
private function findEntityClassName(string $entityType): ?string
{
foreach ($this->entityNamespaceList as $namespace) {
$className = $namespace . '\\' . Util::normilizeClassName($entityType);
if (class_exists($className)) {
return $className;
}
}
return null;
}
private function getGetRDBRepository(
MethodReflection $methodReflection,
MethodCall $methodCall,
Scope $scope
): Type {
$value = $methodCall->args[0]->value;
if (!$value instanceof String_) {
return new ObjectType(RDBRepository::class);
}
$entityType = $value->value;
$entityClassName = $this->findEntityClassName($entityType);
if ($entityClassName) {
return new GenericObjectType(RDBRepository::class, [new ObjectType($entityClassName)]);
}
return new ObjectType(RDBRepository::class);
}
}

View File

@@ -6,3 +6,8 @@ parameters:
reportUnmatchedIgnoredErrors: false
excludePaths:
- application/Espo/Core/Select/SelectManager.php
services:
-
class: EspoDev\PHPStan\Extensions\EntityManagerReturnType
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension