From fa6fb124ad56ca7b51702586a3249267bdb9ac8c Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Sat, 13 Jun 2015 18:21:30 -0400 Subject: [PATCH] Update documentation --- docs/source/dependency-injection.rst | 73 ++++++++++++---------------- docs/source/extending.rst | 2 +- docs/source/middleware.rst | 26 +++++----- 3 files changed, 46 insertions(+), 55 deletions(-) diff --git a/docs/source/dependency-injection.rst b/docs/source/dependency-injection.rst index a3530f5..86bad2c 100644 --- a/docs/source/dependency-injection.rst +++ b/docs/source/dependency-injection.rst @@ -3,7 +3,7 @@ Dependency Injection WellRESTed strives to play nicely with other code and not force developers into using any specific libraries or frameworks. As such, WellRESTed does not provide a dependency injection container, nor does it require you to use a specific container (or any). -This section describes a handful of ways of making the dependency container of your choice available to middleware with WellRESTed. +This section describes a handful of ways of making dependencies available to middleware. Request Attribute ^^^^^^^^^^^^^^^^^ @@ -28,84 +28,75 @@ When the server dispatches middleware, the middleware will be able to read the c // It's a super cool dependency container! } -Callables -^^^^^^^^^ +.. note:: -Another approach is to use callables that return ``MiddlewareInterface`` instances when you assign middleware. This approach provides an opportunity to pass the container into the middleware's constructor. + This approach is technically more of a `service locator`_ pattern. It's easy to implement, and it allows you the most flexibility in how you assign middleware. + It has some drawbacks as well, though. For example, your middleware is now dependent on your container, and describing which items needs to be **in** the container provides its own challenge. + + If your interested in a truer dependency injection approach, read on to the next section where we look at registering middleware factories. + +Middleware Factories +^^^^^^^^^^^^^^^^^^^^ + +Another approach is to use a factory function that returns middleware, usually in the form of a ``MiddlewareInterface`` instance. This approach provides the opportunity to pass dependencies to your middleware's constructor, while still delaying instantiation until the middleware is used. + +Imagine a middleware ``FooHandler`` that depends on a ``BarInterface``, and ``BazInterface``. .. code-block:: php - Class CatHandler implements WellRESTed\MiddlewareInterface + Class FooHandler implements WellRESTed\MiddlewareInterface { - private $container; + private $bar; + private $baz; - public function __construct($container) + public function __construct(BarInterface $bar, BazInterface $bar) { - $this->container = $container; + $this->bar = $bar; + $this->baz = $baz; } public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next); { - // Do something with the $this->container, and make a response. + // Do something with the bar and baz and update the response. // ... return $response; } } -When you add the middleware to the server or register it with a router, use a callable that passes container into the constructor. +When you add the middleware to the server or register it with a router, you can use a callable that passes appropriate instances into the constructor. .. code-block:: php - $container = new MySuperCoolDependencyContainer(); - - $catHandler = function () use ($container) { - return new CatHandler($container); + // Assume $bar and $baz exist in this scope. + $fooHandlerFactory = function () use ($bar, $bar) { + return new FooHandler($bar, $baz); } $server = new Server(); $server->add( $server->createRoute() - ->register("GET", "/cats/{cat}", $catHandler) + ->register("GET", "/foo/{id}", $fooHandlerFactory) ); $server->respond(); -For extra fun, store the callable that provides the handler in the container. Here's an example using Pimple_). +You can combine this approach with a dependency container. Here's an example using Pimple_). .. code-block:: php $c = new Pimple\Container(); - $c["catHandler"] = $c->protect(function () use ($c) { - return new CatHandler($c); + $c["bar"] = /* Return a BarInterface */ + $c["baz"] = /* Return a BazInterface */ + $c["fooHandler"] = $c->protect(function () use ($c) { + return new FooHandler($c["bar"], $c["baz"]); }); $server = new Server(); $server->add( $server->createRoute() - ->register("GET", "/cats/{cat}", $c["catHandler"]) - ); - $server->respond(); - -Combined -^^^^^^^^ - -Of course these two approaches are not mutually exclusive. You can even obtain your server from the container as well, for good measure. - -.. code-block:: php - - $c = new Pimple\Container(); - $c["server"] = function ($c) { - return new Server(["container" => $c); - }; - $c["catHandler"] = $c->protect(function () use ($c) { - return new CatHandler($c); - }); - - $server = $c["server"]; - $server->add( - $server->createRoute() - ->register("GET", "/cats/{cat}", $c["catHandler"]) + ->register("GET", "/foo/{id}", $c["fooHandler"]) ); $server->respond(); .. _Pimple: http://pimple.sensiolabs.org +.. _service locator: https://en.wikipedia.org/wiki/Service_locator_pattern diff --git a/docs/source/extending.rst b/docs/source/extending.rst index a5ee66f..c92b446 100644 --- a/docs/source/extending.rst +++ b/docs/source/extending.rst @@ -1,7 +1,7 @@ Extending and Customizing ========================= -WellRESTed is designed with customization in mind. This section will describe some common scenarios for customization, starting with using middleware that implements a different interface. +WellRESTed is designed with customization in mind. This section describes some common scenarios for customization, starting with using middleware that implements a different interface. Custom Middleware ----------------- diff --git a/docs/source/middleware.rst b/docs/source/middleware.rst index 2c7e922..14c8d6a 100644 --- a/docs/source/middleware.rst +++ b/docs/source/middleware.rst @@ -80,7 +80,7 @@ Middleware can be a callable (as in the `Getting Started`_) or an implementation Using Middleware ^^^^^^^^^^^^^^^^ -Methods that accept middleware (e.g., ``Server::add``, ``Router::register``) allow you to provide middleware in a number of ways. For example, when you can provide a callable, a string containing a class name, an instance, or even an array containing a sequence of middleware. +Methods that accept middleware (e.g., ``Server::add``, ``Router::register``) allow you to provide middleware in a number of ways. For example, you can provide a string containing a class name, a middleware callable, a factory callable, or even an array containing a sequence of middleware. Fully Qualified Class Name (FQCN) --------------------------------- @@ -93,8 +93,8 @@ Assume your Web service has an autoloadable class named ``Webservice\Widgets\Wid The class is not loaded, and no instances are created, until the route is matched and dispatched. Even for a router with 100 routes, no middleware registered by string name is loaded, except for the one that matches the request. -Callable Provider ------------------ +Factory Callable +---------------- You can also use a callable to instantiate and return a ``MiddlewareInterface`` instance or middleware callable. @@ -104,14 +104,14 @@ You can also use a callable to instantiate and return a ``MiddlewareInterface`` return new \Webservice\Widgets\WidgetHandler(); }); -This still delays instantiation, but gives you some added flexibility. For example, you could define middleware that receives some configuration upon construction. +This still delays instantiation, but gives you some added flexibility. For example, you could define middleware that receives some dependencies upon construction. .. code-block:: php $container = new MySuperCoolDependencyContainer(); $router->add("GET,PUT,DELETE", "/widgets/{id}", function () use ($container) { - return new \Webservice\Widgets\WidgetHandler($container); + return new \Webservice\Widgets\WidgetHandler($container["foo"], $container["baz"]); }); This is one approach to `dependency injection`_. @@ -130,10 +130,7 @@ Use a middleware callable directly. return $next($request, $response); }); -Instance --------- - -You can also provide pass an instance directly as middleware. +Because ``WellRESTed\MiddlewareInterface`` has an ``__invoke`` method, implementing instances are also middleware callables. Assuming ``WidgetHandler`` implements ``MiddelewareInterface``, you can do this: .. code-block:: php @@ -141,7 +138,7 @@ You can also provide pass an instance directly as middleware. .. warning:: - This is simple, but has a significant disadvantage over the other options because each middleware used this way will be loaded and instantiated, even though only one middleware will actually be used for a given request-response cycle. You may find this approach useful for testing, but avoid if for production code. + This is simple, but has a significant disadvantage over the other options because each middleware used this way will be loaded and instantiated, even if it's not needed for a given request-response cycle. You may find this approach useful for testing, but avoid if for production code. Array ----- @@ -200,14 +197,17 @@ We can add authorization for just the ``/widgets/{id}`` endpoint like this: .. code-block:: php - $router->register("GET,PUT,DELETE", "/widgets/{id}", [ + $server = new \WellRESTed\Server(); + $server->add($server->createRouter() + ->register("GET,PUT,DELETE", "/widgets/{id}", [ 'Webservice\Authorization', 'Webservice\Widgets\WidgetHandler' - ]); + ]) + ->respond(); Or, if you wanted to use the authorization for the entire service, you can add it to the ``Server`` in front of the ``Router``. - .. code-block:: php +.. code-block:: php $server = new \WellRESTed\Server(); $server