diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php new file mode 100644 index 0000000000000..84ecec3dd1961 --- /dev/null +++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Aaron Scherer + * @author Tobias Nyholm + * @author Nicolas Grekas + */ +class TraceableAdapter implements AdapterInterface +{ + private $pool; + private $calls = array(); + + public function __construct(AdapterInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $event = $this->start(__FUNCTION__, $key); + try { + $item = $this->pool->getItem($key); + } finally { + $event->end = microtime(true); + } + if ($item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + $event->result = $item->get(); + + return $item; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + $event = $this->start(__FUNCTION__, $key); + try { + return $event->result = $this->pool->hasItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + $event = $this->start(__FUNCTION__, $key); + try { + return $event->result = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__, $item); + try { + return $event->result = $this->pool->save($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__, $item); + try { + return $event->result = $this->pool->saveDeferred($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + $event = $this->start(__FUNCTION__, $keys); + try { + $result = $this->pool->getItems($keys); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event) { + $event->result = array(); + foreach ($result as $key => $item) { + if ($item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + $event->result[$key] = $item->get(); + yield $key => $item; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $event = $this->start(__FUNCTION__, $keys); + try { + return $event->result = $this->pool->deleteItems($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->commit(); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + try { + return $this->calls; + } finally { + $this->calls = array(); + } + } + + private function start($name, $argument = null) + { + $this->calls[] = $event = new TraceableAdapterEvent(); + $event->name = $name; + $event->argument = $argument; + $event->start = microtime(true); + + return $event; + } +} + +class TraceableAdapterEvent +{ + public $name; + public $argument; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php new file mode 100644 index 0000000000000..ad55218b0d07a --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\FilesystemAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapter; + +/** + * @group time-sensitive + */ +class TraceableAdapterTest extends AdapterTestCase +{ + public function createCachePool($defaultLifetime = 0) + { + return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime)); + } + + public function testGetItemMiss() + { + $pool = $this->createCachePool(); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('getItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(1, $call->misses); + $this->assertNull($call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testGetItemHit() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertEquals(1, $call->hits); + $this->assertEquals(0, $call->misses); + } + + public function testGetItemsMiss() + { + $pool = $this->createCachePool(); + $arg = array('k0', 'k1'); + $items = $pool->getItems($arg); + foreach ($items as $item) { + } + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('getItems', $call->name); + $this->assertEquals($arg, $call->argument); + $this->assertEquals(2, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemMiss() + { + $pool = $this->createCachePool(); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('hasItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertFalse($call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemHit() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertEquals('hasItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertTrue($call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItem() + { + $pool = $this->createCachePool(); + $pool->deleteItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('deleteItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItems() + { + $pool = $this->createCachePool(); + $arg = array('k0', 'k1'); + $pool->deleteItems($arg); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('deleteItems', $call->name); + $this->assertEquals($arg, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSave() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertEquals('save', $call->name); + $this->assertEquals($item, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSaveDeferred() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->saveDeferred($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertEquals('saveDeferred', $call->name); + $this->assertEquals($item, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testCommit() + { + $pool = $this->createCachePool(); + $pool->commit(); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('commit', $call->name); + $this->assertNull(null, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +}