Skip to content

Instantly share code, notes, and snippets.

@Nemo64
Last active September 27, 2021 15:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nemo64/2b01979b1df17154fd2d787a859dc498 to your computer and use it in GitHub Desktop.
Save Nemo64/2b01979b1df17154fd2d787a859dc498 to your computer and use it in GitHub Desktop.
services:
ProxyManager\Factory\AccessInterceptorValueHolderFactory:
class: ProxyManager\Factory\AccessInterceptorValueHolderFactory
doctrine.dbal.default_connection.stopwatch:
class: Doctrine\DBAL\Connection
decorates: doctrine.dbal.default_connection
factory: [ '@App\Service\StopwatchDecorator', decorate ]
arguments: [ '@doctrine.dbal.default_connection.stopwatch.inner' ]
Symfony\Component\Validator\Validator\ValidatorInterface.stopwatch:
class: Symfony\Component\Validator\Validator\ValidatorInterface
decorates: Symfony\Component\Validator\Validator\ValidatorInterface
factory: [ '@App\Service\StopwatchDecorator', decorate ]
arguments: [ '@Symfony\Component\Validator\Validator\ValidatorInterface.stopwatch.inner' ]
form.factory.stopwatch:
class: Symfony\Component\Form\FormFactory
decorates: form.factory
factory: [ '@App\Service\StopwatchDecorator', decorate ]
arguments: [ '@form.factory.stopwatch.inner' ]
<?php
namespace App\Service;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
class StopwatchCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
foreach ($container->findTaggedServiceIds('form.type') as $formTypeId => $something) {
if (strpos($formTypeId, 'App') !== 0) {
continue;
}
$definition = new Definition($container->getDefinition($formTypeId)->getClass());
$definition->setDecoratedService($formTypeId);
$definition->setFactory([new Reference('App\Service\StopwatchDecorator'), 'decorate']);
$definition->setArguments([new Reference("$formTypeId.stopwatch.inner")]);
$container->setDefinition("$formTypeId.stopwatch", $definition);
}
foreach ($container->findTaggedServiceIds('validator.constraint_validator') as $formTypeId => $something) {
$definition = new Definition($container->getDefinition($formTypeId)->getClass());
$definition->setDecoratedService($formTypeId);
$definition->setFactory([new Reference('App\Service\StopwatchDecorator'), 'decorate']);
$definition->setArguments([new Reference("$formTypeId.stopwatch.inner")]);
$container->setDefinition("$formTypeId.stopwatch", $definition);
}
foreach ($container->findTaggedServiceIds('validator.initializer') as $formTypeId => $something) {
$definition = new Definition($container->getDefinition($formTypeId)->getClass());
$definition->setDecoratedService($formTypeId);
$definition->setFactory([new Reference('App\Service\StopwatchDecorator'), 'decorate']);
$definition->setArguments([new Reference("$formTypeId.stopwatch.inner")]);
$container->setDefinition("$formTypeId.stopwatch", $definition);
}
}
}
<?php
namespace App\Service;
use ProxyManager\Factory\AccessInterceptorValueHolderFactory;
use Symfony\Component\Stopwatch\Stopwatch;
class StopwatchDecorator
{
private AccessInterceptorValueHolderFactory $factory;
private Stopwatch $stopwatch;
public function __construct(AccessInterceptorValueHolderFactory $factory, Stopwatch $stopwatch)
{
$this->factory = $factory;
$this->stopwatch = $stopwatch;
}
public function decorate(object $service): object
{
$class = new \ReflectionClass($service);
$className = $class->getName();
// do not process core or extensions
if ($class->getFileName() === false) {
return $service;
}
// do not process already wrapped services
if (0 === strpos($class->getName(), "ProxyManagerGeneratedProxy")) {
return $service;
}
$prefix = [];
$suffix = [];
$methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
$methods = array_filter($methods, fn($method) => !$method->isStatic());
$methods = array_filter($methods, fn($method) => !$method->isFinal());
foreach ($methods as $method) {
$methodName = $method->getName();
$eventName = "{$class->getShortName()}->{$methodName}";
$prefix[$methodName] = function () use ($eventName, $className) {
$this->stopwatch->start($eventName, $className);
};
$suffix[$methodName] = function ($p, $i, $m, $params, &$returnValue) use ($eventName) {
$this->stopwatch->stop($eventName);
// decorate returned values as well
if (is_object($returnValue)) {
$returnValue = $this->decorate($returnValue);
}
};
}
try {
return $this->factory->createProxy($service, $prefix, $suffix);
} catch (\Throwable $e) {
return $service;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment