Skip to content

Commit

Permalink
Revert "Improve QueryResultDynamicReturnTypeExtension"
Browse files Browse the repository at this point in the history
This reverts commit 03741b4.
  • Loading branch information
ondrejmirtes committed May 4, 2023
1 parent dff01ff commit f1499e5
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 537 deletions.
168 changes: 21 additions & 147 deletions src/Type/Doctrine/Query/QueryResultDynamicReturnTypeExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,14 @@
use PHPStan\ShouldNotHappenException;
use PHPStan\Type\Accessory\AccessoryArrayListType;
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Doctrine\ObjectMetadataResolver;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\IntegerType;
use PHPStan\Type\IterableType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NullType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeTraverser;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\VoidType;
use function count;

final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension
{
Expand All @@ -39,32 +32,14 @@ final class QueryResultDynamicReturnTypeExtension implements DynamicMethodReturn
'getSingleResult' => 0,
];

private const METHOD_HYDRATION_MODE = [
'getArrayResult' => AbstractQuery::HYDRATE_ARRAY,
'getScalarResult' => AbstractQuery::HYDRATE_SCALAR,
'getSingleColumnResult' => AbstractQuery::HYDRATE_SCALAR_COLUMN,
'getSingleScalarResult' => AbstractQuery::HYDRATE_SINGLE_SCALAR,
];

/** @var ObjectMetadataResolver */
private $objectMetadataResolver;

public function __construct(
ObjectMetadataResolver $objectMetadataResolver
)
{
$this->objectMetadataResolver = $objectMetadataResolver;
}

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

public function isMethodSupported(MethodReflection $methodReflection): bool
{
return isset(self::METHOD_HYDRATION_MODE_ARG[$methodReflection->getName()])
|| isset(self::METHOD_HYDRATION_MODE[$methodReflection->getName()]);
return isset(self::METHOD_HYDRATION_MODE_ARG[$methodReflection->getName()]);
}

public function getTypeFromMethodCall(
Expand All @@ -75,23 +50,21 @@ public function getTypeFromMethodCall(
{
$methodName = $methodReflection->getName();

if (isset(self::METHOD_HYDRATION_MODE[$methodName])) {
$hydrationMode = new ConstantIntegerType(self::METHOD_HYDRATION_MODE[$methodName]);
} elseif (isset(self::METHOD_HYDRATION_MODE_ARG[$methodName])) {
$argIndex = self::METHOD_HYDRATION_MODE_ARG[$methodName];
$args = $methodCall->getArgs();
if (!isset(self::METHOD_HYDRATION_MODE_ARG[$methodName])) {
throw new ShouldNotHappenException();
}

if (isset($args[$argIndex])) {
$hydrationMode = $scope->getType($args[$argIndex]->value);
} else {
$parametersAcceptor = ParametersAcceptorSelector::selectSingle(
$methodReflection->getVariants()
);
$parameter = $parametersAcceptor->getParameters()[$argIndex];
$hydrationMode = $parameter->getDefaultValue() ?? new NullType();
}
$argIndex = self::METHOD_HYDRATION_MODE_ARG[$methodName];
$args = $methodCall->getArgs();

if (isset($args[$argIndex])) {
$hydrationMode = $scope->getType($args[$argIndex]->value);
} else {
throw new ShouldNotHappenException();
$parametersAcceptor = ParametersAcceptorSelector::selectSingle(
$methodReflection->getVariants()
);
$parameter = $parametersAcceptor->getParameters()[$argIndex];
$hydrationMode = $parameter->getDefaultValue() ?? new NullType();
}

$queryType = $scope->getType($methodCall->var);
Expand Down Expand Up @@ -125,34 +98,12 @@ private function getMethodReturnTypeForHydrationMode(
return $this->originalReturnType($methodReflection);
}

if (!$hydrationMode instanceof ConstantIntegerType) {
if (!$this->isObjectHydrationMode($hydrationMode)) {
// We support only HYDRATE_OBJECT. For other hydration modes, we
// return the declared return type of the method.
return $this->originalReturnType($methodReflection);
}

$singleResult = false;
switch ($hydrationMode->getValue()) {
case AbstractQuery::HYDRATE_OBJECT:
break;
case AbstractQuery::HYDRATE_ARRAY:
$queryResultType = $this->getArrayHydratedReturnType($queryResultType);
break;
case AbstractQuery::HYDRATE_SCALAR:
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
break;
case AbstractQuery::HYDRATE_SINGLE_SCALAR:
$singleResult = true;
$queryResultType = $this->getSingleScalarHydratedReturnType($queryResultType);
break;
case AbstractQuery::HYDRATE_SIMPLEOBJECT:
$queryResultType = $this->getSimpleObjectHydratedReturnType($queryResultType);
break;
case AbstractQuery::HYDRATE_SCALAR_COLUMN:
$queryResultType = $this->getScalarColumnHydratedReturnType($queryResultType);
break;
default:
return $this->originalReturnType($methodReflection);
}

switch ($methodReflection->getName()) {
case 'getSingleResult':
return $queryResultType;
Expand All @@ -164,10 +115,6 @@ private function getMethodReturnTypeForHydrationMode(
$queryResultType
);
default:
if ($singleResult) {
return $queryResultType;
}

if ($queryKeyType->isNull()->yes()) {
return AccessoryArrayListType::intersectWith(new ArrayType(
new IntegerType(),
Expand All @@ -181,86 +128,13 @@ private function getMethodReturnTypeForHydrationMode(
}
}

private function getArrayHydratedReturnType(Type $queryResultType): Type
{
$objectManager = $this->objectMetadataResolver->getObjectManager();

return TypeTraverser::map(
$queryResultType,
static function (Type $type, callable $traverse) use ($objectManager): Type {
$isObject = (new ObjectWithoutClassType())->isSuperTypeOf($type);
if ($isObject->no()) {
return $traverse($type);
}
if (
$isObject->maybe()
|| !$type instanceof TypeWithClassName
|| $objectManager === null
) {
return new MixedType();
}

return $objectManager->getMetadataFactory()->hasMetadataFor($type->getClassName())
? new ArrayType(new MixedType(), new MixedType())
: $traverse($type);
}
);
}

private function getScalarHydratedReturnType(Type $queryResultType): Type
{
if (!$queryResultType instanceof ArrayType) {
return new ArrayType(new MixedType(), new MixedType());
}

$itemType = $queryResultType->getItemType();
$hasNoObject = (new ObjectWithoutClassType())->isSuperTypeOf($itemType)->no();
$hasNoArray = $itemType->isArray()->no();

if ($hasNoArray && $hasNoObject) {
return $queryResultType;
}

return new ArrayType(new MixedType(), new MixedType());
}

private function getSimpleObjectHydratedReturnType(Type $queryResultType): Type
private function isObjectHydrationMode(Type $type): bool
{
if ((new ObjectWithoutClassType())->isSuperTypeOf($queryResultType)->yes()) {
return $queryResultType;
}

return new MixedType();
}

private function getSingleScalarHydratedReturnType(Type $queryResultType): Type
{
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
if (!$queryResultType instanceof ConstantArrayType) {
return new MixedType();
}

$values = $queryResultType->getValueTypes();
if (count($values) !== 1) {
return new MixedType();
}

return $queryResultType->getFirstIterableValueType();
}

private function getScalarColumnHydratedReturnType(Type $queryResultType): Type
{
$queryResultType = $this->getScalarHydratedReturnType($queryResultType);
if (!$queryResultType instanceof ConstantArrayType) {
return new MixedType();
}

$values = $queryResultType->getValueTypes();
if (count($values) !== 1) {
return new MixedType();
if (!$type instanceof ConstantIntegerType) {
return false;
}

return $queryResultType->getFirstIterableValueType();
return $type->getValue() === AbstractQuery::HYDRATE_OBJECT;
}

private function originalReturnType(MethodReflection $methodReflection): Type
Expand Down
Loading

0 comments on commit f1499e5

Please sign in to comment.