Skip to content

Instantly share code, notes, and snippets.

@dbu
Created April 18, 2019 12:02
Show Gist options
  • Save dbu/d2fb477fb13255632eaa6d580ebd022a to your computer and use it in GitHub Desktop.
Save dbu/d2fb477fb13255632eaa6d580ebd022a to your computer and use it in GitHub Desktop.
symfony compiler pass to detect invalid classes on service configurations
<?php
declare(strict_types=1);
namespace Infrastructure\Symfony\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
/**
* Look at *all* service definitions and validate that their classes exist.
*
* With autoconfigure but no autowire, we otherwise never see invalid class
* names for listeners or such.
*
* This needs to be a compilar pass running at PassConfig::TYPE_BEFORE_REMOVING.
* A command would only see the services that have not been removed from the
* container.
*
* Inspired by https://github.com/matthiasnoback/symfony-service-definition-validator/ which is
* very outdated and validates some things that Symfony will detect and report.
*/
class ValidateServiceDefinitions implements CompilerPassInterface
{
private const KNOWN_UNUSED_SERVICES = [
'beberlei_metrics.util.buzz.curl' => true, // not cleaned out by bundle
'beberlei_metrics.util.buzz.browser' => true, // not cleaned out by bundle
'form.type.entity' => true, // breaks when loading the php file
'debug.file_link_formatter.url_format' => true, // configured to by "string"
'service_container' => true, // ContainerInterface but no factory
];
public function process(ContainerBuilder $container): void
{
foreach ($container->getDefinitions() as $serviceId => $definition) {
if ($definition->isAbstract() || \array_key_exists($serviceId, self::KNOWN_UNUSED_SERVICES)) {
continue;
}
$class = $definition->getClass();
if ($class) {
$class = $container->getParameterBag()->resolveValue($class);
if (!class_exists($class)) {
if (null === $definition->getFactory() || !interface_exists($class)) {
throw new \Exception(sprintf('Service %s is configured to use the nonexistent class %s', $serviceId, $class));
}
}
} elseif (!$definition->isSynthetic()) {
throw new \Exception(sprintf('Service %s has no class', $serviceId));
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment