Providers

Providers allow the developer to reuse parts of an application into another one. PrestoPHP provides two types of providers defined by two interfaces: ServiceProviderInterface for services and ControllerProviderInterface for controllers.

Service Providers

Loading providers

In order to load and use a service provider, you must register it on the application:

$app = new PrestoPHP\Application();

$app->register(new Acme\DatabaseServiceProvider());

You can also provide some parameters as a second argument. These will be set after the provider is registered, but before it is booted:

$app->register(new Acme\DatabaseServiceProvider(), array(
    'database.dsn'      => 'mysql:host=localhost;dbname=myapp',
    'database.user'     => 'root',
    'database.password' => 'secret_root_password',
));

Conventions

You need to watch out in what order you do certain things when interacting with providers. Just keep these rules in mind:

  • Overriding existing services must occur after the provider is registered.

    Reason: If the service already exists, the provider will overwrite it.

  • You can set parameters any time after the provider is registered, but before the service is accessed.

    Reason: Providers can set default values for parameters. Just like with services, the provider will overwrite existing values.

Included providers

There are a few providers that you get out of the box. All of these are within the PrestoPHP\Provider namespace:

Note

The PrestoPHP core team maintains a WebProfiler provider that helps debug code in the development environment thanks to the Symfony web debug toolbar and the Symfony profiler.

Third party providers

Some service providers are developed by the community. Those third-party providers are listed on PrestoPHP’ repository wiki.

You are encouraged to share yours.

Creating a provider

Providers must implement the Pimple\ServiceProviderInterface:

interface ServiceProviderInterface
{
    public function register(Container $container);
}

This is very straight forward, just create a new class that implements the register method. In the register() method, you can define services on the application which then may make use of other services and parameters.

Tip

The Pimple\ServiceProviderInterface belongs to the Pimple package, so take care to only use the API of Pimple\Container within your register method. Not only is this a good practice due to the way Pimple and PrestoPHP work, but may allow your provider to be used outside of PrestoPHP.

Optionally, your service provider can implement the PrestoPHP\Api\BootableProviderInterface. A bootable provider must implement the boot() method, with which you can configure the application, just before it handles a request:

interface BootableProviderInterface
{
    function boot(Application $app);
}

Another optional interface, is the PrestoPHP\Api\EventListenerProviderInterface. This interface contains the subscribe() method, which allows your provider to subscribe event listener with PrestoPHP’s EventDispatcher, just before it handles a request:

interface EventListenerProviderInterface
{
    function subscribe(Container $app, EventDispatcherInterface $dispatcher);
}

Here is an example of such a provider:

namespace Acme;

use Pimple\Container;
use Pimple\ServiceProviderInterface;
use PrestoPHP\Application;
use PrestoPHP\Api\BootableProviderInterface;
use PrestoPHP\Api\EventListenerProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\ResponseEvent;

class HelloServiceProvider implements ServiceProviderInterface, BootableProviderInterface, EventListenerProviderInterface
{
    public function register(Container $app)
    {
        $app['hello'] = $app->protect(function ($name) use ($app) {
            $default = $app['hello.default_name'] ? $app['hello.default_name'] : '';
            $name = $name ?: $default;

            return 'Hello '.$app->escape($name);
        });
    }

    public function boot(Application $app)
    {
        // do something
    }

    public function subscribe(Container $app, EventDispatcherInterface $dispatcher)
    {
        $dispatcher->addListener(KernelEvents::REQUEST, function(ResponseEvent $event) use ($app) {
            // do something
        });
    }
}

This class provides a hello service which is a protected closure. It takes a name argument and will return hello.default_name if no name is given. If the default is also missing, it will use an empty string.

You can now use this provider as follows:

use Symfony\Component\HttpFoundation\Request;

$app = new PrestoPHP\Application();

$app->register(new Acme\HelloServiceProvider(), array(
    'hello.default_name' => 'Igor',
));

$app->get('/hello', function (Request $request) use ($app) {
    $name = $request->get('name');

    return $app['hello']($name);
});

In this example we are getting the name parameter from the query string, so the request path would have to be /hello?name=Fabien.

Controller Providers

Loading providers

In order to load and use a controller provider, you must “mount” its controllers under a path:

$app = new PrestoPHP\Application();

$app->mount('/blog', new Acme\BlogControllerProvider());

All controllers defined by the provider will now be available under the /blog path.

Creating a provider

Providers must implement the PrestoPHP\Api\ControllerProviderInterface:

interface ControllerProviderInterface
{
    public function connect(Application $app);
}

Here is an example of such a provider:

namespace Acme;

use PrestoPHP\Application;
use PrestoPHP\Api\ControllerProviderInterface;

class HelloControllerProvider implements ControllerProviderInterface
{
    public function connect(Application $app)
    {
        // creates a new controller based on the default route
        $controllers = $app['controllers_factory'];

        $controllers->get('/', function (Application $app) {
            return $app->redirect('/hello');
        });

        return $controllers;
    }
}

The connect method must return an instance of ControllerCollection. ControllerCollection is the class where all controller related methods are defined (like get, post, match, …).

Tip

The Application class acts in fact as a proxy for these methods.

You can use this provider as follows:

$app = new PrestoPHP\Application();

$app->mount('/blog', new Acme\HelloControllerProvider());

In this example, the /blog/ path now references the controller defined in the provider.

Tip

You can also define a provider that implements both the service and the controller provider interface and package in the same class the services needed to make your controllers work.