1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Cache\DependencyInjection; 13 14use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; 15use Symfony\Component\Cache\Adapter\TraceableAdapter; 16use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter; 17use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 18use Symfony\Component\DependencyInjection\ContainerBuilder; 19use Symfony\Component\DependencyInjection\Definition; 20use Symfony\Component\DependencyInjection\Reference; 21 22/** 23 * Inject a data collector to all the cache services to be able to get detailed statistics. 24 * 25 * @author Tobias Nyholm <tobias.nyholm@gmail.com> 26 */ 27class CacheCollectorPass implements CompilerPassInterface 28{ 29 private $dataCollectorCacheId; 30 private $cachePoolTag; 31 private $cachePoolRecorderInnerSuffix; 32 33 public function __construct(string $dataCollectorCacheId = 'data_collector.cache', string $cachePoolTag = 'cache.pool', string $cachePoolRecorderInnerSuffix = '.recorder_inner') 34 { 35 if (0 < \func_num_args()) { 36 trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__); 37 } 38 39 $this->dataCollectorCacheId = $dataCollectorCacheId; 40 $this->cachePoolTag = $cachePoolTag; 41 $this->cachePoolRecorderInnerSuffix = $cachePoolRecorderInnerSuffix; 42 } 43 44 /** 45 * {@inheritdoc} 46 */ 47 public function process(ContainerBuilder $container) 48 { 49 if (!$container->hasDefinition($this->dataCollectorCacheId)) { 50 return; 51 } 52 53 foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) { 54 $poolName = $attributes[0]['name'] ?? $id; 55 56 $this->addToCollector($id, $poolName, $container); 57 } 58 } 59 60 private function addToCollector(string $id, string $name, ContainerBuilder $container) 61 { 62 $definition = $container->getDefinition($id); 63 if ($definition->isAbstract()) { 64 return; 65 } 66 67 $collectorDefinition = $container->getDefinition($this->dataCollectorCacheId); 68 $recorder = new Definition(is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class); 69 $recorder->setTags($definition->getTags()); 70 if (!$definition->isPublic() || !$definition->isPrivate()) { 71 $recorder->setPublic($definition->isPublic()); 72 } 73 $recorder->setArguments([new Reference($innerId = $id.$this->cachePoolRecorderInnerSuffix)]); 74 75 foreach ($definition->getMethodCalls() as [$method, $args]) { 76 if ('setCallbackWrapper' !== $method || !$args[0] instanceof Definition || !($args[0]->getArguments()[2] ?? null) instanceof Definition) { 77 continue; 78 } 79 if ([new Reference($id), 'setCallbackWrapper'] == $args[0]->getArguments()[2]->getFactory()) { 80 $args[0]->getArguments()[2]->setFactory([new Reference($innerId), 'setCallbackWrapper']); 81 } 82 } 83 84 $definition->setTags([]); 85 $definition->setPublic(false); 86 87 $container->setDefinition($innerId, $definition); 88 $container->setDefinition($id, $recorder); 89 90 // Tell the collector to add the new instance 91 $collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]); 92 $collectorDefinition->setPublic(false); 93 } 94} 95