Skip to content

Commit

Permalink
Merge tag 'v2.5.6'
Browse files Browse the repository at this point in the history
This release relaxes [`doctrine/common`](https://github.com/doctrine/common) requirements
in order to allow installation of versions that support PHP 7.1 features in proxy class
generation. Please note that a similar requirement relaxation still needs to be applied to
[`doctrine/dbal`](https://github.com/doctrine/dbal) in order to allow installation of
the latest `doctrine/common` versions. [doctrine#6156](doctrine#6156)

This version also backports some fixes around the eviction of the second level cache entries
of inverse side associations in one-to-many - many-to-one mappings. [doctrine#6159](doctrine#6159)

Further fixes were applied in order to have child classes in inheritance mapping share the
same timestamp region when the second level cache is enabled. [doctrine#6028](doctrine#6028)

Also, `Doctrine\ORM\EntityManager#merge()` now triggers `Doctrine\ORM\Events::prePersist`
listeners with the merged entity state whenever an internal `Doctrine\ORM\UnitOfWork#persist()`
call is implied. [doctrine#6177](doctrine#6177).

Total issues resolved: **8**

- [5570: Fix PrePersist EventListener when using merge instead of persist](doctrine#5570)
- [6028: Make child entity share the timestamp region with parent class](doctrine#6028)
- [6110: Clear $this->collection even when empty, to reset keys](doctrine#6110)
- [6156: Allow doctrine/common 2.7](doctrine#6156)
- [6159: doctrine#5821 Backport doctrine#1551 - Fixed support for inverse side second level cache](doctrine#6159)
- [6174: Merging a new entity with PrePersist event make changes in callback not be considered](doctrine#6174)
- [6177: Fix doctrine#6174 doctrine#5570: merging new entities should also trigger prepersist lifecycle callbacks with merged entity data](doctrine#6177)
- [6178: Backport doctrine#6177 - fix doctrine#6174 doctrine#5570: merging new entities should also trigger prepersist lifecycle callbacks with the merged data](doctrine#6178)

# gpg: directory `/c/Users/PC/.gnupg' created
# gpg: new configuration file `/c/Users/PC/.gnupg/gpg.conf' created
# gpg: WARNING: options in `/c/Users/PC/.gnupg/gpg.conf' are not yet active during this run
# gpg: keyring `/c/Users/PC/.gnupg/pubring.gpg' created
# gpg: Signature made Tue Dec 20 00:49:05 2016 FLEST using DSA key ID 12EC2DF8
# gpg: Can't check signature: public key not found

# Conflicts:
#	lib/Doctrine/ORM/Event/LoadClassMetadataEventArgs.php
#	lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php
#	tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php
#	tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryOutputWalkerTest.php
#	tests/Doctrine/Tests/ORM/Tools/Pagination/PaginationTestCase.php
  • Loading branch information
AlexKryvets committed Dec 23, 2016
2 parents e64c123 + e6c4341 commit 17f9bb2
Show file tree
Hide file tree
Showing 80 changed files with 1,812 additions and 184 deletions.
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ script:
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml $PHPUNIT_FLAGS
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional

after_script:
- php vendor/bin/coveralls -v

matrix:
exclude:
- php: hhvm
Expand Down
10 changes: 10 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Upgrade to 2.5

## Minor BC BREAK: query cache key time is now a float

As of 2.5.5, the `QueryCacheEntry#time` property will contain a float value
instead of an integer in order to have more precision and also to be consistent
with the `TimestampCacheEntry#time`.

## Minor BC BREAK: discriminator map must now include all non-transient classes

It is now required that you declare the root of an inheritance in the
Expand Down Expand Up @@ -138,6 +144,10 @@ From now on, the resultset will look like this:
...
)

## Minor BC BREAK: added second parameter $indexBy in EntityRepository#createQueryBuilder method signature

Added way to access the underlying QueryBuilder#from() method's 'indexBy' parameter when using EntityRepository#createQueryBuilder()

# Upgrade to 2.4

## BC BREAK: Compatibility Bugfix in PersistentCollection#matching()
Expand Down
9 changes: 4 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,13 @@
"doctrine/collections": "~1.2",
"doctrine/dbal": "dev-odbc",
"doctrine/instantiator": "~1.0.1",
"doctrine/common": ">=2.5-dev,<2.6-dev",
"doctrine/common": ">=2.5-dev,<2.8-dev",
"doctrine/cache": "~1.4",
"symfony/console": "~2.5"
"symfony/console": "~2.5|~3.0"
},
"require-dev": {
"symfony/yaml": "~2.1",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "dev-master"
"symfony/yaml": "~2.3|~3.0",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"symfony/yaml": "If you want to use YAML Metadata Mapping Driver"
Expand Down
36 changes: 29 additions & 7 deletions lib/Doctrine/ORM/AbstractQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
use Doctrine\DBAL\Cache\QueryCacheProfile;

use Doctrine\ORM\Cache;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\ResultSetMapping;

/**
* Base contract for ORM queries. Base class for Query and NativeQuery.
Expand Down Expand Up @@ -993,32 +993,54 @@ private function executeIgnoreQueryCache($parameters = null, $hydrationMode = nu
private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
{
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL);
$queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion);
$result = $queryCache->get($querykey, $rsm, $this->_hints);
$queryKey = new QueryCacheKey(
$this->getHash(),
$this->lifetime,
$this->cacheMode ?: Cache::MODE_NORMAL,
$this->getTimestampKey()
);

$result = $queryCache->get($queryKey, $rsm, $this->_hints);

if ($result !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $querykey);
$this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
}

return $result;
}

$result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);
$cached = $queryCache->put($querykey, $rsm, $result, $this->_hints);
$cached = $queryCache->put($queryKey, $rsm, $result, $this->_hints);

if ($this->cacheLogger) {
$this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $querykey);
$this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);

if ($cached) {
$this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $querykey);
$this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
}
}

return $result;
}

/**
* @return \Doctrine\ORM\Cache\TimestampCacheKey|null
*/
private function getTimestampKey()
{
$entityName = reset($this->_resultSetMapping->aliasMap);

if (empty($entityName)) {
return null;
}

$metadata = $this->_em->getClassMetadata($entityName);

return new Cache\TimestampCacheKey($metadata->rootEntityName);
}

/**
* Get the result cache id to use to store the result set cache entry.
* Will return the configured id if it exists otherwise a hash will be
Expand Down
4 changes: 3 additions & 1 deletion lib/Doctrine/ORM/Cache/CacheConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ public function setRegionsConfiguration(RegionsConfiguration $regionsConfig)
public function getQueryValidator()
{
if ($this->queryValidator === null) {
$this->queryValidator = new TimestampQueryCacheValidator();
$this->queryValidator = new TimestampQueryCacheValidator(
$this->cacheFactory->getTimestampRegion()
);
}

return $this->queryValidator;
Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ORM/Cache/DefaultEntityHydrator.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public function __construct(EntityManagerInterface $em)
public function buildCacheEntry(ClassMetadata $metadata, EntityCacheKey $key, $entity)
{
$data = $this->uow->getOriginalEntityData($entity);
$data = array_merge($data, $key->identifier); // why update has no identifier values ?
$data = array_merge($data, $metadata->getIdentifierValues($entity)); // why update has no identifier values ?
foreach ($metadata->associationMappings as $name => $assoc) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

/**
* @author Fabio B. Silva <[email protected]>
* @author Guilherme Blanco <[email protected]>
* @since 2.5
*/
abstract class AbstractCollectionPersister implements CachedCollectionPersister
Expand Down Expand Up @@ -164,10 +165,18 @@ public function loadCollectionCache(PersistentCollection $collection, Collection
public function storeCollectionCache(CollectionCacheKey $key, $elements)
{
/* @var $targetPersister CachedEntityPersister */
$associationMapping = $this->sourceEntity->associationMappings[$key->association];
$targetPersister = $this->uow->getEntityPersister($this->targetEntity->rootEntityName);
$targetRegion = $targetPersister->getCacheRegion();
$targetHydrator = $targetPersister->getEntityHydrator();
$entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);

// Only preserve ordering if association configured it
if ( ! (isset($associationMapping['indexBy']) && $associationMapping['indexBy'])) {
// Elements may be an array or a Collection
$elements = array_values(is_array($elements) ? $elements : $elements->getValues());
}

$entry = $this->hydrator->buildCacheEntry($this->targetEntity, $key, $elements);

foreach ($entry->identifiers as $index => $entityKey) {
if ($targetRegion->contains($entityKey)) {
Expand Down
55 changes: 27 additions & 28 deletions lib/Doctrine/ORM/Cache/Persister/Entity/AbstractEntityPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public function __construct(EntityPersister $persister, Region $region, EntityMa
$this->cacheLogger = $cacheConfig->getCacheLogger();
$this->timestampRegion = $cacheFactory->getTimestampRegion();
$this->hydrator = $cacheFactory->buildEntityHydrator($em, $class);
$this->timestampKey = new TimestampCacheKey($this->class->getTableName());
$this->timestampKey = new TimestampCacheKey($this->class->rootEntityName);
}

/**
Expand Down Expand Up @@ -287,17 +287,16 @@ private function storeJoinedAssociations($entity)
* @param array $orderBy
* @param integer $limit
* @param integer $offset
* @param integer $timestamp
*
* @return string
*/
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null, $timestamp = null)
protected function getHash($query, $criteria, array $orderBy = null, $limit = null, $offset = null)
{
list($params) = ($criteria instanceof Criteria)
? $this->persister->expandCriteriaParameters($criteria)
: $this->persister->expandParameters($criteria);

return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset . $timestamp);
return sha1($query . serialize($params) . serialize($orderBy) . $limit . $offset);
}

/**
Expand Down Expand Up @@ -368,17 +367,16 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint
}

//handle only EntityRepository#findOneBy
$timestamp = $this->timestampRegion->get($this->timestampKey);
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, null, $orderBy);
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
$hash = $this->getHash($query, $criteria, null, null, null);
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
$queryCache = $this->cache->getQueryCache($this->regionName);
$result = $queryCache->get($querykey, $rsm);
$result = $queryCache->get($queryKey, $rsm);

if ($result !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
}

return $result[0];
Expand All @@ -388,15 +386,15 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint
return null;
}

$cached = $queryCache->put($querykey, $rsm, array($result));
$cached = $queryCache->put($queryKey, $rsm, array($result));

if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}

if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
}
}

Expand All @@ -408,32 +406,31 @@ public function load(array $criteria, $entity = null, $assoc = null, array $hint
*/
public function loadAll(array $criteria = array(), array $orderBy = null, $limit = null, $offset = null)
{
$timestamp = $this->timestampRegion->get($this->timestampKey);
$query = $this->persister->getSelectSQL($criteria, null, null, $limit, $offset, $orderBy);
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
$hash = $this->getHash($query, $criteria, null, null, null);
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
$queryCache = $this->cache->getQueryCache($this->regionName);
$result = $queryCache->get($querykey, $rsm);
$result = $queryCache->get($queryKey, $rsm);

if ($result !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
}

return $result;
}

$result = $this->persister->loadAll($criteria, $orderBy, $limit, $offset);
$cached = $queryCache->put($querykey, $rsm, $result);
$cached = $queryCache->put($queryKey, $rsm, $result);

if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}

if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
}
}

Expand Down Expand Up @@ -507,32 +504,34 @@ public function count($criteria = array())
*/
public function loadCriteria(Criteria $criteria)
{
$orderBy = $criteria->getOrderings();
$limit = $criteria->getMaxResults();
$offset = $criteria->getFirstResult();
$query = $this->persister->getSelectSQL($criteria);
$timestamp = $this->timestampRegion->get($this->timestampKey);
$hash = $this->getHash($query, $criteria, null, null, null, $timestamp ? $timestamp->time : null);
$hash = $this->getHash($query, $criteria, $orderBy, $limit, $offset);
$rsm = $this->getResultSetMapping();
$querykey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL);
$queryKey = new QueryCacheKey($hash, 0, Cache::MODE_NORMAL, $this->timestampKey);
$queryCache = $this->cache->getQueryCache($this->regionName);
$cacheResult = $queryCache->get($querykey, $rsm);
$cacheResult = $queryCache->get($queryKey, $rsm);

if ($cacheResult !== null) {
if ($this->cacheLogger) {
$this->cacheLogger->queryCacheHit($this->regionName, $querykey);
$this->cacheLogger->queryCacheHit($this->regionName, $queryKey);
}

return $cacheResult;
}

$result = $this->persister->loadCriteria($criteria);
$cached = $queryCache->put($querykey, $rsm, $result);
$cached = $queryCache->put($queryKey, $rsm, $result);

if ($this->cacheLogger) {
if ($result) {
$this->cacheLogger->queryCacheMiss($this->regionName, $querykey);
$this->cacheLogger->queryCacheMiss($this->regionName, $queryKey);
}

if ($cached) {
$this->cacheLogger->queryCachePut($this->regionName, $querykey);
$this->cacheLogger->queryCachePut($this->regionName, $queryKey);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* Specific non-strict read/write cached entity persister
*
* @author Fabio B. Silva <[email protected]>
* @author Guilherme Blanco <[email protected]>
* @since 2.5
*/
class NonStrictReadWriteCachedEntityPersister extends AbstractEntityPersister
Expand Down Expand Up @@ -77,13 +78,16 @@ public function afterTransactionRolledBack()
*/
public function delete($entity)
{
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$deleted = $this->persister->delete($entity);

if ($this->persister->delete($entity)) {
if ($deleted) {
$this->region->evict($key);
}

$this->queuedCache['delete'][] = $key;

return $deleted;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
* Specific read-write entity persister
*
* @author Fabio B. Silva <[email protected]>
* @author Guilherme Blanco <[email protected]>
* @since 2.5
*/
class ReadWriteCachedEntityPersister extends AbstractEntityPersister
Expand Down Expand Up @@ -100,21 +101,24 @@ public function afterTransactionRolledBack()
*/
public function delete($entity)
{
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$lock = $this->region->lock($key);
$key = new EntityCacheKey($this->class->rootEntityName, $this->uow->getEntityIdentifier($entity));
$lock = $this->region->lock($key);
$deleted = $this->persister->delete($entity);

if ($this->persister->delete($entity)) {
if ($deleted) {
$this->region->evict($key);
}

if ($lock === null) {
return;
return $deleted;
}

$this->queuedCache['delete'][] = array(
'lock' => $lock,
'key' => $key
);

return $deleted;
}

/**
Expand Down
Loading

0 comments on commit 17f9bb2

Please sign in to comment.