diff --git a/UPGRADE_TO_2_2 b/UPGRADE_TO_2_2 index 757413029..5b01fab04 100644 --- a/UPGRADE_TO_2_2 +++ b/UPGRADE_TO_2_2 @@ -1,6 +1,6 @@ # ResultCache implementation rewritten -The result cache is completly rewritten and now works on the database result level, not inside the ORM AbstractQuery +The result cache is completely rewritten and now works on the database result level, not inside the ORM AbstractQuery anymore. This means that for result cached queries the hydration will now always be performed again, regardless of the hydration mode. Affected areas are: @@ -12,20 +12,24 @@ The API is backwards compatible however most of the getter methods on the `Abstr deprecated in favor of calling AbstractQuery#getQueryCacheProfile(). This method returns a `Doctrine\DBAL\Cache\QueryCacheProfile` instance with access to result cache driver, lifetime and cache key. + # EntityManager#getPartialReference() creates read-only entity Entities returned from EntityManager#getPartialReference() are now marked as read-only if they haven't been in the identity map before. This means objects of this kind never lead to changes in the UnitOfWork. + # Fields omitted in a partial DQL query or a native query are never updated Fields of an entity that are not returned from a partial DQL Query or native SQL query will never be updated through an UPDATE statement. + # Removed support for onUpdate in @JoinColumn -The onUpdate foreign key handling makes absolutly no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed. +The onUpdate foreign key handling makes absolutely no sense in an ORM. Additionally Oracle doesn't even support it. Support for it is removed. + # Changes in Annotation Handling @@ -41,4 +45,32 @@ from 2.0 have to configure the annotation driver if they don't use `Configuratio $driver = new AnnotationDriver($reader, (array)$paths); - $config->setMetadataDriverImpl($driver); \ No newline at end of file + $config->setMetadataDriverImpl($driver); + + +# Scalar mappings can now be ommitted from DQL result + +You are now allowed to mark scalar SELECT expressions as HIDDEN an they are not hydrated anymore. +Example: + +SELECT u, SUM(a.id) AS HIDDEN numArticles FROM User u LEFT JOIN u.Articles a ORDER BY numArticles DESC HAVING numArticles > 10 + +Your result will be a collection of Users, and not an array with key 0 as User object instance and "numArticles" as the number of articles per user + + +# Map entities as scalars in DQL result + +When hydrating to array or even a mixed result in object hydrator, previously you had the 0 index holding you entity instance. +You are now allowed to alias this, providing more flexibility for you code. +Example: + +SELECT u AS user FROM User u + +Will now return a collection of arrays with index "user" pointing to the User object instance. + + +# Performance optimizations + +Thousands of lines were completely reviewed and optimized for best performance. +Removed redundancy and improved code readability made now internal Doctrine code easier to understand. +Also, Doctrine 2.2 now is around 10-15% faster than 2.1. \ No newline at end of file diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 72eab194b..9a812f446 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -144,7 +144,7 @@ abstract class AbstractQuery { return $this->_params; } - + /** * Get all defined parameter types. * @@ -163,7 +163,11 @@ abstract class AbstractQuery */ public function getParameter($key) { - return isset($this->_params[$key]) ? $this->_params[$key] : null; + if (isset($this->_params[$key])) { + return $this->_params[$key]; + } + + return null; } /** @@ -174,7 +178,11 @@ abstract class AbstractQuery */ public function getParameterType($key) { - return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null; + if (isset($this->_paramTypes[$key])) { + return $this->_paramTypes[$key]; + } + + return null; } /** @@ -199,14 +207,14 @@ abstract class AbstractQuery public function setParameter($key, $value, $type = null) { $key = trim($key, ':'); - + if ($type === null) { $type = Query\ParameterTypeInferer::inferType($value); } - + $this->_paramTypes[$key] = $type; $this->_params[$key] = $value; - + return $this; } @@ -220,12 +228,9 @@ abstract class AbstractQuery public function setParameters(array $params, array $types = array()) { foreach ($params as $key => $value) { - if (isset($types[$key])) { - $this->setParameter($key, $value, $types[$key]); - } else { - $this->setParameter($key, $value); - } + $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null); } + return $this; } @@ -238,6 +243,7 @@ abstract class AbstractQuery public function setResultSetMapping(Query\ResultSetMapping $rsm) { $this->_resultSetMapping = $rsm; + return $this; } @@ -252,11 +258,11 @@ abstract class AbstractQuery if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { throw ORMException::invalidResultCacheDriver(); } - if ($this->_queryCacheProfile) { - $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver); - } else { - $this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver); - } + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver) + : new QueryCacheProfile(0, null, $resultCacheDriver); + return $this; } @@ -270,9 +276,9 @@ abstract class AbstractQuery { if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) { return $this->_queryCacheProfile->getResultCacheDriver(); - } else { - return $this->_em->getConfiguration()->getResultCacheImpl(); } + + return $this->_em->getConfiguration()->getResultCacheImpl(); } /** @@ -289,9 +295,12 @@ abstract class AbstractQuery if ($bool) { $this->setResultCacheLifetime($lifetime); $this->setResultCacheId($resultCacheId); - } else { - $this->_queryCacheProfile = null; + + return $this; } + + $this->_queryCacheProfile = null; + return $this; } @@ -303,16 +312,12 @@ abstract class AbstractQuery */ public function setResultCacheLifetime($lifetime) { - if ($lifetime === null) { - $lifetime = 0; - } else { - $lifetime = (int)$lifetime; - } - if ($this->_queryCacheProfile) { - $this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime); - } else { - $this->_queryCacheProfile = new QueryCacheProfile($lifetime); - } + $lifetime = ($lifetime !== null) ? (int) $lifetime : 0; + + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setLifetime($lifetime) + : new QueryCacheProfile($lifetime); + return $this; } @@ -336,6 +341,7 @@ abstract class AbstractQuery public function expireResultCache($expire = true) { $this->_expireResultCache = $expire; + return $this; } @@ -374,6 +380,7 @@ abstract class AbstractQuery } $this->_hints['fetchMode'][$class][$assocName] = $fetchMode; + return $this; } @@ -387,6 +394,7 @@ abstract class AbstractQuery public function setHydrationMode($hydrationMode) { $this->_hydrationMode = $hydrationMode; + return $this; } @@ -451,14 +459,15 @@ abstract class AbstractQuery return null; } - if (is_array($result)) { - if (count($result) > 1) { - throw new NonUniqueResultException; - } - return array_shift($result); + if ( ! is_array($result)) { + return $result; } - return $result; + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); } /** @@ -482,14 +491,15 @@ abstract class AbstractQuery throw new NoResultException; } - if (is_array($result)) { - if (count($result) > 1) { - throw new NonUniqueResultException; - } - return array_shift($result); + if ( ! is_array($result)) { + return $result; } - return $result; + if (count($result) > 1) { + throw new NonUniqueResultException; + } + + return array_shift($result); } /** @@ -515,6 +525,7 @@ abstract class AbstractQuery public function setHint($name, $value) { $this->_hints[$name] = $value; + return $this; } @@ -531,7 +542,7 @@ abstract class AbstractQuery /** * Return the key value map of query hints that are currently set. - * + * * @return array */ public function getHints() @@ -588,8 +599,8 @@ abstract class AbstractQuery } return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll( - $stmt, $this->_resultSetMapping, $this->_hints - ); + $stmt, $this->_resultSetMapping, $this->_hints + ); } /** @@ -602,11 +613,10 @@ abstract class AbstractQuery */ public function setResultCacheId($id) { - if ($this->_queryCacheProfile) { - $this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id); - } else { - $this->_queryCacheProfile = new QueryCacheProfile(0, $id); - } + $this->_queryCacheProfile = $this->_queryCacheProfile + ? $this->_queryCacheProfile->setCacheKey($id) + : new QueryCacheProfile(0, $id); + return $this; } diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 8bb7a5896..f09bacb89 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -130,10 +130,12 @@ class EntityManager implements ObjectManager $this->metadataFactory->setCacheDriver($this->config->getMetadataCacheImpl()); $this->unitOfWork = new UnitOfWork($this); - $this->proxyFactory = new ProxyFactory($this, - $config->getProxyDir(), - $config->getProxyNamespace(), - $config->getAutoGenerateProxyClasses()); + $this->proxyFactory = new ProxyFactory( + $this, + $config->getProxyDir(), + $config->getProxyNamespace(), + $config->getAutoGenerateProxyClasses() + ); } /** @@ -175,6 +177,7 @@ class EntityManager implements ObjectManager if ($this->expressionBuilder === null) { $this->expressionBuilder = new Query\Expr; } + return $this->expressionBuilder; } @@ -267,9 +270,11 @@ class EntityManager implements ObjectManager public function createQuery($dql = "") { $query = new Query($this); + if ( ! empty($dql)) { $query->setDql($dql); } + return $query; } @@ -296,6 +301,7 @@ class EntityManager implements ObjectManager $query = new NativeQuery($this); $query->setSql($sql); $query->setResultSetMapping($rsm); + return $query; } @@ -308,6 +314,7 @@ class EntityManager implements ObjectManager public function createNamedNativeQuery($name) { list($sql, $rsm) = $this->config->getNamedNativeQuery($name); + return $this->createNativeQuery($sql, $rsm); } @@ -336,6 +343,7 @@ class EntityManager implements ObjectManager public function flush($entity = null) { $this->errorIfClosed(); + $this->unitOfWork->commit($entity); } @@ -371,16 +379,18 @@ class EntityManager implements ObjectManager if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { return ($entity instanceof $class->name) ? $entity : null; } + if ($class->subClasses) { - $entity = $this->find($entityName, $identifier); - } else { - if ( ! is_array($identifier)) { - $identifier = array($class->identifier[0] => $identifier); - } - $entity = $this->proxyFactory->getProxy($class->name, $identifier); - $this->unitOfWork->registerManaged($entity, $identifier, array()); + return $this->find($entityName, $identifier); } + if ( ! is_array($identifier)) { + $identifier = array($class->identifier[0] => $identifier); + } + + $entity = $this->proxyFactory->getProxy($class->name, $identifier); + $this->unitOfWork->registerManaged($entity, $identifier, array()); + return $entity; } @@ -411,6 +421,7 @@ class EntityManager implements ObjectManager if ($entity = $this->unitOfWork->tryGetById($identifier, $class->rootEntityName)) { return ($entity instanceof $class->name) ? $entity : null; } + if ( ! is_array($identifier)) { $identifier = array($class->identifier[0] => $identifier); } @@ -461,7 +472,9 @@ class EntityManager implements ObjectManager if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } + $this->errorIfClosed(); + $this->unitOfWork->persist($entity); } @@ -478,7 +491,9 @@ class EntityManager implements ObjectManager if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } + $this->errorIfClosed(); + $this->unitOfWork->remove($entity); } @@ -493,7 +508,9 @@ class EntityManager implements ObjectManager if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } + $this->errorIfClosed(); + $this->unitOfWork->refresh($entity); } @@ -511,6 +528,7 @@ class EntityManager implements ObjectManager if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } + $this->unitOfWork->detach($entity); } @@ -527,7 +545,9 @@ class EntityManager implements ObjectManager if ( ! is_object($entity)) { throw new \InvalidArgumentException(gettype($entity)); } + $this->errorIfClosed(); + return $this->unitOfWork->merge($entity); } @@ -567,20 +587,20 @@ class EntityManager implements ObjectManager public function getRepository($entityName) { $entityName = ltrim($entityName, '\\'); + if (isset($this->repositories[$entityName])) { return $this->repositories[$entityName]; } $metadata = $this->getClassMetadata($entityName); - $customRepositoryClassName = $metadata->customRepositoryClassName; + $repositoryClassName = $metadata->customRepositoryClassName; - if ($customRepositoryClassName !== null) { - $repository = new $customRepositoryClassName($this, $metadata); - } else { - $repositoryClass = $this->config->getDefaultRepositoryClassName(); - $repository = new $repositoryClass($this, $metadata); + if ($repositoryClassName === null) { + $repositoryClassName = $this->config->getDefaultRepositoryClassName(); } + $repository = new $repositoryClassName($this, $metadata); + $this->repositories[$entityName] = $repository; return $repository; @@ -594,9 +614,9 @@ class EntityManager implements ObjectManager */ public function contains($entity) { - return $this->unitOfWork->isScheduledForInsert($entity) || - $this->unitOfWork->isInIdentityMap($entity) && - ! $this->unitOfWork->isScheduledForDelete($entity); + return $this->unitOfWork->isScheduledForInsert($entity) + || $this->unitOfWork->isInIdentityMap($entity) + && ! $this->unitOfWork->isScheduledForDelete($entity); } /** @@ -679,29 +699,27 @@ class EntityManager implements ObjectManager { switch ($hydrationMode) { case Query::HYDRATE_OBJECT: - $hydrator = new Internal\Hydration\ObjectHydrator($this); - break; + return new Internal\Hydration\ObjectHydrator($this); + case Query::HYDRATE_ARRAY: - $hydrator = new Internal\Hydration\ArrayHydrator($this); - break; + return new Internal\Hydration\ArrayHydrator($this); + case Query::HYDRATE_SCALAR: - $hydrator = new Internal\Hydration\ScalarHydrator($this); - break; + return new Internal\Hydration\ScalarHydrator($this); + case Query::HYDRATE_SINGLE_SCALAR: - $hydrator = new Internal\Hydration\SingleScalarHydrator($this); - break; + return new Internal\Hydration\SingleScalarHydrator($this); + case Query::HYDRATE_SIMPLEOBJECT: - $hydrator = new Internal\Hydration\SimpleObjectHydrator($this); - break; + return new Internal\Hydration\SimpleObjectHydrator($this); + default: if ($class = $this->config->getCustomHydrationMode($hydrationMode)) { - $hydrator = new $class($this); - break; + return new $class($this); } - throw ORMException::invalidHydrationMode($hydrationMode); } - return $hydrator; + throw ORMException::invalidHydrationMode($hydrationMode); } /** @@ -737,18 +755,25 @@ class EntityManager implements ObjectManager */ public static function create($conn, Configuration $config, EventManager $eventManager = null) { - if (!$config->getMetadataDriverImpl()) { + if ( ! $config->getMetadataDriverImpl()) { throw ORMException::missingMappingDriverImpl(); } - if (is_array($conn)) { - $conn = \Doctrine\DBAL\DriverManager::getConnection($conn, $config, ($eventManager ?: new EventManager())); - } else if ($conn instanceof Connection) { - if ($eventManager !== null && $conn->getEventManager() !== $eventManager) { - throw ORMException::mismatchedEventManager(); - } - } else { - throw new \InvalidArgumentException("Invalid argument: " . $conn); + switch (true) { + case (is_array($conn)): + $conn = \Doctrine\DBAL\DriverManager::getConnection( + $conn, $config, ($eventManager ?: new EventManager()) + ); + break; + + case ($conn instanceof Connection): + if ($eventManager !== null && $conn->getEventManager() !== $eventManager) { + throw ORMException::mismatchedEventManager(); + } + break; + + default: + throw new \InvalidArgumentException("Invalid argument: " . $conn); } return new EntityManager($conn, $config, $conn->getEventManager()); diff --git a/lib/Doctrine/ORM/EntityRepository.php b/lib/Doctrine/ORM/EntityRepository.php index a4c239001..f74448141 100644 --- a/lib/Doctrine/ORM/EntityRepository.php +++ b/lib/Doctrine/ORM/EntityRepository.php @@ -109,11 +109,11 @@ class EntityRepository implements ObjectRepository { // Check identity map first if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) { - if (!($entity instanceof $this->_class->name)) { + if ( ! ($entity instanceof $this->_class->name)) { return null; } - - if ($lockMode != LockMode::NONE) { + + if ($lockMode !== LockMode::NONE) { $this->_em->lock($entity, $lockMode, $lockVersion); } @@ -126,23 +126,27 @@ class EntityRepository implements ObjectRepository $id = array_combine($this->_class->identifier, $value); } - if ($lockMode == LockMode::NONE) { - return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); - } else if ($lockMode == LockMode::OPTIMISTIC) { - if (!$this->_class->isVersioned) { - throw OptimisticLockException::notVersioned($this->_entityName); - } - $entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); + switch ($lockMode) { + case LockMode::NONE: + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); - $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion); + case LockMode::OPTIMISTIC: + if ( ! $this->_class->isVersioned) { + throw OptimisticLockException::notVersioned($this->_entityName); + } - return $entity; - } else { - if (!$this->_em->getConnection()->isTransactionActive()) { - throw TransactionRequiredException::transactionRequired(); - } - - return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode); + $entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id); + + $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion); + + return $entity; + + default: + if ( ! $this->_em->getConnection()->isTransactionActive()) { + throw TransactionRequiredException::transactionRequired(); + } + + return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode); } } @@ -191,30 +195,35 @@ class EntityRepository implements ObjectRepository */ public function __call($method, $arguments) { - if (substr($method, 0, 6) == 'findBy') { - $by = substr($method, 6, strlen($method)); - $method = 'findBy'; - } else if (substr($method, 0, 9) == 'findOneBy') { - $by = substr($method, 9, strlen($method)); - $method = 'findOneBy'; - } else { - throw new \BadMethodCallException( - "Undefined method '$method'. The method name must start with ". - "either findBy or findOneBy!" - ); + switch (true) { + case (substr($method, 0, 6) == 'findBy'): + $by = substr($method, 6, strlen($method)); + $method = 'findBy'; + break; + + case (substr($method, 0, 9) == 'findOneBy'): + $by = substr($method, 9, strlen($method)); + $method = 'findOneBy'; + break; + + default: + throw new \BadMethodCallException( + "Undefined method '$method'. The method name must start with ". + "either findBy or findOneBy!" + ); } if (empty($arguments)) { - throw ORMException::findByRequiresParameter($method.$by); + throw ORMException::findByRequiresParameter($method . $by); } $fieldName = lcfirst(\Doctrine\Common\Util\Inflector::classify($by)); if ($this->_class->hasField($fieldName) || $this->_class->hasAssociation($fieldName)) { return $this->$method(array($fieldName => $arguments[0])); - } else { - throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); } + + throw ORMException::invalidFindByCall($this->_entityName, $fieldName, $method.$by); } /** diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 9caa79e11..808e58552 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -24,7 +24,7 @@ use ReflectionClass, ReflectionProperty; /** * A ClassMetadata instance holds all the object-relational mapping metadata * of an entity and it's associations. - * + * * Once populated, ClassMetadata instances are usually cached in a serialized form. * * IMPORTANT NOTE: @@ -47,10 +47,10 @@ class ClassMetadata extends ClassMetadataInfo * @var array */ public $reflFields = array(); - + /** * The prototype from which new instances of the mapped class are created. - * + * * @var object */ private $_prototype; @@ -103,13 +103,13 @@ class ClassMetadata extends ClassMetadataInfo } return $this->reflFields[$this->identifier[0]]; } - + /** * Validates & completes the given field mapping. * * @param array $mapping The field mapping to validated & complete. * @return array The validated and completed field mapping. - * + * * @throws MappingException */ protected function _validateAndCompleteFieldMapping(array &$mapping) @@ -124,7 +124,7 @@ class ClassMetadata extends ClassMetadataInfo /** * Extracts the identifier values of an entity of this class. - * + * * For composite identifiers, the identifier values are returned as an array * with the same order as the field order in {@link identifier}. * @@ -135,20 +135,25 @@ class ClassMetadata extends ClassMetadataInfo { if ($this->isIdentifierComposite) { $id = array(); + foreach ($this->identifier as $idField) { $value = $this->reflFields[$idField]->getValue($entity); + if ($value !== null) { $id[$idField] = $value; } } + return $id; - } else { - $value = $this->reflFields[$this->identifier[0]]->getValue($entity); - if ($value !== null) { - return array($this->identifier[0] => $value); - } - return array(); } + + $value = $this->reflFields[$this->identifier[0]]->getValue($entity); + + if ($value !== null) { + return array($this->identifier[0] => $value); + } + + return array(); } /** @@ -215,18 +220,18 @@ class ClassMetadata extends ClassMetadataInfo { return __CLASS__ . '@' . spl_object_hash($this); } - + /** * Determines which fields get serialized. * * It is only serialized what is necessary for best unserialization performance. * That means any metadata properties that are not set or empty or simply have * their default value are NOT serialized. - * + * * Parts that are also NOT serialized because they can not be properly unserialized: * - reflClass (ReflectionClass) * - reflFields (ReflectionProperty array) - * + * * @return array The names of all the fields that should be serialized. */ public function __sleep() @@ -301,7 +306,7 @@ class ClassMetadata extends ClassMetadataInfo /** * Restores some state that can not be serialized/unserialized. - * + * * @return void */ public function __wakeup() @@ -310,30 +315,27 @@ class ClassMetadata extends ClassMetadataInfo $this->reflClass = new ReflectionClass($this->name); foreach ($this->fieldMappings as $field => $mapping) { - if (isset($mapping['declared'])) { - $reflField = new ReflectionProperty($mapping['declared'], $field); - } else { - $reflField = $this->reflClass->getProperty($field); - } + $reflField = isset($mapping['declared']) + ? new ReflectionProperty($mapping['declared'], $field) + : $this->reflClass->getProperty($field); + $reflField->setAccessible(true); $this->reflFields[$field] = $reflField; } foreach ($this->associationMappings as $field => $mapping) { - if (isset($mapping['declared'])) { - $reflField = new ReflectionProperty($mapping['declared'], $field); - } else { - $reflField = $this->reflClass->getProperty($field); - } + $reflField = isset($mapping['declared']) + ? new ReflectionProperty($mapping['declared'], $field) + : $this->reflClass->getProperty($field); $reflField->setAccessible(true); $this->reflFields[$field] = $reflField; } } - + /** * Creates a new instance of the mapped class, without invoking the constructor. - * + * * @return object */ public function newInstance() @@ -341,6 +343,7 @@ class ClassMetadata extends ClassMetadataInfo if ($this->_prototype === null) { $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name)); } + return clone $this->_prototype; } @@ -354,6 +357,7 @@ class ClassMetadata extends ClassMetadataInfo ($this->reflClass->getMethod($callback)->getModifiers() & \ReflectionMethod::IS_PUBLIC) == 0) { throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callback); } + return parent::addLifecycleCallback($callback, $event); } } diff --git a/lib/Doctrine/ORM/NativeQuery.php b/lib/Doctrine/ORM/NativeQuery.php index dea223fa3..2ab87441a 100644 --- a/lib/Doctrine/ORM/NativeQuery.php +++ b/lib/Doctrine/ORM/NativeQuery.php @@ -1,4 +1,4 @@ -_sql = $sql; + return $this; } @@ -58,16 +59,18 @@ final class NativeQuery extends AbstractQuery protected function _doExecute() { $params = $this->_params; - $types = $this->_paramTypes; - if ($params) { - if (is_int(key($params))) { - ksort($params); - ksort($types); - $params = array_values($params); - $types = array_values($types); - } + $types = $this->_paramTypes; + + if ($params && is_int(key($params))) { + ksort($params); + ksort($types); + + $params = array_values($params); + $types = array_values($types); } - return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile); + return $this->_em->getConnection()->executeQuery( + $this->_sql, $params, $types, $this->_queryCacheProfile + ); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 7fbafce7b..0aa22df41 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -202,26 +202,30 @@ final class Query extends AbstractQuery return $this->_parserResult; } - // Check query cache. - if ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver())) { - $hash = $this->_getQueryCacheId(); - $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash); + $this->_state = self::STATE_CLEAN; - if ($cached === false) { - // Cache miss. - $parser = new Parser($this); - $this->_parserResult = $parser->parse(); - $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL); - } else { - // Cache hit. - $this->_parserResult = $cached; - } - } else { + // Check query cache. + if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) { $parser = new Parser($this); $this->_parserResult = $parser->parse(); + + return $this->_parserResult; } - $this->_state = self::STATE_CLEAN; + $hash = $this->_getQueryCacheId(); + $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash); + + if ($cached !== false) { + // Cache hit. + $this->_parserResult = $cached; + + return $this->_parserResult; + } + + // Cache miss. + $parser = new Parser($this); + $this->_parserResult = $parser->parse(); + $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL); return $this->_parserResult; } @@ -232,6 +236,7 @@ final class Query extends AbstractQuery protected function _doExecute() { $executor = $this->_parse()->getSqlExecutor(); + if ($this->_queryCacheProfile) { $executor->setQueryCacheProfile($this->_queryCacheProfile); } @@ -339,6 +344,7 @@ final class Query extends AbstractQuery public function setQueryCacheDriver($queryCache) { $this->_queryCache = $queryCache; + return $this; } @@ -351,6 +357,7 @@ final class Query extends AbstractQuery public function useQueryCache($bool) { $this->_useQueryCache = $bool; + return $this; } @@ -364,9 +371,9 @@ final class Query extends AbstractQuery { if ($this->_queryCache) { return $this->_queryCache; - } else { - return $this->_em->getConfiguration()->getQueryCacheImpl(); } + + return $this->_em->getConfiguration()->getQueryCacheImpl(); } /** @@ -380,6 +387,7 @@ final class Query extends AbstractQuery if ($timeToLive !== null) { $timeToLive = (int) $timeToLive; } + $this->_queryCacheTTL = $timeToLive; return $this; @@ -424,6 +432,7 @@ final class Query extends AbstractQuery public function free() { parent::free(); + $this->_dql = null; $this->_state = self::STATE_CLEAN; } @@ -440,6 +449,7 @@ final class Query extends AbstractQuery $this->_dql = $dqlQuery; $this->_state = self::STATE_DIRTY; } + return $this; } @@ -489,6 +499,7 @@ final class Query extends AbstractQuery { $this->_firstResult = $firstResult; $this->_state = self::STATE_DIRTY; + return $this; } @@ -513,6 +524,7 @@ final class Query extends AbstractQuery { $this->_maxResults = $maxResults; $this->_state = self::STATE_DIRTY; + return $this; } @@ -538,6 +550,7 @@ final class Query extends AbstractQuery public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) { $this->setHint(self::HINT_INTERNAL_ITERATION, true); + return parent::iterate($params, $hydrationMode); } @@ -547,6 +560,7 @@ final class Query extends AbstractQuery public function setHint($name, $value) { $this->_state = self::STATE_DIRTY; + return parent::setHint($name, $value); } @@ -556,6 +570,7 @@ final class Query extends AbstractQuery public function setHydrationMode($hydrationMode) { $this->_state = self::STATE_DIRTY; + return parent::setHydrationMode($hydrationMode); } @@ -568,13 +583,14 @@ final class Query extends AbstractQuery */ public function setLockMode($lockMode) { - if ($lockMode == LockMode::PESSIMISTIC_READ || $lockMode == LockMode::PESSIMISTIC_WRITE) { - if (!$this->_em->getConnection()->isTransactionActive()) { + if ($lockMode === LockMode::PESSIMISTIC_READ || $lockMode === LockMode::PESSIMISTIC_WRITE) { + if ( ! $this->_em->getConnection()->isTransactionActive()) { throw TransactionRequiredException::transactionRequired(); } } $this->setHint(self::HINT_LOCK_MODE, $lockMode); + return $this; } @@ -586,9 +602,11 @@ final class Query extends AbstractQuery public function getLockMode() { $lockMode = $this->getHint(self::HINT_LOCK_MODE); - if (!$lockMode) { + + if ( ! $lockMode) { return LockMode::NONE; } + return $lockMode; } @@ -618,6 +636,7 @@ final class Query extends AbstractQuery public function __clone() { parent::__clone(); + $this->_state = self::STATE_DIRTY; } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php index d033eaff2..21143214b 100644 --- a/lib/Doctrine/ORM/QueryBuilder.php +++ b/lib/Doctrine/ORM/QueryBuilder.php @@ -355,12 +355,9 @@ class QueryBuilder public function setParameters(array $params, array $types = array()) { foreach ($params as $key => $value) { - if (isset($types[$key])) { - $this->setParameter($key, $value, $types[$key]); - } else { - $this->setParameter($key, $value); - } + $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null); } + return $this; } @@ -394,6 +391,7 @@ class QueryBuilder public function setFirstResult($firstResult) { $this->_firstResult = $firstResult; + return $this; } @@ -417,6 +415,7 @@ class QueryBuilder public function setMaxResults($maxResults) { $this->_maxResults = $maxResults; + return $this; } @@ -652,13 +651,16 @@ class QueryBuilder public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) { $rootAlias = substr($join, 0, strpos($join, '.')); - if (!in_array($rootAlias, $this->getRootAliases())) { + + if ( ! in_array($rootAlias, $this->getRootAliases())) { $rootAlias = $this->getRootAlias(); } - return $this->add('join', array( - $rootAlias => new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy) - ), true); + $join = new Expr\Join( + Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); } /** @@ -685,13 +687,16 @@ class QueryBuilder public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) { $rootAlias = substr($join, 0, strpos($join, '.')); - if (!in_array($rootAlias, $this->getRootAliases())) { + + if ( ! in_array($rootAlias, $this->getRootAliases())) { $rootAlias = $this->getRootAlias(); } - return $this->add('join', array( - $rootAlias => new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy) - ), true); + $join = new Expr\Join( + Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy + ); + + return $this->add('join', array($rootAlias => $join), true); } /** diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 64f0608e7..25d45fcd0 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1790,7 +1790,7 @@ class UnitOfWork implements PropertyChangedListener public function detach($entity) { $visited = array(); - + $this->doDetach($entity, $visited); } @@ -1804,7 +1804,7 @@ class UnitOfWork implements PropertyChangedListener private function doDetach($entity, array &$visited, $noCascade = false) { $oid = spl_object_hash($entity); - + if (isset($visited[$oid])) { return; // Prevent infinite recursion } @@ -1816,13 +1816,13 @@ class UnitOfWork implements PropertyChangedListener if ($this->isInIdentityMap($entity)) { $this->removeFromIdentityMap($entity); } - + unset( - $this->entityInsertions[$oid], + $this->entityInsertions[$oid], $this->entityUpdates[$oid], - $this->entityDeletions[$oid], + $this->entityDeletions[$oid], $this->entityIdentifiers[$oid], - $this->entityStates[$oid], + $this->entityStates[$oid], $this->originalEntityData[$oid] ); break; @@ -1846,7 +1846,7 @@ class UnitOfWork implements PropertyChangedListener public function refresh($entity) { $visited = array(); - + $this->doRefresh($entity, $visited); } @@ -1860,7 +1860,7 @@ class UnitOfWork implements PropertyChangedListener private function doRefresh($entity, array &$visited) { $oid = spl_object_hash($entity); - + if (isset($visited[$oid])) { return; // Prevent infinite recursion } @@ -1868,11 +1868,11 @@ class UnitOfWork implements PropertyChangedListener $visited[$oid] = $entity; // mark visited $class = $this->em->getClassMetadata(get_class($entity)); - + if ($this->getEntityState($entity) !== self::STATE_MANAGED) { throw new InvalidArgumentException("Entity is not MANAGED."); } - + $this->getEntityPersister($class->name)->refresh( array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), $entity @@ -1890,31 +1890,32 @@ class UnitOfWork implements PropertyChangedListener private function cascadeRefresh($entity, array &$visited) { $class = $this->em->getClassMetadata(get_class($entity)); - - foreach ($class->associationMappings as $assoc) { - if ( ! $assoc['isCascadeRefresh']) { - continue; - } - + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRefresh']; } + ); + + foreach ($associationMappings as $assoc) { $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); - + switch (true) { case ($relatedEntities instanceof PersistentCollection): // Unwrap so that foreach() does not initialize $relatedEntities = $relatedEntities->unwrap(); // break; is commented intentionally! - + case ($relatedEntities instanceof Collection): case (is_array($relatedEntities)): foreach ($relatedEntities as $relatedEntity) { $this->doRefresh($relatedEntity, $visited); } break; - + case ($relatedEntities !== null): $this->doRefresh($relatedEntities, $visited); break; - + default: // Do nothing } @@ -1930,31 +1931,32 @@ class UnitOfWork implements PropertyChangedListener private function cascadeDetach($entity, array &$visited) { $class = $this->em->getClassMetadata(get_class($entity)); - - foreach ($class->associationMappings as $assoc) { - if ( ! $assoc['isCascadeDetach']) { - continue; - } - + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeDetach']; } + ); + + foreach ($associationMappings as $assoc) { $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); - + switch (true) { case ($relatedEntities instanceof PersistentCollection): // Unwrap so that foreach() does not initialize $relatedEntities = $relatedEntities->unwrap(); // break; is commented intentionally! - + case ($relatedEntities instanceof Collection): case (is_array($relatedEntities)): foreach ($relatedEntities as $relatedEntity) { $this->doDetach($relatedEntity, $visited); } break; - + case ($relatedEntities !== null): $this->doDetach($relatedEntities, $visited); break; - + default: // Do nothing } @@ -1971,11 +1973,15 @@ class UnitOfWork implements PropertyChangedListener private function cascadeMerge($entity, $managedCopy, array &$visited) { $class = $this->em->getClassMetadata(get_class($entity)); - foreach ($class->associationMappings as $assoc) { - if ( ! $assoc['isCascadeMerge']) { - continue; - } + + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeMerge']; } + ); + + foreach ($associationMappings as $assoc) { $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); + if ($relatedEntities instanceof Collection) { if ($relatedEntities === $class->reflFields[$assoc['fieldName']]->getValue($managedCopy)) { continue; @@ -1985,6 +1991,7 @@ class UnitOfWork implements PropertyChangedListener // Unwrap so that foreach() does not initialize $relatedEntities = $relatedEntities->unwrap(); } + foreach ($relatedEntities as $relatedEntity) { $this->doMerge($relatedEntity, $visited, $managedCopy, $assoc); } @@ -2004,31 +2011,32 @@ class UnitOfWork implements PropertyChangedListener private function cascadePersist($entity, array &$visited) { $class = $this->em->getClassMetadata(get_class($entity)); - - foreach ($class->associationMappings as $assoc) { - if ( ! $assoc['isCascadePersist']) { - continue; - } + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadePersist']; } + ); + + foreach ($associationMappings as $assoc) { $relatedEntities = $class->reflFields[$assoc['fieldName']]->getValue($entity); - + switch (true) { case ($relatedEntities instanceof PersistentCollection): // Unwrap so that foreach() does not initialize $relatedEntities = $relatedEntities->unwrap(); // break; is commented intentionally! - + case ($relatedEntities instanceof Collection): case (is_array($relatedEntities)): foreach ($relatedEntities as $relatedEntity) { $this->doPersist($relatedEntity, $visited); } break; - + case ($relatedEntities !== null): $this->doPersist($relatedEntities, $visited); break; - + default: // Do nothing } @@ -2045,11 +2053,12 @@ class UnitOfWork implements PropertyChangedListener { $class = $this->em->getClassMetadata(get_class($entity)); - foreach ($class->associationMappings as $assoc) { - if ( ! $assoc['isCascadeRemove']) { - continue; - } + $associationMappings = array_filter( + $class->associationMappings, + function ($assoc) { return $assoc['isCascadeRemove']; } + ); + foreach ($associationMappings as $assoc) { if ($entity instanceof Proxy && !$entity->__isInitialized__) { $entity->__load(); } @@ -2064,11 +2073,11 @@ class UnitOfWork implements PropertyChangedListener $this->doRemove($relatedEntity, $visited); } break; - + case ($relatedEntities !== null): $this->doRemove($relatedEntities, $visited); break; - + default: // Do nothing } @@ -2100,15 +2109,15 @@ class UnitOfWork implements PropertyChangedListener if ($lockVersion === null) { return; } - + $entityVersion = $class->reflFields[$class->versionField]->getValue($entity); - + if ($entityVersion != $lockVersion) { throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion); } - + break; - + case \Doctrine\DBAL\LockMode::PESSIMISTIC_READ: case \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE: if (!$this->em->getConnection()->isTransactionActive()) { @@ -2122,7 +2131,7 @@ class UnitOfWork implements PropertyChangedListener $lockMode ); break; - + default: // Do nothing } @@ -2138,6 +2147,7 @@ class UnitOfWork implements PropertyChangedListener if ($this->commitOrderCalculator === null) { $this->commitOrderCalculator = new Internal\CommitOrderCalculator; } + return $this->commitOrderCalculator; } @@ -2226,9 +2236,11 @@ class UnitOfWork implements PropertyChangedListener private function newInstance($class) { $entity = $class->newInstance(); + if ($entity instanceof \Doctrine\Common\Persistence\ObjectManagerAware) { $entity->injectObjectManager($this->em, $class); } + return $entity; } @@ -2252,34 +2264,30 @@ class UnitOfWork implements PropertyChangedListener if ($class->isIdentifierComposite) { $id = array(); - + foreach ($class->identifier as $fieldName) { - if (isset($class->associationMappings[$fieldName])) { - $id[$fieldName] = $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']]; - } else { - $id[$fieldName] = $data[$fieldName]; - } + $id[$fieldName] = isset($class->associationMappings[$fieldName]) + ? $data[$class->associationMappings[$fieldName]['joinColumns'][0]['name']] + : $data[$fieldName]; } - + $idHash = implode(' ', $id); } else { - if (isset($class->associationMappings[$class->identifier[0]])) { - $idHash = $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']]; - } else { - $idHash = $data[$class->identifier[0]]; - } - + $idHash = isset($class->associationMappings[$class->identifier[0]]) + ? $data[$class->associationMappings[$class->identifier[0]]['joinColumns'][0]['name']] + : $data[$class->identifier[0]]; + $id = array($class->identifier[0] => $idHash); } if (isset($this->identityMap[$class->rootEntityName][$idHash])) { $entity = $this->identityMap[$class->rootEntityName][$idHash]; $oid = spl_object_hash($entity); - + if ($entity instanceof Proxy && ! $entity->__isInitialized__) { $entity->__isInitialized__ = true; $overrideLocalValues = true; - + if ($entity instanceof NotifyPropertyChanged) { $entity->addPropertyChangedListener($this); } @@ -2303,23 +2311,23 @@ class UnitOfWork implements PropertyChangedListener } else { $entity = $this->newInstance($class); $oid = spl_object_hash($entity); - + $this->entityIdentifiers[$oid] = $id; $this->entityStates[$oid] = self::STATE_MANAGED; $this->originalEntityData[$oid] = $data; $this->identityMap[$class->rootEntityName][$idHash] = $entity; - + if ($entity instanceof NotifyPropertyChanged) { $entity->addPropertyChangedListener($this); } - + $overrideLocalValues = true; } if ( ! $overrideLocalValues) { return $entity; } - + foreach ($data as $field => $value) { if (isset($class->fieldMappings[$field])) { $class->reflFields[$field]->setValue($entity, $value); @@ -2337,7 +2345,7 @@ class UnitOfWork implements PropertyChangedListener if (isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) { return $entity; } - + foreach ($class->associationMappings as $field => $assoc) { // Check if the association is not among the fetch-joined associations already. if (isset($hints['fetchAlias']) && isset($hints['fetched'][$hints['fetchAlias']][$field])) {