New Symfony Way: Scratch Bundle for Packages with Compiler Passes

In the same way silex have service providers you can also have the same in Symfony, no need for bundles to define services. You can just plug some compiler passes and special configuration classes but really there is no need to create a bundle to have configuration capabilities.

The reasons for not having any bundle is that there is really no need for a bundle when you are creating an application package. Creating a bundle pulls your direction away from creating a reusable package outside symfony whereas if you create a standalone package and offer some service definitions it ends up being a better choice.

The addon which i have not mentioned is that there are really good projects out there like PageKit, even yucky projects like laravel that use symfony components or even others that do use the config and di components and yet do not use the bundle concept. For them the world of bundles is far away and will never be reached. Whereas if you design a library/package standalone it can have providers for these too. The end result is more reusability.

Let’s start with a good example. At the end of this blog from Matthias Noback’s blog post we have learned how to register ancillary event dispatcher services that are decoupled from the symfony default event_dispatcher system and how we register listeners or subscribers with that sub event system.

Here is the same code showing how you register the compiler pass in the way proposed in that blog post:

use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\HttpKernel\DependencyInjection\RegisterListenersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 
class YourBundle extends Bundle
{
    public function build(ContainerBuilder $container)
    {
        $container->addCompilerPass(
            new RegisterListenersPass(
                'domain_event_dispatcher',
                'domain_event_listener',
                'domain_event_subscriber'
            ),
            PassConfig::TYPE_BEFORE_REMOVING
        );
    }
}

and add the service definitions:

services:
    domain_event_dispatcher:
        class: Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher
        arguments:
            - @service_container

And how you create a listener (and extra bonus for the subscriber):

services:
    some_domain_event_listener
        class: ...
        tags:
            -
                name: domain_event_listener
                event: my_domain_event
                method: onMyDomainEvent
    some_domain_event_subscriber
        class: ...
        tags:
            -
                name: domain_event_subscriber

There is I believe another way to do this. What is truly required is a way to plug directly into the kernel (like it is done in bundles), the compiler passes that create this service definitions and then be done with it.

Let’s take a stab. First we can modify generically our AppKernel.php like this:

    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->hookCompilers($container);
 
        // ensure these extensions are implicitly loaded
        $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions));
    }

Notice the hookCompilers is a custom method added:

    private function hookCompilers(ContainerBuilder $container)
    {
        foreach ($this->registerCompilers() as $compilerRow) {
            $instance = $compilerRow[0];
            $type = $compilerRow[1];
            $debug = $compilerRow[2];
            if (!$debug || $container->getParameter('kernel.debug')) {
                $container->addCompilerPass($instance, $type);
            }
        }
    }

But that will not need to change. The registerCompilers method is equivalent to the registerBundles method that is well known by the bundle system users. This registerCompilers method will look like:

    public function registerCompilers()
    {
        $configuration = new Configuration();
        $configuration->setEvaluateExpressions(true);
 
        return [
            [new ValidateServiceDefinitionsPass($configuration), PassConfig::TYPE_AFTER_REMOVING, true],
            [new FixValidatorDefinitionPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, true],
            [new RegisterListenersPass(Dispatchers::READ, Dispatchers::READ_LISTENER, Dispatchers::READ_SUBSCRIBER), PassConfig::TYPE_BEFORE_REMOVING, false],
            [new RegisterListenersPass(Dispatchers::WRITE, Dispatchers::WRITE_LISTENER, Dispatchers::WRITE_SUBSCRIBER), PassConfig::TYPE_BEFORE_REMOVING, false],
        ];
    }

Notice that we have added several compiler passes here. The first two are coming from the package for validating the service definitions and stuff that we saw in a previous post. Now though they are totally standardized. The 2 last compiler passes added are two new event sub systems, easily pluggable just by adding new rows here. After this tapping into a different event dispatcher is totally straightforward.

Mind that we have just compiled the constants into a class Dispatchers.php:

<?php
 
namespace Grace;
 
class Dispatchers
{
    const READ = 'read_event_dispatcher';
    const READ_LISTENER = 'read_event_dispatcher_listener';
    const READ_SUBSCRIBER = 'read_event_dispatcher_subscriber';
 
    const WRITE = 'write_event_dispatcher';
    const WRITE_LISTENER = 'write_event_dispatcher_listener';
    const WRITE_SUBSCRIBER = 'write_event_dispatcher_subscriber';
}

You can now just define a subscriber like:

    grace.some_domain_event_subscriber:
        class: Grace\ExampleSubscriber
        tags:
            -
                name: Grace\Dispatchers::READ_SUBSCRIBER

The whole story can be better seen in a diff of this PR in matthew-7-12 project https://github.com/cordoval/matthew-7-12/pull/70/files.

A note on the logging capability of these new event dispatcher sub systems was made by @sstok. The concern is that on debug mode the event dispatcher is actually wrapped like this:

        <service id="debug.event_dispatcher" class="%debug.event_dispatcher.class%">
            <tag name="monolog.logger" channel="event" />
            <argument type="service" id="debug.event_dispatcher.parent" />
            <argument type="service" id="debug.stopwatch" />
            <argument type="service" id="logger" on-invalid="null" />
        </service>

Perhaps we can do a conditional and check for the interface they extend or class of the event dispatcher base that they extend and decorate accordingly our dispatchers too in case we are under the development mode. This of course is not hard to do and it could be accomplished by a DecorateRegisterListenerPasses.php compiler pass too.

An also good example on how to split a bundle and a dependency injection provider package is https://github.com/rollerworks/RollerworksSearchBundle. This bundle actually requires its dependency extension component in its composer.json:

    "require": {
        "php": ">=5.3.3",
        "rollerworks/search-symfony-di": "1.*@dev",
        "symfony/framework-bundle": "~2.1",
        "symfony/finder": "~2.1"
    },

Offering very good separation for those who integrate Rollerworks Search package without having to be tied to use a bundle. This is a better approach than the bundle one. You can take a look at the package here https://github.com/rollerworks/rollerworks-search-symfony-di/tree/master/src. In there you can also see a custom loader which is nice for when you go all the way with a bundle-less approach.

This approach will do great good to those systems that only use some symfony components and yet leverage their own component subsystem. Also for those projects that require flexibility, especially when dealing with DDD this is a desirable architecture to pursue.

Hope you like the approach, comment and don’t forget to retweet sil vous plait!

Alternative Method to Tame Symfony/Doctrine Repositories

Doctrine repositories are something the standard developer should know about, but how to really work well with repositories is hard to master without some good thought to it. Benjamin @beberlei wrote a blog post on taming repositories here http://www.whitewashing.de/2013/03/04/doctrine_repositories.html sometime ago. The ending result of its refactor creates several classes that are centered around Specification pattern that has a match method to which it gets passed the query builder, an object that is implementation specific. This creep however is somewhat unavoidable because of doctrine api and how it works. When you change implementation or use another library you have trashed all of this approach once and for all. Since this is very tied to implementation details I proposed a more quick and dirty approach that aims for versatility and could be easily implemented by anyone in less time. It is more deficient and quickly discardable and centers around quicker readable code.

With the previous approach one ends up with a bunch of classes and things like:

<?php
$specification = new Spec\AsArray(
    new Spec\AndX(
        new Spec\FilterGroup($groupId),
        new Spec\FilterPermission($permission)
    )
);
 
$groups = $app['orm.ems']['api']
    ->getRepository('\EasyBib\Api\Entity\Group')
    ->match($specification)
;

What if we make the specification more tuned in a single callable class:

$groups = $app['orm.ems']['api']
    ->getRepository('\EasyBib\Api\Entity\Group')
    ->match(new PowerUsers(15))
;

Then your user repository turns from:

<?php
class UserRepository
{
    public function match(UserSpecification $specification)
    {
        $qb = $this->createQueryBuilder('u');
        $expr = $specification->match($qb, 'u');
 
        $query = $qb->where($expr)->getQuery();
 
        $specification->modifyQuery($query);
 
        return $query->getResult();
    }
}

Into this:

<?php
class UserRepository
{
    public function match(callable $filter)
    {
        $qb = $this->createQueryBuilder('u');
 
        return call_user_func($filter, $qb);
    }
}

And all the things that build the query land into one place that gets encapsulated with a good old name:

<?php
class PowerUsers
{
    protected $limit;
 
    public function __construct($number)
    {
        $this->limit = $number;
    }
 
    public function __invoke(QueryBuilder $qb)
    {
        // here goes all the heavy lifting under one place
    }
}

This approach speaks for versatility, you can still compose reusability via the constructor if you may though the versatile aspect of this leaves the door open for more separation of concerns and composition but gives you the advantage of not spending too much time on prototyping phase. Also with some more sauce on it you can provide adapters for different implementations should you need to change gateways for doctrine odm or orm or other possibly.

I personally found to come up with more descriptive names so that controllers or services get slimmer and more readable.

Please feel free to leave your feedback and retweet this blog post. Thanks!

Symfony: Forget About Permission Settings

Ever wanted to forget about going into symfony.com looking for the configuration chapter of the book and copy pasting the long commands to set acl’s right on your systems? That is not only on your local but also on your server?

Well there was a discussion in a DX ticket that today I stumbled upon and here it is https://github.com/symfony/symfony/issues/11563 and skimming over it I found this pearl from @umpirski. It is called umpirsky/composer-permissions-handler and here it is how you set it up:

         "symfony/monolog-bundle": "~2.4",
         "sensio/distribution-bundle": "3.0.*@dev",
         "sensio/framework-extra-bundle": "~3.0",
-        "incenteev/composer-parameter-handler": "~2.0"
+        "incenteev/composer-parameter-handler": "~2.0",
+        "umpirsky/composer-permissions-handler": "dev-master"
     },
     "require-dev": {
         "sensio/generator-bundle": "~2.3"
 @@ -32,7 +33,8 @@
             "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
             "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
             "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
-            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles"
+            "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::removeSymfonyStandardFiles",
+            "Umpirsky\\PermissionsHandler\\ScriptHandler::setPermissions"
         ],
         "post-update-cmd": [
             "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters",
 @@ -49,6 +51,7 @@
     "extra": {
         "symfony-app-dir": "app",
         "symfony-web-dir": "web",
+        "writable-dirs": ["app/cache", "app/logs"],
         "incenteev-parameters": {
             "file": "app/config/parameters.yml"
         },

In mac it works like a charm and I am sure it works the same way in servers due to its nice phpspec’ed logic:

<?php
 
namespace Umpirsky\PermissionsHandler;
 
use Umpirsky\PermissionsHandler\Exception\PathNotFoundException;
 
class SetfaclPermissionsSetter extends PermissionsSetter
{
    public function setPermissions($path)
    {
        if (!is_dir($path)) {
            throw new PathNotFoundException($path);
        }
 
        $this->runCommand('setfacl -R -m u:"%httpduser%":rwX -m u:`whoami`:rwX %path%', $path);
        $this->runCommand('setfacl -dR -m u:"%httpduser%":rwX -m u:`whoami`:rwX %path%', $path);
    }
}

It is a very good example to learn and review phpspec niceties too!

Enjoy, thanks @umpirski for a nice package!

Decoupling in Symfony How to Pull out from Annotations

Annotations bring coupling as shown in Matthias Noback’s book on “A year with Symfony” and elsewhere in many blog posts around. You realize this too late if you are a newcomer because the documentation and many bundles assumed sometime ago that annotations was a good idea for automating stuff. This is the magic that Symfony in its beginnings set out itself to pursue but did not stick to as we are seeing lately in some developments. Easy of use can be sometimes a trap to add too much magic and blur the view of developers to use a library or framework.

Thanks to some efforts however there is a way in which you can pull your project out of annotations. One of them is using the doctrine bundle built-in capability to export mapping to yaml or other formats. The exporting seldom needs adjustments and even though some say it could be broken or lack some features, pretty much saves you a lot of time when you are doing the move with minor or no adjustments at all.

For instance if you have a

Vendor\CoreBundle

you can just issue this command:

php app/console doctrine:mapping:import "VendorCoreBundle" yml

The only thing with the output is that you will have to remove the indexes that it created and lifecyclecallbacks if you like to be clean and did not have anything specified in those fields.

It however generates a complete specification which helps you don’t miss anything:

// ...
            targetEntity: VendorUsers
            cascade: {  }
            fetch: LAZY
            mappedBy: null
            inversedBy: null
            joinColumns:
                locked_by_id:
                    referencedColumnName: id
            orphanRemoval: false
// ...

That is for mapping, but what about annotations. Well simply just install the bundle https://github.com/sed-szeged/SedRouteExporterBundle and run:

mkdir builds-output
php app/console sed:router:export --format yaml --output builds-output

The output is clogged into a single

routing.yml

file but it is easily split and cleaned up. The specification is also complete on the output:

user_lock:
    path: /users/lock
    defaults: { _controller: 'Vendor\Bundle\CoreBundle\Controller\UserController::lock' }
    options: { compiler_class: Symfony\Component\Routing\RouteCompiler }

Now your controllers will look much cleaner, you can actually go Controllers as Services and then have actual tests that cleanly tests part of your controllers at least. Definitely this will increase the quality of the code of your project because you are decoupling not only mapping, but routing, and others as well as you don’t need necessarily tools to convert but they speed up the process in converting something like this:

/**
 * @Cache(expires="tomorrow")
 * @Method(...)
 * @Template()
 * @ParamConverter()
 * @Route("some_route_here")
 * @Secure(roles="ROLE_USER")
 * @RunAs(roles="ROLE_PRIVATE_SERVICE")
 * @InjectParams({
 *     "em" = @Inject("doctrine.entity_manager")
 * })
 */
 */

Which is madness and that is just a quick invented example on how some projects end up. The conversion will heal you and your team.

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!