<?php
declare(strict_types=1);
namespace Sentry\SentryBundle\Tracing\Cache;
use Psr\Cache\CacheItemInterface;
use Sentry\State\HubInterface;
use Sentry\Tracing\SpanContext;
use Symfony\Component\Cache\Adapter\AdapterInterface;
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\PruneableInterface;
use Symfony\Component\Cache\ResettableInterface;
use Symfony\Contracts\Cache\CacheInterface;
/**
* @internal
*
* @phpstan-template T of AdapterInterface
*/
trait TraceableCacheAdapterTrait
{
/**
* @var HubInterface The current hub
*/
private $hub;
/**
* @var AdapterInterface|TagAwareAdapterInterface The decorated adapter
*
* @phpstan-var T
*/
private $decoratedAdapter;
/**
* {@inheritdoc}
*/
public function getItem($key): CacheItem
{
return $this->traceFunction('cache.get_item', function () use ($key): CacheItem {
return $this->decoratedAdapter->getItem($key);
}, $key);
}
/**
* {@inheritdoc}
*/
public function getItems(array $keys = []): iterable
{
return $this->traceFunction('cache.get_items', function () use ($keys): iterable {
return $this->decoratedAdapter->getItems($keys);
});
}
/**
* {@inheritdoc}
*/
public function clear(string $prefix = ''): bool
{
return $this->traceFunction('cache.clear', function () use ($prefix): bool {
return $this->decoratedAdapter->clear($prefix);
}, $prefix);
}
/**
* {@inheritdoc}
*/
public function delete(string $key): bool
{
return $this->traceFunction('cache.delete_item', function () use ($key): bool {
if (!$this->decoratedAdapter instanceof CacheInterface) {
throw new \BadMethodCallException(sprintf('The %s::delete() method is not supported because the decorated adapter does not implement the "%s" interface.', self::class, CacheInterface::class));
}
return $this->decoratedAdapter->delete($key);
}, $key);
}
/**
* {@inheritdoc}
*/
public function hasItem($key): bool
{
return $this->traceFunction('cache.has_item', function () use ($key): bool {
return $this->decoratedAdapter->hasItem($key);
}, $key);
}
/**
* {@inheritdoc}
*/
public function deleteItem($key): bool
{
return $this->traceFunction('cache.delete_item', function () use ($key): bool {
return $this->decoratedAdapter->deleteItem($key);
}, $key);
}
/**
* {@inheritdoc}
*/
public function deleteItems(array $keys): bool
{
return $this->traceFunction('cache.delete_items', function () use ($keys): bool {
return $this->decoratedAdapter->deleteItems($keys);
});
}
/**
* {@inheritdoc}
*/
public function save(CacheItemInterface $item): bool
{
return $this->traceFunction('cache.save', function () use ($item): bool {
return $this->decoratedAdapter->save($item);
});
}
/**
* {@inheritdoc}
*/
public function saveDeferred(CacheItemInterface $item): bool
{
return $this->traceFunction('cache.save_deferred', function () use ($item): bool {
return $this->decoratedAdapter->saveDeferred($item);
});
}
/**
* {@inheritdoc}
*/
public function commit(): bool
{
return $this->traceFunction('cache.commit', function (): bool {
return $this->decoratedAdapter->commit();
});
}
/**
* {@inheritdoc}
*/
public function prune(): bool
{
return $this->traceFunction('cache.prune', function (): bool {
if (!$this->decoratedAdapter instanceof PruneableInterface) {
return false;
}
return $this->decoratedAdapter->prune();
});
}
/**
* {@inheritdoc}
*/
public function reset(): void
{
if ($this->decoratedAdapter instanceof ResettableInterface) {
$this->decoratedAdapter->reset();
}
}
/**
* @phpstan-template TResult
*
* @phpstan-param \Closure(): TResult $callback
*
* @phpstan-return TResult
*/
private function traceFunction(string $spanOperation, \Closure $callback, string $spanDescription = null)
{
$span = $this->hub->getSpan();
if (null !== $span) {
$spanContext = new SpanContext();
$spanContext->setOp($spanOperation);
if (null !== $spanDescription) {
$spanContext->setDescription(urldecode($spanDescription));
}
$span = $span->startChild($spanContext);
}
try {
return $callback();
} finally {
if (null !== $span) {
$span->finish();
}
}
}
}