Providers allow the developer to reuse parts of an application into another
one. Silex provides two types of providers defined by two interfaces:
ServiceProviderInterface
for services and ControllerProviderInterface
for controllers.
In order to load and use a service provider, you must register it on the application:
$app = new Silex\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:
1 2 3 4 5 | $app->register(new Acme\DatabaseServiceProvider(), array(
'database.dsn' => 'mysql:host=localhost;dbname=myapp',
'database.user' => 'root',
'database.password' => 'secret_root_password',
));
|
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.
There are a few providers that you get out of the box. All of these are within
the Silex\Provider
namespace:
Note
The Silex 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.
Some service providers are developed by the community. Those third-party providers are listed on Silex' repository wiki.
You are encouraged to share yours.
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 Silex work, but may allow your provider to be used outside of Silex.
Optionally, your service provider can implement the
Silex\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 Silex\Api\EventListenerProviderInterface
.
This interface contains the subscribe()
method, which allows your provider to
subscribe event listener with Silex's EventDispatcher, just before it handles a
request:
interface EventListenerProviderInterface
{
function subscribe(Container $app, EventDispatcherInterface $dispatcher);
}
Here is an example of such a provider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | namespace Acme;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Silex\Application;
use Silex\Api\BootableProviderInterface;
use Silex\Api\EventListenerProviderInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
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(FilterResponseEvent $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:
1 2 3 4 5 6 7 8 9 10 11 12 13 | use Symfony\Component\HttpFoundation\Request;
$app = new Silex\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
.
In order to load and use a controller provider, you must "mount" its controllers under a path:
$app = new Silex\Application();
$app->mount('/blog', new Acme\BlogControllerProvider());
All controllers defined by the provider will now be available under the
/blog
path.
Providers must implement the Silex\Api\ControllerProviderInterface
:
interface ControllerProviderInterface
{
public function connect(Application $app);
}
Here is an example of such a provider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | namespace Acme;
use Silex\Application;
use Silex\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 Silex\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.