Extending and Customizing

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

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:

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.

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

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

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

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

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

// 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);

Server Customization

As an alternative to passing you preferred request and response instances into Server::respond, you can extend Server to obtain default values from a different source.

Classes such as Server that create dependencies as defaults keep the instantiation isolated in easy-to-override methods. For example, Server has a protected method getResponse that instantiates and returns a new response. You can easily replace this method with your own that returns the default response of your choice.

For example, imagine you have a dependency container that provides the starting messages for you. You can subclass Server to obtain and use these messages as defaults like this:

class CustomerServer extends WellRESTed\Server
{
    /** @var A dependency container */
    private $container;

    public function __construct(
        $container,
        array $attributes = null,
        DispatcherInterface $dispatcher = null,
        $pathVariablesAttributeName = null
    ) {
        // Call the parent constructor with the expected parameters.
        parent::__construct($attributes, $dispatcher, $pathVariablesAttributeName);
        // Store the container.
        $this->container = $container;
    }

    /**
     * Redefine this method, which is called in Server::respond when
     * the caller does not provide a request.
     */
    protected function getRequest()
    {
        // Return a request obtained from the container.
        return $this->container["request"];
    }

    /**
     * Redefine this method, which is called in Server::respond when
     * the caller does not provide a response.
     */
    protected function getResponse()
    {
        // Return a response obtained from the container.
        return $this->container["response"];
    }
}

In addition to the messages, you can do similar customization for other Server dependencies such as the dispatcher (see above), the transmitter (which writes the response out to the client), and the routers that are created with Server::createRouter. These dependencies are instantiated in isolated methods as with the request and response to make this sort of customization easy, and other classes such as Router use this pattern as well. See the source code, and don’t hesitated to subclass.