diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 25a284ceb4c..d39d62325f5 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -72,7 +72,7 @@ abstract class AbstractQuery /** * The parameter map of this query. * - * @var \Doctrine\Common\Collections\ArrayCollection + * @var \Doctrine\Common\Collections\ArrayCollection|\Doctrine\ORM\Query\Parameter[] */ protected $parameters; diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 525aa7a5081..9bbc4d3e7cf 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -19,15 +19,17 @@ namespace Doctrine\ORM; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\DBAL\LockMode; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Query\Exec\AbstractSqlExecutor; +use Doctrine\ORM\Query\Parameter; +use Doctrine\ORM\Query\ParameterTypeInferer; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\ParserResult; use Doctrine\ORM\Query\QueryException; -use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Query\ParameterTypeInferer; -use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver; +use function assert; /** * A Query object represents a DQL query. @@ -387,26 +389,13 @@ private function processParameterMappings($paramMappings) $types = []; foreach ($this->parameters as $parameter) { - $key = $parameter->getName(); - $value = $parameter->getValue(); - $rsm = $this->getResultSetMapping(); + $key = $parameter->getName(); if ( ! isset($paramMappings[$key])) { throw QueryException::unknownParameter($key); } - if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) { - $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]); - } - - if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) { - $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em)); - } - - $value = $this->processParameterValue($value); - $type = ($parameter->getValue() === $value) - ? $parameter->getType() - : ParameterTypeInferer::inferType($value); + [$value, $type] = $this->resolveParameterValue($parameter); foreach ($paramMappings[$key] as $position) { $types[$position] = $type; @@ -439,6 +428,38 @@ private function processParameterMappings($paramMappings) return [$sqlParams, $types]; } + /** @return mixed[] tuple of (value, type) */ + private function resolveParameterValue(Parameter $parameter) : array + { + if ($parameter->typeWasSpecified()) { + return [$parameter->getValue(), $parameter->getType()]; + } + + $key = $parameter->getName(); + $originalValue = $parameter->getValue(); + $value = $originalValue; + $rsm = $this->getResultSetMapping(); + + assert($rsm !== null); + + if ($value instanceof ClassMetadata && isset($rsm->metadataParameterMapping[$key])) { + $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]); + } + + if ($value instanceof ClassMetadata && isset($rsm->discriminatorParameters[$key])) { + $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em)); + } + + $processedValue = $this->processParameterValue($value); + + return [ + $processedValue, + $originalValue === $processedValue + ? $parameter->getType() + : ParameterTypeInferer::inferType($processedValue), + ]; + } + /** * Defines a cache driver to be used for caching queries. * diff --git a/lib/Doctrine/ORM/Query/Parameter.php b/lib/Doctrine/ORM/Query/Parameter.php index 39e2a7a4f21..360a3e4b103 100644 --- a/lib/Doctrine/ORM/Query/Parameter.php +++ b/lib/Doctrine/ORM/Query/Parameter.php @@ -49,6 +49,13 @@ class Parameter */ private $type; + /** + * Whether the parameter type was explicitly specified or not + * + * @var bool + */ + private $typeSpecified; + /** * Constructor. * @@ -58,7 +65,8 @@ class Parameter */ public function __construct($name, $value, $type = null) { - $this->name = trim($name, ':'); + $this->name = trim($name, ':'); + $this->typeSpecified = $type !== null; $this->setValue($value, $type); } @@ -104,4 +112,9 @@ public function setValue($value, $type = null) $this->value = $value; $this->type = $type ?: ParameterTypeInferer::inferType($value); } + + public function typeWasSpecified() : bool + { + return $this->typeSpecified; + } } diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index 1cf8ab1646f..17d10f4b2dc 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -609,8 +609,11 @@ public function testSetParameter() ->setParameter('id', 1); $parameter = new Parameter('id', 1, ParameterTypeInferer::inferType(1)); + $inferred = $qb->getParameter('id'); - $this->assertEquals($parameter, $qb->getParameter('id')); + self::assertSame($parameter->getValue(), $inferred->getValue()); + self::assertSame($parameter->getType(), $inferred->getType()); + self::assertFalse($inferred->typeWasSpecified()); } public function testSetParameters()