From 1945f63ca11e308b942f9f6196f8e568d1ecec45 Mon Sep 17 00:00:00 2001 From: PJ Dietz Date: Sun, 7 Jun 2015 09:42:27 -0400 Subject: [PATCH] Add docs for extending and customizing --- docs/source/extending.rst | 168 ++++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 1 + 2 files changed, 169 insertions(+) create mode 100644 docs/source/extending.rst diff --git a/docs/source/extending.rst b/docs/source/extending.rst new file mode 100644 index 0000000..d32d2c4 --- /dev/null +++ b/docs/source/extending.rst @@ -0,0 +1,168 @@ +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. + +Custom Middleware +----------------- + +Imagine you found a middleware class from a third party that does exactly what you need. The only problem is that it implements a different middleware interface. + +Here's the interface for the third-party middleware: + +.. code-block:: php + + interface OtherMiddlewareInterface + { + /** + * @param \Psr\Http\Message\ServerRequestInterface $request + * @param \Psr\Http\Message\ResponseInterface $response + * @return \Psr\Http\Message\ResponseInterface + */ + public function run( + \Psr\Http\Message\ServerRequestInterface $request, + \Psr\Http\Message\ResponseInterface $response + ); + } + +Wrapping +^^^^^^^^ + +One solution is to wrap an instance of this middleware inside of a ``WellRESTed\MiddlewareInterface`` instance. + +.. code-block:: php + + /** + * Wraps an instance of OtherMiddlewareInterface + */ + class OtherWrapper implements \WellRESTed\MiddlewareInterface + { + private $middleware; + + public function __construct(OtherMiddlewareInterface $middleware) + { + $this->middleware = $middleware; + } + + public function __invoke( + \Psr\Http\Message\ServerRequestInterface $request, + \Psr\Http\Message\ResponseInterface $response, + $next + ) { + // Run the wrapped middleware. + $response = $this->middleware->run($request, $response); + // Pass the middleware's response to $next and return the result. + return $next($request, $myResponse); + } + } + +.. note:: + + ``OtherMiddlewareInterface`` doesn't provide any information about how to propagate the request and response through a chain of middleware, so I chose to call ``$next`` every time. If there's a sensible way to tell that you should stop propagating, your wrapper class could return a response without calling ``$next`` under those circumstances. It's up to you and the middleware you're wrapping. + + +To use this wrapped middleware, you can do something like this: + +.. code-block:: php + + // The class we need to wrap; implements OtherMiddlewareInterface + $other = new OtherMiddleware(); + + // The wrapper class; implements WellRESTed\MiddlewareInterface + $otherWrapper = new OtherWrapper($other) + + $server = new WellRESTed\Server(); + $server->add($otherWrapper); + +Custom Dispatcher +^^^^^^^^^^^^^^^^^ + +Wrapping works well when you have one or two middleware implementing a third-party interface. If you want to integrate a lot of middleware classes that implement a given third-party interface, you're better off customizing the dispatcher. + +The dispatcher is an instance that unpacks your middleware and sends the request and response through it. A default dispatcher is created for you when you instantiate your ``WellRESTed\Server`` (without passing the second argument). The server instantiates a ``WellRESTed\Dispatching\Dispatcher`` which is capable of running middleware provided as a callable, a string containing the fully qualified class name of a middleware, or an array of middleware. (See `Using Middleware`_ for a description of what a default dispatcher can dispatch.) + +If you need the ability to dispatch other types of middleware, you can create your own by implementing ``WellRESTed\Dispatching\DispatcherInterface``. The easiest way to do this is to subclass ``WellRESTed\Dispatching\Dispatcher``. Here's an example that extends ``Dispatcher`` and adds support for ``OtherMiddlewareInterface``: + +.. code-block:: php + + namespace MyApi; + + /** + * Dispatcher with support for OtherMiddlewareInterface + */ + class CustomDispatcher extends \WellRESTed\Dispatching\Dispatcher + { + public function dispatch( + $middleware, + \Psr\Http\Message\ServerRequestInterface $request, + \Psr\Http\Message\ResponseInterface $response, + $next + ) { + try { + // Use the dispatch method in the parent class first. + $response = parent::dispatch($middleware, $request, $response, $next); + } catch (\WellRESTed\Dispatching\DispatchException $e) { + // If there's a problem, check if the middleware implements + // OtherMiddlewareInterface. Dispatch it if it does. + if ($middleware instanceof OtherMiddlewareInterface) { + $response = $middleware->run($request, $response); + $response = $next($request, $response); + } else { + // Otherwise, re-throw the exception. + throw $e; + } + } + return $response; + } + } + +To use this dispatcher, pass it to the constructor of ``WellRESTed\Server`` as the second argument. (The first argument is a hash array to use as `request attributes`_.) + +.. code-block:: php + + // Create an instance of your custom dispatcher. + $dispatcher = new MyApi\CustomDispatcher; + + // Pass this dispatcher to the server. + $server = new WellRESTed\Server(null, $dispatcher); + + // Now, you can add any middleware implementing OtherMiddlewareInterface + $other = new OtherMiddleware(); + $server->add($other); + + // Registering OtherMiddlewareInterface middleware by FQCN will work, too. + +Message Customization +--------------------- + +In the example above, we passed a custom dispatcher to the server. You can also customize your server in other ways. For example, if you have a different implementation of PSR-7_ messages that you prefer, you can pass them into the ``Server::respond`` method: + +.. code-block:: php + + // Represents the request submitted by the client. + $request = new ThirdParty\Request(); + // A "blank" response. + $response = new ThirdParty\Response(); + + $server = new WellRESTed\Server(); + // ...add middleware... + + // Pass your request and response to Server::respond + $server->response($request, $response); + +Even if you don't want to use a different implementation, you may still find a reason to provide you're own messages. For example, the default response status code for a ``WellRESTed\Message\Response`` is 500. If you wanted to make the default 200 instead, you could do something like this: + +.. code-block:: php + + // The first argument is the status code. + $response = new \WellRESTed\Message\Response(200); + + $server = new \WellRESTed\Server(); + // ...add middleware... + + // Pass the response to respond() + $server->respond(null, $response); + +.. _PSR-7: http://www.php-fig.org/psr/psr-7/ +.. _Using Middleware: middleware.html#using-middleware +.. _Request Attributes: messages.html#attributes diff --git a/docs/source/index.rst b/docs/source/index.rst index bda4298..698c6b5 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -108,6 +108,7 @@ Contents router uri-templates uri-templates-advanced + extending dependency-injection web-server-configuration