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.