HTTP Factories

Factory interfaces for PSR-7 HTTP objects.

Rationale

While it should be possible to use every PSR-7 aware HTTP client with any request, URI and stream implementation, instantiating objects explicitly would still tie the code to a specific implementation. If each reusable library is tied to a specific message implementation, an application could end up installing several message implementations. The factories move instantiation out of the library code, further decoupling libraries from implementation.

The FIG was pretty straightforward by NOT putting any construction logic into PSR-7. The MessageFactory aims to provide an easy way to construct messages.

Factories

The php-http/message-factory package defines interfaces for PSR-7 factories including:

  • RequestFactory

  • ResponseFactory

  • MessageFactory (combination of request and response factories)

  • StreamFactory

  • UriFactory

Implementations of the interfaces above for Laminas Diactoros (and its abandoned predecessor Zend Diactoros), Guzzle PSR-7 and the `Slim Framework`_ can be found in php-http/message.

Usage

Instantiate the factories in your bootstrap code or use discovery for them. Inject the factories into the rest of your code to limit the implementation choice to the bootstrapping code:

// ApiClient.php

use Http\Message\RequestFactory;
use Http\Message\StreamFactory;
use Http\Message\UriFactory;

class ApiClient
{
    /**
     * @var RequestFactory
     */
    private $requestFactory;

    /**
     * @var StreamFactory
     */
    private $streamFactory;

    /**
     * @var UriFactory
     */
    private $uriFactory;

    public function __construct(
        RequestFactory $requestFactory,
        StreamFactory $streamFactory,
        UriFactory $uriFactory
    ) {
        $this->requestFactory = $requestFactory;
        $this->streamFactory = $streamFactory;
        $this->uriFactory = $uriFactory;
    }

    public function doStuff()
    {
        $request = $this->requestFactory->createRequest('GET', 'http://httplug.io');
        $stream = $this->streamFactory->createStream('stream content');
        $uri = $this->uriFactory->createUri('http://httplug.io');
        ...
    }
}

The bootstrapping code could look like this:

// bootstrap.php
use Http\Message\MessageFactory\DiactorosMessageFactory;
use Http\Message\StreamFactory\DiactorosStreamFactory;
use Http\Message\UriFactory\DiactorosUriFactory;

$apiClient = new ApiClient(
    new DiactorosMessageFactory(),
    new DiactorosStreamFactory(),
    new DiactorosUriFactory()
);

You could also use Discovery to make the factory arguments optional and automatically find an available factory in the client:

// ApiClient.php

use Http\Discovery\MessageFactoryDiscovery;
use Http\Discovery\StreamFactoryDiscovery;
use Http\Discovery\UriFactoryDiscovery;
use Http\Message\RequestFactory;
use Http\Message\StreamFactory;
use Http\Message\UriFactory;

class ApiClient
{
    public function __construct(
        RequestFactory $requestFactory = null,
        StreamFactory $streamFactory = null,
        UriFactory $uriFactory = null
    ) {
        $this->requestFactory = $requestFactory ?: MessageFactoryDiscovery::find(),
        $this->streamFactory = $streamFactory ?: StreamFactoryDiscovery::find();
        $this->uriFactory = $uriFactory ?: UriFactoryDiscovery::find();;
    }

    ...
}

Hint

If you create requests only and no responses, use RequestFactory in the type hint, instead of the MessageFactory. And vice versa if you create responses only.

Server Side Factories

It would make sense to also provide factories for the server side constructs ServerRequestInterface and UploadedFileInterface. We did not get around to do that yet. Contributions are welcome if you want to define the ServerRequestFactory and UploadedFileFactory.