Source Maven

Code Partition for Beau Simensen

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

PHP-FIG Focus

I'm a huge fan of PHP-FIG. I take part where I think I can make a constructive contribution and I try to tune in to as many of the discussions as my time allows. So when I saw Larry's poll about the Focus of FIG I was happy to have the opportunity to share what I think PHP-FIG should be focusing on. This sparked an interesting conversation about whether I thought PSR-2 was a waste of time.


Larry specifically requested that people simply vote and not discuss the issue. I think this was a great idea and people have done a pretty good job of following this request. A downside to this is that people may wonder why someone votes the way they do when there is zero context for their vote.

Hari asked for more context from me on Twitter. I was happy to talk about it and I thought the discussion went well and was productive. However, I also found it hard to really express some of these complex ideas in 140 character bursts. So I told him I'd write about why I voted the way I did in more detail.


I was surprised that PHP-FIG got involved with PSR-2. I did not think that it was something that would further the groups goals. There were other interesting things happening at that point but the entire focus of the group turned to... a style guide? Really?

As far as I was concerned, the process for creating PSR-2 drew a lot of time and energy from places it could have been better spent. Since then PSR-2 has been a PR nightmare and a drain on resources.

This does not mean that I am not actively trying to write valid PSR-2 nor does it mean I don't see utility in having it available now that it already exist. Quite the opposite, in fact. How do I explain this duality?

First, it was done and it is now a thing. Why fight it?

Second, it was more or less compatible with Symfony CS. I had never had a formal CS for any of my projects in the past and I had recently decided to adopt Symfony CS for all of my projects. This is hugely important so it deserves some emphasis. Adopting PSR-2 for me meant changing nothing about the way I was already writing code.

Third, the notion of PSR-2 being an open and independent standard not tied directly to the Symfony framework meant that in theory I would be more likely to find other projects using my preferred personal/project CS. As someone who likes to promote writing framework agnostic code, this is a huge win!

So I can see how from the outside it would appear that I don't actually think that PSR-2 was a complete waste of time. Why did I vote 10, "Specs along the lines of PSR-3 that affect code-level interop. PSR-2-esque specs are a waste of time," instead of something a little softer?

PSR-2 exists now. There is nothing we can do about it. I actually find it to be useful and serving a purpose. That is a good thing! But this does not mean that I think PHP-FIG should spend resources on creating more specs like PSR-2.

For example, I think that proposals along the lines of SQL formatting standards* are just begging for trouble. Discussions like these are going to end up eating valuable resources and the end results will have a high probability of making even more people unhappy.

I would rather have PHP-FIG focus on bringing things like a Cache interface to the table than publishing something telling people how they should write a properly PSR formatted SQL query.

That said, I've learned over the last few years that some people think that interfaces may be a bad idea as well. So I guess you really can't make everyone happy. :)

In the end, I think that on average PHP-FIG will probably score somewhere in the middle. Some things will be more targeted at humans. Some will be more targeted at computers.

If I had a vote, though, I would vote that the group aim at computer related specs for awhile. I would vote for this in the hopes of getting some more wins in the group's column and hopefully avoid fresh human drama brought on by human oriented specs. Someday it may be safe to soften this stance, but for now I think this is the best way for PHP-FIG to move forward.

* I picked the SQL query example because it was the first thing that came to mind and I thought it made a great example of a human oriented spec. I realize the SQL PR was already closed at the time of writing. :)

Tags: php, php-fig, opinion, psr-2

Writing Silex Service Providers

So you've gotten to the point in your Silex application that you want to start breaking it out into modular pieces. Silex service providers to the rescue! The service provider interface appears to be really simple but do you know which things can safely be done in each method?

First, a high level overly generalized anatomy lesson of a Silex service provider:

namespace Acme\AwesomePackage;

use Silex\Application;
use Silex\ServiceProviderInterface;

class WhizbangServiceProvider implements ServiceProviderInterface
{
    public function register(Application $app)
    {
        // Define services here.
    }

    public function boot(Application $app)
    {
        // Configure the application and *carefully* use services.
    }
}

The primary purpose of a service provider is to configure the service container.

The what now?

The Silex Application class extends Pimple. This means that Application is a service container, more formally known as a Dependency Injection Container. This can be extremely useful once you know and understand about Dependency Injection Containers. For new users this can instead be very confusing.

If you are new to service containers or Dependency Injection, it would be a good idea to read up on the concept. If you are new to Pimple, reading up on it is going to be extremely important. Pimple's documentation is pretty sparse but dense.

One important aspect of a service container, and specifically in the case of Silex, is laziness.

Laziness

Services should be lazy. This means that where possible a service should not be instantiated until it is needed. With respect to a Silex application, if a service is accessed before run(), chances are you are doing it wrong!

Pimple does a pretty good job of this if you understand what this means and use it properly.

Here is a basic example of laziness in action:

<?php

$app['whirligig'] = $app->share(function() {
    return new Whirligig;
});

$app['whizbang.thingy'] = $app->share(function($app) {
    return new Whizbang\Thingy($app['whirligig']);
});

In this case, two services are defined. The whirligig service is referenced, but it is referenced from inside the whizbang.thingy service definition. This makes both of these services lazy.

A general rule, when dealing with a typical Silex/Pimple application, if $app or $container are passed into a closure or are provided as a function argument, the code is probably lazy.

Let's look at a more complex example that also shows the rule listed above in action:

<?php

// This is a BAD example.
$app['some.service']->addThing($app['another.service']);

// This is a GOOD example. It accomplishes the same thing as
// the bad example but in a way that is lazy.
$app['some.service'] = $app->share(
    $app->extend(
        'some.service',
        function($someService, $app) {
            // Note: we are in a closure and $app was passed
            // to the function; an indicator that this is
            // properly lazy code!
            $someService->addThing($app['another.service']);

            return $someService;
        }
    )
);

Why is the first example bad? As soon as this code is executed, both some.service and another.service services will be instantiated. Every time the application is run. This is not lazy.

The second example is better because the call to addThing on the some.service will only happen the first time some.service is requested. In this case, neither some.service nor another.service will be instantiated when this code is run. This makes both of these services lazy!

You can see the closure rule listed above in action. In the second example, $app is passed to the closure as a function argument. In the first example, this is not the case.

Laziness is very important when writing a service provider, so keep it in mind as you read about the register() and boot() methods. You can split your code up correctly but you if you get the laziness wrong you can still run into problems.

The register() Method

The purpose of the register() method is to configure the service container. In practical terms, this means setting parameters, defining new services, or extending existing services. Thats it!

For all intents and purposes, it is better to think of the method signature for register() to be:

public function register(\Pimple $container)
{
    // Define services here.
}

If you can't do it with Pimple, you probably shouldn't be doing it in register()! So it is best to forget about anything else you might otherwise do with Application, like get(), post(), mount(), before(), etc.

So, again, the only things that should be done in register() are setting parameters, defining new services, or extending existing services.

This will become more obvious in the future when Pimple service providers become a reality. At that point, the method signature for register() will actually be similar to what was written above, it will type hint Pimple. Until then, the Silex ServiceProviderInterface is going to continue to type hint Silex Application as an argument to register(). Don't let this fool you! You've been armed with knowledge! You should not actually be doing anything specific to Application in register().

The boot() Method

The boot() method is called after all of the service providers have registered themselves and after any inline services have been defined. The documentation for ServiceProviderInterface states that boot() can be used for "dynamic" configuration using services.

This is also a safe place to tie into Application specific methods. For example a service provider could choose to mount() a controller provider or it could choose to register a middleware like before() or after() from the boot() method.

While it is technically considered safe to access services from the service container at this point it is a good idea to keep in mind that services used in boot() will be instantiated for every request.

Conclusion

Service providers are invaluable in helping to modularize a Silex application but writing them incorrectly can be the source of many headaches and hidden performance problems. Hopefully this will help prevent some of the more easily avoidable mistakes.

These are somewhat tricky topics and can easily trip up even people who have been working with Silex for awhile. Have questions? There is quite a cool community brewing in #silex-php on Freenode. There are often people there willing to help out!

Tags: silex, pimple

Default Configuration Parameters for Pimple Service Providers

A common requirement when writing service providers for Pimple based projects is to provide default values for some or all of the configuration parameters. Unfortunately, this is not as straightforward as it sounds.


Update: And just like that, the whole premise of this blog post became moot. Thanks Igor! On one hand it is nice to have changed the world; on the other hand, it would have been nice to not have spent time writing this post. :P I would argue this is still a nice defensive strategy to guard against people going against the official recommendation and continue to set parameter values before registering a service, but I believe you can now officially tell someone to shove off and/or RTFM if they complain that your default parameters clobber their settings.


Since we cannot guarantee how Pimple is being configured or the order in which our service provider is loaded strange things can happen if we try to set default values in our service provider's register method.

This sample Silex service provider illustrates the problem:

use Silex\Application;
use Silex\ServiceProviderInterface;

class ExampleServiceProvider implements ServiceProviderInterface
{
    public function register(Application $app)
    {
        // Default configuration is for 'hello' to be 'world'.
        $app['example.hello'] = 'world';
    }

    public function boot(Application $app)
    {
    }
}

$app = new Application;
$app['example.hello'] = 'earth';
$app->register(new ExampleServiceProvider);

// INCORRECT
echo var_dump($app['example.hello']); // string(5) "world"

$app = new Application;
$app->register(new ExampleServiceProvider, array('example.hello' => 'earth'));

// CORRECT
echo var_dump($app['example.hello']); // string(5) "earth"

$app = new Application;
$app->register(new ExampleServiceProvider);
$app['example.hello'] = 'earth';

// CORRECT
echo var_dump($app['example.hello']); // string(5) "earth"

See what happened there? In the first case the parameter is set before the service provider is registered. The end result is that the service provider wrote the default value over the value previously set by the user. This is likely not going to be what the user expects.

This means we cannot blindly set any parameters in register. We must take care to only set a parameter to its default value if it has not already been set.

I borrowed the following solution from the Doctrine Service Provider while writing the Doctrine ORM Service Provider and it works pretty nicely:

use Silex\Application;
use Silex\ServiceProviderInterface;

class ExampleServiceProvider implements ServiceProviderInterface
{
    public function register(Application $app)
    {
        foreach ($this->getDefaultParameters() as $name => $value) {
            if (!isset($app[$name])) {
                $app[$name] = $value;
            }
        }
    }

    public function boot(Application $app)
    {
    }

    protected function getDefaultParameters()
    {
        return array(
            // Default configuration is for 'hello' to be 'world'.
            'example.hello' => 'world',
        );
    }
}

$app = new Application;
$app['example.hello'] = 'earth';
$app->register(new ExampleServiceProvider);

// CORRECT
echo var_dump($app['example.hello']); // string(5) "world"

$app = new Application;
$app->register(new ExampleServiceProvider);
$app['example.hello'] = 'earth';

// CORRECT
echo var_dump($app['example.hello']); // string(5) "world"

$app = new Application;
$app->register(new ExampleServiceProvider, array('example.hello' => 'earth'));
$app['example.hello'] = 'earth';

// CORRECT
echo var_dump($app['example.hello']); // string(5) "world"

Now we get the same expected value in all cases. Huzzah!

Tags: php, pimple, silex


Older Posts