TraceableAdapter.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Cache\Adapter;
  11. use Psr\Cache\CacheItemInterface;
  12. use Symfony\Component\Cache\CacheItem;
  13. use Symfony\Component\Cache\PruneableInterface;
  14. use Symfony\Component\Cache\ResettableInterface;
  15. use Symfony\Contracts\Cache\CacheInterface;
  16. use Symfony\Contracts\Service\ResetInterface;
  17. /**
  18. * An adapter that collects data about all cache calls.
  19. *
  20. * @author Aaron Scherer <aequasi@gmail.com>
  21. * @author Tobias Nyholm <tobias.nyholm@gmail.com>
  22. * @author Nicolas Grekas <p@tchwork.com>
  23. */
  24. class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
  25. {
  26. protected $pool;
  27. private array $calls = [];
  28. public function __construct(AdapterInterface $pool)
  29. {
  30. $this->pool = $pool;
  31. }
  32. /**
  33. * {@inheritdoc}
  34. */
  35. public function get(string $key, callable $callback, float $beta = null, array &$metadata = null): mixed
  36. {
  37. if (!$this->pool instanceof CacheInterface) {
  38. throw new \BadMethodCallException(sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', get_debug_type($this->pool), CacheInterface::class));
  39. }
  40. $isHit = true;
  41. $callback = function (CacheItem $item, bool &$save) use ($callback, &$isHit) {
  42. $isHit = $item->isHit();
  43. return $callback($item, $save);
  44. };
  45. $event = $this->start(__FUNCTION__);
  46. try {
  47. $value = $this->pool->get($key, $callback, $beta, $metadata);
  48. $event->result[$key] = get_debug_type($value);
  49. } finally {
  50. $event->end = microtime(true);
  51. }
  52. if ($isHit) {
  53. ++$event->hits;
  54. } else {
  55. ++$event->misses;
  56. }
  57. return $value;
  58. }
  59. /**
  60. * {@inheritdoc}
  61. */
  62. public function getItem(mixed $key): CacheItem
  63. {
  64. $event = $this->start(__FUNCTION__);
  65. try {
  66. $item = $this->pool->getItem($key);
  67. } finally {
  68. $event->end = microtime(true);
  69. }
  70. if ($event->result[$key] = $item->isHit()) {
  71. ++$event->hits;
  72. } else {
  73. ++$event->misses;
  74. }
  75. return $item;
  76. }
  77. /**
  78. * {@inheritdoc}
  79. */
  80. public function hasItem(mixed $key): bool
  81. {
  82. $event = $this->start(__FUNCTION__);
  83. try {
  84. return $event->result[$key] = $this->pool->hasItem($key);
  85. } finally {
  86. $event->end = microtime(true);
  87. }
  88. }
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public function deleteItem(mixed $key): bool
  93. {
  94. $event = $this->start(__FUNCTION__);
  95. try {
  96. return $event->result[$key] = $this->pool->deleteItem($key);
  97. } finally {
  98. $event->end = microtime(true);
  99. }
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function save(CacheItemInterface $item): bool
  105. {
  106. $event = $this->start(__FUNCTION__);
  107. try {
  108. return $event->result[$item->getKey()] = $this->pool->save($item);
  109. } finally {
  110. $event->end = microtime(true);
  111. }
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function saveDeferred(CacheItemInterface $item): bool
  117. {
  118. $event = $this->start(__FUNCTION__);
  119. try {
  120. return $event->result[$item->getKey()] = $this->pool->saveDeferred($item);
  121. } finally {
  122. $event->end = microtime(true);
  123. }
  124. }
  125. /**
  126. * {@inheritdoc}
  127. */
  128. public function getItems(array $keys = []): iterable
  129. {
  130. $event = $this->start(__FUNCTION__);
  131. try {
  132. $result = $this->pool->getItems($keys);
  133. } finally {
  134. $event->end = microtime(true);
  135. }
  136. $f = function () use ($result, $event) {
  137. $event->result = [];
  138. foreach ($result as $key => $item) {
  139. if ($event->result[$key] = $item->isHit()) {
  140. ++$event->hits;
  141. } else {
  142. ++$event->misses;
  143. }
  144. yield $key => $item;
  145. }
  146. };
  147. return $f();
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. public function clear(string $prefix = ''): bool
  153. {
  154. $event = $this->start(__FUNCTION__);
  155. try {
  156. if ($this->pool instanceof AdapterInterface) {
  157. return $event->result = $this->pool->clear($prefix);
  158. }
  159. return $event->result = $this->pool->clear();
  160. } finally {
  161. $event->end = microtime(true);
  162. }
  163. }
  164. /**
  165. * {@inheritdoc}
  166. */
  167. public function deleteItems(array $keys): bool
  168. {
  169. $event = $this->start(__FUNCTION__);
  170. $event->result['keys'] = $keys;
  171. try {
  172. return $event->result['result'] = $this->pool->deleteItems($keys);
  173. } finally {
  174. $event->end = microtime(true);
  175. }
  176. }
  177. /**
  178. * {@inheritdoc}
  179. */
  180. public function commit(): bool
  181. {
  182. $event = $this->start(__FUNCTION__);
  183. try {
  184. return $event->result = $this->pool->commit();
  185. } finally {
  186. $event->end = microtime(true);
  187. }
  188. }
  189. /**
  190. * {@inheritdoc}
  191. */
  192. public function prune(): bool
  193. {
  194. if (!$this->pool instanceof PruneableInterface) {
  195. return false;
  196. }
  197. $event = $this->start(__FUNCTION__);
  198. try {
  199. return $event->result = $this->pool->prune();
  200. } finally {
  201. $event->end = microtime(true);
  202. }
  203. }
  204. /**
  205. * {@inheritdoc}
  206. */
  207. public function reset()
  208. {
  209. if ($this->pool instanceof ResetInterface) {
  210. $this->pool->reset();
  211. }
  212. $this->clearCalls();
  213. }
  214. /**
  215. * {@inheritdoc}
  216. */
  217. public function delete(string $key): bool
  218. {
  219. $event = $this->start(__FUNCTION__);
  220. try {
  221. return $event->result[$key] = $this->pool->deleteItem($key);
  222. } finally {
  223. $event->end = microtime(true);
  224. }
  225. }
  226. public function getCalls()
  227. {
  228. return $this->calls;
  229. }
  230. public function clearCalls()
  231. {
  232. $this->calls = [];
  233. }
  234. protected function start(string $name)
  235. {
  236. $this->calls[] = $event = new TraceableAdapterEvent();
  237. $event->name = $name;
  238. $event->start = microtime(true);
  239. return $event;
  240. }
  241. }
  242. /**
  243. * @internal
  244. */
  245. class TraceableAdapterEvent
  246. {
  247. public string $name;
  248. public float $start;
  249. public float $end;
  250. public array|bool $result;
  251. public int $hits = 0;
  252. public int $misses = 0;
  253. }