Symfony Bundleless Service Definition Validation

You have decided to go bundleless in your project.
You also want to still use Matthias Noback’s nice library that helps you validating the service definitions in the container, whether your project is symfony or based just on the symfony dependency injection component, you can still plug this in. Here is how I did it in our open source project Matthew-7-12 project.

<?php
 
namespace Grace;
 
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
use Matthias\SymfonyServiceDefinitionValidator\Compiler\ValidateServiceDefinitionsPass;
use Matthias\SymfonyServiceDefinitionValidator\Configuration;
// ...
use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
 
class AppKernel extends Kernel
{
    // ...
 
    protected function prepareContainer(ContainerBuilder $container)
    {
        $extensions = array();
        foreach ($this->bundles as $bundle) {
            if ($extension = $bundle->getContainerExtension()) {
                $container->registerExtension($extension);
                $extensions[] = $extension->getAlias();
            }
 
            if ($this->debug) {
                $container->addObjectResource($bundle);
            }
        }
        foreach ($this->bundles as $bundle) {
            $bundle->build($container);
        }
 
        $this->buildBundleless($container);
 
        // ensure these extensions are implicitly loaded
        $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));
    }
 
    private function buildBundleless(ContainerBuilder $container)
    {
        if ($container->getParameter('kernel.debug')) {
            $container->addCompilerPass(new FixValidatorDefinitionPass());
 
            $configuration = new Configuration();
            $configuration->setEvaluateExpressions(true);
            $container->addCompilerPass(
                new ValidateServiceDefinitionsPass($configuration),
                PassConfig::TYPE_AFTER_REMOVING
            );
        }
    }
}

Notice here we hack the kernel, and is ok. We have done it already and methods there are set to protected and not to private because the demands of the app need to remain open especially open for the kernel. In this case we have overridden the prepareContainer method because we want to also add a compiler pass

$this->buildBundleless($container);

but without the need to create an entire bundle just for this. After all we don’t want to create a bundle in a project that is bundle-less. creating 3+ files just because we want to do something simple does not make sense, makes it complex, teaches you less, you learn less about the basics, and well it is just not our cup of tea.

For the contents of buildBundleless it basically will check if in development environment or such and will fix a common validation fix in Symfony service definitions and then will add the check for service definitions from the library. These both are implemented as compiler passes.

Here is the FixValidatorDefinitionPass class:

<?php
 
namespace Grace;
 
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
 
class FixValidatorDefinitionPass implements CompilerPassInterface
{
    public function process(ContainerBuilder $container)
    {
        $container->getDefinition('validator')->setClass('Symfony\Component\Validator\Validator');
    }
}

Just sits right next to the AppKernel in Matthew-7-12. We of course could beautify this further, but for now this is enough to spot some service definition corrections.

Next time you try to bring up the Kernel you will see errors that will be easier to correct on your service definitions.

~ php console                                                                            Luiss-MacBook-Pro-3 [16:09:36]
 
 
 
  [Matthias\SymfonyServiceDefinitionValidator\Exception\InvalidServiceDefinitionsException]
  Service definition validation errors (1):
  - grace.pull_flow: Argument for type-hint "Grace\Collabs\Mailer" points to a service of class "Grace\Collabs\Collab
  "

even runtime errors:

~ php console                                                                            Luiss-MacBook-Pro-3 [16:07:32]
 
 
 
  [RuntimeException]
  The autoloader expected class "Grace\Endpoints\Push" to be defined in file "/Users/cordoval/Sites/libs/matthew-7-12
  /src/Endpoints/Push.php". The file was found but the class was not in it, the class name or namespace probably has
  a typo.

Enjoy, thanks for your reading!

Leave a Reply

Your email address will not be published. Required fields are marked *