1
0
mirror of synced 2025-03-05 04:13:20 +03:00

Merge branch 'DDC-217'

This commit is contained in:
Benjamin Eberlei 2011-10-27 22:36:25 +02:00
commit a98c7df2f1
11 changed files with 108 additions and 155 deletions

View File

@ -1,3 +1,17 @@
# ResultCache implementation rewritten
The result cache is completly 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:
1. Fixes the problem that entities coming from the result cache were not registered in the UnitOfWork
leading to problems during EntityManager#flush. Calls to EntityManager#merge are not necessary anymore.
2. Affects the array hydrator which now includes the overhead of hydration compared to caching the final result.
The API is backwards compatible however most of the getter methods on the `AbstractQuery` object are now
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 # EntityManager#getPartialReference() creates read-only entity
Entities returned from EntityManager#getPartialReference() are now marked as read-only if they Entities returned from EntityManager#getPartialReference() are now marked as read-only if they

View File

@ -20,7 +20,8 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type, use Doctrine\DBAL\Types\Type,
Doctrine\ORM\Query\QueryException; Doctrine\ORM\Query\QueryException,
Doctrine\DBAL\Cache\QueryCacheProfile;
/** /**
* Base contract for ORM queries. Base class for Query and NativeQuery. * Base contract for ORM queries. Base class for Query and NativeQuery.
@ -91,34 +92,15 @@ abstract class AbstractQuery
protected $_hydrationMode = self::HYDRATE_OBJECT; protected $_hydrationMode = self::HYDRATE_OBJECT;
/** /**
* The locally set cache driver used for caching result sets of this query. * @param \Doctrine\DBAL\Cache\QueryCacheProfile
*
* @var CacheDriver
*/ */
protected $_resultCacheDriver; protected $_queryCacheProfile;
/**
* Boolean flag for whether or not to cache the results of this query.
*
* @var boolean
*/
protected $_useResultCache;
/**
* @var string The id to store the result cache entry under.
*/
protected $_resultCacheId;
/** /**
* @var boolean Boolean value that indicates whether or not expire the result cache. * @var boolean Boolean value that indicates whether or not expire the result cache.
*/ */
protected $_expireResultCache = false; protected $_expireResultCache = false;
/**
* @var int Result Cache lifetime.
*/
protected $_resultCacheTTL;
/** /**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>. * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
* *
@ -260,7 +242,7 @@ abstract class AbstractQuery
} }
/** /**
* Defines a cache driver to be used for caching result sets. * Defines a cache driver to be used for caching result sets and implictly enables caching.
* *
* @param Doctrine\Common\Cache\Cache $driver Cache driver * @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery * @return Doctrine\ORM\AbstractQuery
@ -270,9 +252,10 @@ abstract class AbstractQuery
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver(); throw ORMException::invalidResultCacheDriver();
} }
$this->_resultCacheDriver = $resultCacheDriver; if ($this->_queryCacheProfile) {
if ($resultCacheDriver) { $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver);
$this->_useResultCache = true; } else {
$this->_queryCacheProfile = new QueryCacheProfile(0, null, $resultCacheDriver);
} }
return $this; return $this;
} }
@ -280,12 +263,13 @@ abstract class AbstractQuery
/** /**
* Returns the cache driver used for caching result sets. * Returns the cache driver used for caching result sets.
* *
* @deprecated
* @return Doctrine\Common\Cache\Cache Cache driver * @return Doctrine\Common\Cache\Cache Cache driver
*/ */
public function getResultCacheDriver() public function getResultCacheDriver()
{ {
if ($this->_resultCacheDriver) { if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
return $this->_resultCacheDriver; return $this->_queryCacheProfile->getResultCacheDriver();
} else { } else {
return $this->_em->getConfiguration()->getResultCacheImpl(); return $this->_em->getConfiguration()->getResultCacheImpl();
} }
@ -296,18 +280,17 @@ abstract class AbstractQuery
* how long and which ID to use for the cache entry. * how long and which ID to use for the cache entry.
* *
* @param boolean $bool * @param boolean $bool
* @param integer $timeToLive * @param integer $lifetime
* @param string $resultCacheId * @param string $resultCacheId
* @return Doctrine\ORM\AbstractQuery This query instance. * @return Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null) public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
{ {
$this->_useResultCache = $bool; if ($bool) {
if ($timeToLive) { $this->setResultCacheLifetime($lifetime);
$this->setResultCacheLifetime($timeToLive); $this->setResultCacheId($resultCacheId);
} } else {
if ($resultCacheId) { $this->_queryCacheProfile = null;
$this->_resultCacheId = $resultCacheId;
} }
return $this; return $this;
} }
@ -315,27 +298,33 @@ abstract class AbstractQuery
/** /**
* Defines how long the result cache will be active before expire. * Defines how long the result cache will be active before expire.
* *
* @param integer $timeToLive How long the cache entry is valid. * @param integer $lifetime How long the cache entry is valid.
* @return Doctrine\ORM\AbstractQuery This query instance. * @return Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function setResultCacheLifetime($timeToLive) public function setResultCacheLifetime($lifetime)
{ {
if ($timeToLive !== null) { if ($lifetime === null) {
$timeToLive = (int) $timeToLive; $lifetime = 0;
} else {
$lifetime = (int)$lifetime;
}
if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
} else {
$this->_queryCacheProfile = new QueryCacheProfile($lifetime);
} }
$this->_resultCacheTTL = $timeToLive;
return $this; return $this;
} }
/** /**
* Retrieves the lifetime of resultset cache. * Retrieves the lifetime of resultset cache.
* *
* @deprecated
* @return integer * @return integer
*/ */
public function getResultCacheLifetime() public function getResultCacheLifetime()
{ {
return $this->_resultCacheTTL; return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
} }
/** /**
@ -360,6 +349,14 @@ abstract class AbstractQuery
return $this->_expireResultCache; return $this->_expireResultCache;
} }
/**
* @return QueryCacheProfile
*/
public function getQueryCacheProfile()
{
return $this->_queryCacheProfile;
}
/** /**
* Change the default fetch mode of an association for this query. * Change the default fetch mode of an association for this query.
* *
@ -584,28 +581,6 @@ abstract class AbstractQuery
$this->setParameters($params); $this->setParameters($params);
} }
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
list($key, $hash) = $this->getResultCacheId();
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($hash);
if ($cached === false || !isset($cached[$key])) {
// Cache miss.
$stmt = $this->_doExecute();
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($hash, array($key => $result), $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached[$key];
}
}
$stmt = $this->_doExecute(); $stmt = $this->_doExecute();
if (is_numeric($stmt)) { if (is_numeric($stmt)) {
@ -627,43 +602,23 @@ abstract class AbstractQuery
*/ */
public function setResultCacheId($id) public function setResultCacheId($id)
{ {
$this->_resultCacheId = $id; if ($this->_queryCacheProfile) {
$this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
} else {
$this->_queryCacheProfile = new QueryCacheProfile(0, $id);
}
return $this; return $this;
} }
/** /**
* Get the result cache id to use to store the result set cache entry. * Get the result cache id to use to store the result set cache entry if set.
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
* *
* @return array ($key, $hash) * @deprecated
* @return string
*/ */
protected function getResultCacheId() public function getResultCacheId()
{ {
if ($this->_resultCacheId) { return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
return array($this->_resultCacheId, $this->_resultCacheId);
} else {
$params = $this->_params;
foreach ($params AS $key => $value) {
if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(get_class($value))) {
if ($this->_em->getUnitOfWork()->getEntityState($value) == UnitOfWork::STATE_MANAGED) {
$idValues = $this->_em->getUnitOfWork()->getEntityIdentifier($value);
} else {
$class = $this->_em->getClassMetadata(get_class($value));
$idValues = $class->getIdentifierValues($value);
}
$params[$key] = $idValues;
} else {
$params[$key] = $value;
}
}
$sql = $this->getSql();
ksort($this->_hints);
$key = implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode;
return array($key, md5($key));
}
} }
/** /**

View File

@ -209,27 +209,6 @@ class Configuration extends \Doctrine\DBAL\Configuration
$this->_attributes['metadataDriverImpl'] : null; $this->_attributes['metadataDriverImpl'] : null;
} }
/**
* Gets the cache driver implementation that is used for query result caching.
*
* @return \Doctrine\Common\Cache\Cache
*/
public function getResultCacheImpl()
{
return isset($this->_attributes['resultCacheImpl']) ?
$this->_attributes['resultCacheImpl'] : null;
}
/**
* Sets the cache driver implementation that is used for query result caching.
*
* @param \Doctrine\Common\Cache\Cache $cacheImpl
*/
public function setResultCacheImpl(Cache $cacheImpl)
{
$this->_attributes['resultCacheImpl'] = $cacheImpl;
}
/** /**
* Gets the cache driver implementation that is used for the query cache (SQL cache). * Gets the cache driver implementation that is used for the query cache (SQL cache).
* *

View File

@ -57,17 +57,17 @@ final class NativeQuery extends AbstractQuery
*/ */
protected function _doExecute() protected function _doExecute()
{ {
$stmt = $this->_em->getConnection()->prepare($this->_sql);
$params = $this->_params; $params = $this->_params;
foreach ($params as $key => $value) { $types = $this->_paramTypes;
if (isset($this->_paramTypes[$key])) { if ($params) {
$stmt->bindValue($key, $value, $this->_paramTypes[$key]); if (is_int(key($params))) {
} else { ksort($params);
$stmt->bindValue($key, $value); ksort($types);
$params = array_values($params);
$types = array_values($types);
} }
} }
$stmt->execute();
return $stmt; return $this->_em->getConnection()->executeQuery($this->_sql, $params, $types, $this->_queryCacheProfile);
} }
} }

View File

@ -232,6 +232,9 @@ final class Query extends AbstractQuery
protected function _doExecute() protected function _doExecute()
{ {
$executor = $this->_parse()->getSqlExecutor(); $executor = $this->_parse()->getSqlExecutor();
if ($this->_queryCacheProfile) {
$executor->setQueryCacheProfile($this->_queryCacheProfile);
}
// Prepare parameters // Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings(); $paramMappings = $this->_parserResult->getParameterMappings();

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -22,6 +20,7 @@
namespace Doctrine\ORM\Query\Exec; namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Cache\QueryCacheProfile;
/** /**
* Base class for SQL statement executors. * Base class for SQL statement executors.
@ -34,8 +33,16 @@ use Doctrine\DBAL\Connection;
*/ */
abstract class AbstractSqlExecutor abstract class AbstractSqlExecutor
{ {
/**
* @var array
*/
protected $_sqlStatements; protected $_sqlStatements;
/**
* @var QueryCacheProfile
*/
protected $queryCacheProfile;
/** /**
* Gets the SQL statements that are executed by the executor. * Gets the SQL statements that are executed by the executor.
* *
@ -46,12 +53,18 @@ abstract class AbstractSqlExecutor
return $this->_sqlStatements; return $this->_sqlStatements;
} }
public function setQueryCacheProfile(QueryCacheProfile $qcp)
{
$this->queryCacheProfile = $qcp;
}
/** /**
* Executes all sql statements. * Executes all sql statements.
* *
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries. * @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters. * @param array $params The parameters.
* @param array $types The parameter types.
* @return Doctrine\DBAL\Driver\Statement * @return Doctrine\DBAL\Driver\Statement
*/ */
abstract public function execute(Connection $conn, array $params, array $types); abstract public function execute(Connection $conn, array $params, array $types);
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -32,7 +30,6 @@ use Doctrine\DBAL\Connection,
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.doctrine-project.org * @link http://www.doctrine-project.org
* @since 2.0 * @since 2.0
* @version $Revision$
*/ */
class MultiTableDeleteExecutor extends AbstractSqlExecutor class MultiTableDeleteExecutor extends AbstractSqlExecutor
{ {
@ -102,11 +99,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor
} }
/** /**
* Executes all SQL statements. * {@inheritDoc}
*
* @param Doctrine\DBAL\Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/ */
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {

View File

@ -141,11 +141,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
} }
/** /**
* Executes all SQL statements. * {@inheritDoc}
*
* @param Connection $conn The database connection that is used to execute the queries.
* @param array $params The parameters.
* @override
*/ */
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
*/ */
@ -41,8 +38,11 @@ class SingleSelectExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkSelectStatement($AST); $this->_sqlStatements = $sqlWalker->walkSelectStatement($AST);
} }
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {
return $conn->executeQuery($this->_sqlStatements, $params, $types); return $conn->executeQuery($this->_sqlStatements, $params, $types, $this->queryCacheProfile);
} }
} }

View File

@ -1,7 +1,5 @@
<?php <?php
/* /*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@ -30,7 +28,6 @@ use Doctrine\DBAL\Connection,
* *
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @version $Revision$
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor. * @todo This is exactly the same as SingleSelectExecutor. Unify in SingleStatementExecutor.
@ -45,7 +42,10 @@ class SingleTableDeleteUpdateExecutor extends AbstractSqlExecutor
$this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST); $this->_sqlStatements = $sqlWalker->walkDeleteStatement($AST);
} }
} }
/**
* {@inheritDoc}
*/
public function execute(Connection $conn, array $params, array $types) public function execute(Connection $conn, array $params, array $types)
{ {
return $conn->executeUpdate($this->_sqlStatements, $params, $types); return $conn->executeUpdate($this->_sqlStatements, $params, $types);

View File

@ -90,10 +90,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCache() public function testUseResultCache()
{ {
$cache = new \Doctrine\Common\Cache\ArrayCache(); $cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux');
$query->useResultCache(true); $query->useResultCache(true);
$query->setResultCacheDriver($cache);
$query->setResultCacheId('testing_result_cache_id'); $query->setResultCacheId('testing_result_cache_id');
$users = $query->getResult(); $users = $query->getResult();
@ -108,11 +108,11 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testUseResultCacheParams() public function testUseResultCacheParams()
{ {
$cache = new \Doctrine\Common\Cache\ArrayCache(); $cache = new \Doctrine\Common\Cache\ArrayCache();
$this->_em->getConfiguration()->setResultCacheImpl($cache);
$sqlCount = count($this->_sqlLoggerStack->queries); $sqlCount = count($this->_sqlLoggerStack->queries);
$query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1'); $query = $this->_em->createQuery('select ux from Doctrine\Tests\Models\CMS\CmsUser ux WHERE ux.id = ?1');
$query->setParameter(1, 1); $query->setParameter(1, 1);
$query->setResultCacheDriver($cache);
$query->useResultCache(true); $query->useResultCache(true);
$query->getResult(); $query->getResult();
@ -149,10 +149,10 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
} }
/** /**
* @param <type> $query * @param string $query
* @depends testNativeQueryResultCaching * @depends testNativeQueryResultCaching
*/ */
public function testResultCacheDependsOnQueryHints($query) public function testResultCacheNotDependsOnQueryHints($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache); $cacheCount = $this->getCacheSize($cache);
@ -160,7 +160,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$query->setHint('foo', 'bar'); $query->setHint('foo', 'bar');
$query->getResult(); $query->getResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache)); $this->assertEquals($cacheCount, $this->getCacheSize($cache));
} }
/** /**
@ -182,7 +182,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
* @param <type> $query * @param <type> $query
* @depends testNativeQueryResultCaching * @depends testNativeQueryResultCaching
*/ */
public function testResultCacheDependsOnHydrationMode($query) public function testResultCacheNotDependsOnHydrationMode($query)
{ {
$cache = $query->getResultCacheDriver(); $cache = $query->getResultCacheDriver();
$cacheCount = $this->getCacheSize($cache); $cacheCount = $this->getCacheSize($cache);
@ -190,7 +190,7 @@ class ResultCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode()); $this->assertNotEquals(\Doctrine\ORM\Query::HYDRATE_ARRAY, $query->getHydrationMode());
$query->getArrayResult(); $query->getArrayResult();
$this->assertEquals($cacheCount + 1, $this->getCacheSize($cache)); $this->assertEquals($cacheCount, $this->getCacheSize($cache));
} }
/** /**