Providers
=========

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.

Service Providers
-----------------

Loading providers
~~~~~~~~~~~~~~~~~

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::

    $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 ``Silex\Provider`` namespace:

* :doc:`DoctrineServiceProvider <providers/doctrine>`
* :doc:`FormServiceProvider <providers/form>`
* :doc:`HttpCacheServiceProvider <providers/http_cache>`
* :doc:`MonologServiceProvider <providers/monolog>`
* :doc:`RememberMeServiceProvider <providers/remember_me>`
* :doc:`SecurityServiceProvider <providers/security>`
* :doc:`SerializerServiceProvider <providers/serializer>`
* :doc:`ServiceControllerServiceProvider <providers/service_controller>`
* :doc:`SessionServiceProvider <providers/session>`
* :doc:`SwiftmailerServiceProvider <providers/swiftmailer>`
* :doc:`TranslationServiceProvider <providers/translation>`
* :doc:`TwigServiceProvider <providers/twig>`
* :doc:`ValidatorServiceProvider <providers/validator>`

.. note::

    The Silex core team maintains a `WebProfiler
    <https://github.com/silexphp/Silex-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 `Silex' repository wiki
<https://github.com/silexphp/Silex/wiki/Third-Party-ServiceProviders>`_.

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 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::

    namespace Acme;

    use Pimple\Container;
    use Pimple\ServiceProviderInterface;
    use Silex\Application;
    use Silex\Api\BootableProviderInterface;
    use Silex\Api\EventListenerProviderInterface;
    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::

    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``.

.. _controller-providers:

Controller Providers
--------------------

Loading providers
~~~~~~~~~~~~~~~~~

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.

Creating a provider
~~~~~~~~~~~~~~~~~~~

Providers must implement the ``Silex\Api\ControllerProviderInterface``::

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

Here is an example of such a provider::

    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.
