Source Maven

Code Partition for Beau Simensen

Say Hello to That Podcast

Wow, we are well into 2014 and I haven't posted since last July? Ugh. Well, a lot has happened since then. A lot. I should get to documenting that at some point but for now I wanted to share something new!

Last week as php[tek] came to an end my friend Dave Marshall and I finally launched a project we have been working on for a few months: That Podcast

I guess it is fitting that this post follows up a few posts about Symfony Live 2013 Portland since that is where I finally had a chance to meet Dave in person. Hopefully we'll have a chance to meet in person again soon!

I'd love to get more input on our project! If you listen and want to give us feedback, make sure to either mention us on Twitter (@thatpodcast, @beausimensen, or @davedevelopment) or drop us an email at hello@thatpodcast.io.

Symfony Live 2013 Portland: Embedded Composer

For the last few weeks I've been polishing Embedded Composer for my talk at Symfony Live 2013 in Portland.

Embedded Composer started its life as a part of Sculpin but was extracted so that other people could potentially use it. I'm hoping that Embedded Composer will help more people get interested in the idea of embedding Composer into their applications.

I'm super excited to have an opportunity to share this work with more people. I had a blast at Symfony Live 2012 in San Francisco. I'm hoping I can continue to attend these in the future.

I also took this opportunity to launch a new landing page for myself. beau.io is now live and showcasing this talk. :)

I'd love to get feedback on the talk. :) If you saw it in person, please rate my talk and provide some feedback. Otherwise, please send me a Tweet!

Composer is a wonderful way to manage a project. But what happens when you need your application to be extensible at runtime? Enter Embedded Composer. Embedding Composer will ensure that the dependencies already included by your application are taken into account when adding additional dependencies at runtime. While this can be very useful for any application that may be installed globally it is critical for any application that may be distributed as a phar.

Tags: symfony, composer, speaking, symfony_live

Sending a Pull Request on GitHub Even when the Base Repository You Want is Missing

You want to help out on a large project. You find a Pull Request that someone else is working on and decide you want to help out. You make your changes and are ready to send a Pull Request but to your surprise you find that the fork you want to send your PR to is not listed. What now?

It turns out this is a known limitation with GitHub's Pull Request implementation for network with many repositories. If there are too many forks GitHub filters the list to only include the most popular forks.

I've run into this a number of times, usually when trying to work with the php-fig/fig-standards repository. More often than not, if I want to send changes to someone else's proposal (a very common use case) I'll find that their repository is not listed in the "base repo" selection list:

base repo example

I've contacted GitHub about this several times. This most recent time I got antsy and started poking around while waiting for a response and came to a workaround on my own. Huzzah!

I received an email the next day stating that the team is "discussing ways to address this particular issue with large repository networks." It also included instructions on the same workaround. :)

The Workaround

It turns out it is pretty easy to get around this limitation with a little URL hacking. Using my example above as a reference, I can target a PR at any base repo I want using the following pattern and replacing base-repo-username and base-branch as needed:

https://github.com/simensen/fig-standards/pull/new/base-repo-username:base-branch...simensen:is-hit-is-miss

So if I want to send something to, say, tedivm's Cache proposal, I would use the following URL since the base-repo-username is tedivm and the base-branch would be Cache:

https://github.com/simensen/fig-standards/pull/new/tedivm:Cache...simensen:is-hit-is-miss

I've had mixed luck on what actually happens when loading the hacked URL. In at least one case either the "commits" or "files" tabs was missing content on viewing, despite having the correct number of items listed in the tabs. The PR worked just fine, though, so if you see this I don't think there is a lot of reason to be alarmed.

Hopefully GitHub will support this functionality natively sometime in the near future. Until then, this workaround should help people currently stuck on this problem.

Tags: github

Silex Service Providers and Controller Providers; What Is Safe To Do Where?

Since discussing writing Silex service providers a few edge cases have come up that I'd like to touch on. I've also learned more about controller providers and how they fit into the big picture. Here are a few more rules and guidelines to help make writing Silex service providers and controller providers a little easier.

Service Provider Registering Another Service Provider

One may be tempted to register another service provider from a service provider's register method. Take the following example:

use Silex\Application;
use Silex\Provider\DoctrineServiceProvider;
use Silex\ServiceProviderInterface;

public function MyServiceProvider implements ServiceProviderInterface
{
    public function boot(Application $app)
    {
    }

    public function register(Application $app)
    {
        // This is probably not wise.
        $app->register(new DoctrineServiceProvider);
    }
}

While there is nothing wrong with this from an interface perspective, it violates the spirit of the service provider contract.

One of the rules from Writing Silex Service Providers was to treat the Application type hint for register as if it were actually Pimple. Pimple does not have a register method so by this rule register should not be called from within register.

What about the fact that Pimple itself may some day support a register method of its own? Well, when that happens we can revisit the issue. :) Until then the existing rule holds.

But doesn't registering a service provider pretty much just defines more services? Yes, this is true. However, doing so hides the fact that the service provider is being registered from the user. This is not good.

Consider the example above where we are registering the Doctrine service provider. What happens if the user doesn't realize this and they register the Doctrine provider themselves? In some cases this may be mostly harmless but in others this could be very bad.

By registering another service provider directly we end up with two problems. First, we are hiding dependencies. Second, we are tying our service provider to a specific implementation.

Hidden Dependencies

Take a slightly extended example where Doctrine service provider is registered by a service provider:

use Silex\Application;
use Silex\Provider\DoctrineServiceProvider;
use Silex\ServiceProviderInterface;

public function MyServiceProvider implements ServiceProviderInterface
{
    public function boot(Application $app)
    {
    }

    public function register(Application $app)
    {
        // This is probably not wise.
        $app->register(new DoctrineServiceProvider);

        $app['myapp.someservice'] = $app->share(function () use ($app) {
            // do something with $app['db'];
        });
    }
}

Written this way our service provider has a hidden dependency on the Doctrine service provider.

Rather than register the Doctrine service provider directly we should document that the user needs to register it themselves. This allows them to register it and configure it however they see fit.

To quote Igor on why this is the best approach:

... it means you don't get to configure that provider when it is registered
it's about giving the user control and dependency inversion ...

Tied to a Specific Implementation

To take it a step further, we can document exactly what we need, namely that we need $app['db'] to be available and that it needs to be an instance of Doctrine DBAL Connection.

To help the user out we can say that this service can be provided by the Doctrine service provider and even use it in example code.

An example of this in action would be the requirements of the Doctrine ORM Service Provider:

Currently requires both dbs and dbs.event_manager services in order to work. These can be provided by a Doctrine Service Provider like the Silex or Cilex service providers. If you can or want to fake it, go for it. :)

As is usually the case, by not tying our service provider to another service provider directly we will also get the added benefit of increased testability for our service provider.

Responsibility of Controller Provider's connect() Method

The responsibility of a Controller Provider's connect method is to wire up controllers. That is it. The rule is simple. This means that services should not be defined in a controller provider.

A Single Class Can Provide Both Services and Controllers

When discussing controller services and splitting the service definitions from the routing, Igor reminded me of a simple truth about interfaces. Classes can implement more than one interface at a time.

This means that a class can implement both the service provider interface and the controller provider interface at the same time. This is a nice solution for building out a provider for controllers as services:

class MyAppControllersProvider implements
    ServiceProviderInterface,
    ControllerProviderInterface
{
    function boot(Application $app)
    {
    }

    function register(Application $app)
    {
        //
        // Define controller services
        //

        $app['myapp.hellocontroller'] = $app->share(function() use ($app) {
            return new MyApp\Controller\HelloController($app['some.service']);
        });
    }

    public function connect(Application $app)
    {
        $controllers = $app['controllers_factory'];

        //
        // Define routing referring to controller services
        //

        $controllers->get('/hello/{name}', 'myapp.hellocontroller:say')
            ->method('GET')
            ->bind('myapp.hello');

        return $controllers;
    }
}

This allows us to follow the rules of only defining services in register and only defining controllers in connect but keeping the code in the same class. Win!


One caveat to this is that we have to register and mount the provider separately. For example:

$myAppControllersProvider = new MyAppControllersProvider;

$app->register($myAppControllersProvider);
$app->mount('/', $myAppControllersProvider);

Mounting Controller Provider in boot()

It might seem logical to mount a controller provider in boot. While not strictly wrong since Application code is more or less safe to call from inside boot it is probably not the best choice.

Mounting a controller provider inside boot will take away some control of the controller provider from the user. For example, the user will lose the ability to mount the controller provider at a path of their choice.

So while one can do it, it would be best to do so sparingly. More often than not the right decision is probably to allow the user to mount the controller provider themselves.

Control

A common theme seen here is control. The question of mounting a controller in boot is similar to question of registering a service provider from within a service provider. Both are attempts to wrangle control from the user.

This control is important. We should do what we can to not limit it if we do not need to.

Join Us

Seriously, people, #silex-php rocks. Come join us if you want to discuss awesome things and learn Silex best practices. :)

Tags: silex


Older Posts