$30 off During Our Annual Pro Sale. View Details »

Learning the PHP-FIG HTTP Stack (Midwest PHP 2019)

Learning the PHP-FIG HTTP Stack (Midwest PHP 2019)

The PHP-FIG has produced four PSRs relating to HTTP: PSR-7 HTTP message interfaces, PSR-15 HTTP Server Request Handlers, PSR-17 HTTP Factories and PSR-18 HTTP Client. Learn the differences between these PSRs and how they work together to enable a rich set of tools making it easier for applications and libraries to work with HTTP in PHP.

Beau Simensen

March 08, 2019
Tweet

More Decks by Beau Simensen

Other Decks in Programming

Transcript

  1. Learning the
    PHP-FIG HTTP Stack
    Beau Simensen
    Technical Strategy Consultant
    beausimensen.com • astrocasts.com • thatpodcast.io

    View Slide

  2. View Slide

  3. View Slide

  4. $client = create_http_client(); // implementation specific
    $response = $client ->request('GET', 'http: // www.php.net');
    if ($response ->getStatusCode() == 200) {
    $content = $response ->getContent();
    }
    $response = $client ->request('GET', 'http: //api/returning.json');
    if ($response ->getContentType() == 'application/json') {
    $json = json_decode($response ->getContent());
    }

    View Slide

  5. interface ClientInterface
    {
    /**
    * @return ResponseInterface
    */
    public function request($method, $url, array $headers = [], $content = null);
    }

    View Slide

  6. –Kris Wallsmith, 2012-03-24
    "[...] I think we should take a step back and start with interfaces for
    an HTTP request and an HTTP response. Once those are done, then
    we can move on to an HTTP client."

    View Slide

  7. interface ClientInterface
    {
    /**
    * @return ResponseInterface
    */
    public function sendRequest(RequestInterface $request);
    }

    View Slide

  8. –Beau Simensen, 2012-06-17
    "Either the client needs to construct the request itself
    (factory) or we need to provide a PSR request implementation that
    anyone can use."

    View Slide

  9. interface ClientInterface
    {
    /**
    * @return RequestInterface
    */
    public function createRequest($method, $url, array $headers = [], $content = null);
    }

    View Slide

  10. –Benjamin Eberlei, 2012-03-24
    "Just realized I forgot cookies."

    View Slide

  11. –Evert Pot, 2012-03-24
    "If you're taking this approach, you will find that you probably forgot
    a lot of HTTP features :)"

    View Slide

  12. –Evert Pot, 2012-03-24
    "If you want to keep things simple, I'd strongly suggest focusing on
    the following..."
    - http method
    - url
    - http version
    - headers
    - stream for body
    - http status code
    - human-readable status
    - http version
    - headers
    - stream for body
    Request Response

    View Slide

  13. Modeling HTTP
    Or "How PSR-7 happened"

    View Slide

  14. View Slide

  15. use Psr\Http\Message\RequestInterface;
    use Psr\Http\Message\ResponseInterface;
    class MyHttpClient
    {
    public function send(RequestInterface $request): ResponseInterface
    {
    // ...
    }
    }

    View Slide

  16. View Slide

  17. View Slide

  18. View Slide

  19. View Slide

  20. namespace Symfony\Component\HttpFoundation;
    class Request {
    public function __construct(
    array $query = [],
    array $request = [],
    array $attributes = [],
    array $cookies = [],
    array $files = [],
    array $server = [],
    $content = null
    ) {
    // ...
    }
    }

    View Slide

  21. namespace Symfony\Component\HttpFoundation;
    class Request {
    public static function createFromGlobals() {
    return self ::createRequestFromFactory(
    $_GET,
    $_POST,
    [],
    $_COOKIE,
    $_FILES,
    $_SERVER
    );
    }
    }

    View Slide

  22. namespace Zend\Http\PhpEnvironment;
    class Request extends \Zend\Http\Request {
    public function __construct() {
    $this ->setEnv(new Parameters($_ENV));
    if ($_GET) {
    $this ->setQuery(new Parameters($_GET));
    }
    if ($_POST) {
    $this ->setPost(new Parameters($_POST));
    }
    if ($_COOKIE) {
    $this ->setCookies(new Parameters($_COOKIE));
    }
    if ($_FILES) {
    $files = $this ->mapPhpFiles();
    $this ->setFiles(new Parameters($files));
    }
    $this ->setServer(new Parameters($_SERVER));
    }
    }

    View Slide

  23. View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. $request = Zend\Diactoros\ServerRequestFactory ::fromGlobals(
    $_SERVER,
    $_GET,
    $_POST,
    $_COOKIE,
    $_FILES
    );

    View Slide

  28. View Slide

  29. HTTP Messages are HTTP Messages

    View Slide

  30. View Slide

  31. PSR-7
    HTTP Message Interfaces

    View Slide

  32. Guzzle
    http://guzzlephp.org/

    View Slide

  33. Zend Diactoros
    https://github.com/zendframework/zend-diactoros

    View Slide

  34. Zend Stratigility
    https://github.com/zendframework/zend-stratigility

    View Slide

  35. Zend Expressive
    https://github.com/zendframework/zend-expressive

    View Slide

  36. Slim
    http://www.slimframework.com/

    View Slide

  37. View Slide

  38. View Slide

  39. The PHP HTTP group
    https://github.com/php-http

    View Slide

  40. HTTP Interop
    https://github.com/http-interop

    View Slide

  41. View Slide

  42. class ApiTheRightWay
    {
    private $client;
    public function __construct($client)
    {
    $this ->client = $client;
    }
    public function getStatus()
    {
    $request = new GuzzleHttp\Psr7\Request();
    $response = $this ->client ->send($request);
    return json_decode($response ->getBody() ->getContents(), true);
    }
    }

    View Slide

  43. interface DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface;
    }
    interface SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface;
    }

    View Slide

  44. interface DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface;
    }
    interface SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface;
    }

    View Slide

  45. __invoke()

    View Slide

  46. interface Middleware()

    View Slide

  47. View Slide

  48. View Slide

  49. –You might be thinking this
    “How is this better?”

    View Slide

  50. Completing the Stack

    View Slide

  51. –Kris Wallsmith, 2012-03-24
    "[...] I think we should take a step back and start with interfaces for
    an HTTP request and an HTTP response. Once those are done, then
    we can move on to an HTTP client."

    View Slide

  52. PSR-15
    HTTP Handlers

    View Slide

  53. interface DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface;
    }
    interface SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface;
    }

    View Slide

  54. class ChaosMiddleware implements SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface
    {
    if (rand(0, 99) === 0) {
    return (new GuzzleHttp\Psr7\Response(404));
    }
    return $next($request);
    }
    }

    View Slide

  55. class ChaosMiddleware implements DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface
    {
    if (rand(0, 99) === 0) {
    return $response ->withStatus(404);
    }
    return $next($request);
    }
    }

    View Slide

  56. interface DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface;
    }
    interface SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface;
    }

    View Slide

  57. interface DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface;
    }
    interface SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface;
    }

    View Slide

  58. –Beau Simensen, That Podcast Episode 45
    “You're going to need the Response.”

    View Slide

  59. –Dave Marshall, That Podcast Episode 45
    “Are you though?”

    View Slide

  60. –Scary Technical Question
    “But is the ResponseInterface instance even in a usable state?”

    View Slide

  61. –My personal soapbox for many, many years...
    “We either pass the object or we introduce factories.”

    View Slide

  62. Spoiler alert
    They chose factories.

    View Slide

  63. interface DoublePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    ResponseInterface $response,
    callable $next
    ): ResponseInterface;
    }
    interface SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface;
    }

    View Slide

  64. namespace Psr\Http\Server;
    use Psr\Http\Message\ResponseInterface;
    use Psr\Http\Message\ServerRequestInterface;
    interface MiddlewareInterface
    {
    public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler
    ): ResponseInterface;
    }

    View Slide

  65. namespace Psr\Http\Server;
    use Psr\Http\Message\ResponseInterface;
    use Psr\Http\Message\ServerRequestInterface;
    interface RequestHandlerInterface
    {
    public function handle(ServerRequestInterface $request): ResponseInterface;
    }

    View Slide

  66. View Slide

  67. View Slide

  68. View Slide

  69. View Slide

  70. View Slide

  71. View Slide

  72. View Slide

  73. View Slide

  74. View Slide

  75. View Slide

  76. View Slide

  77. View Slide

  78. PSR-17
    HTTP Factories

    View Slide

  79. namespace Psr\Http\Message;
    use Psr\Http\Message\RequestInterface;
    use Psr\Http\Message\UriInterface;
    interface RequestFactoryInterface
    {
    public function createRequest(string $method, $uri): RequestInterface;
    }

    View Slide

  80. namespace Psr\Http\Message;
    use Psr\Http\Message\ResponseInterface;
    interface ResponseFactoryInterface
    {
    public function createResponse(
    int $code = 200,
    string $reasonPhrase = ''
    ): ResponseInterface;
    }

    View Slide

  81. namespace Psr\Http\Message;
    use Psr\Http\Message\ServerRequestInterface;
    use Psr\Http\Message\UriInterface;
    interface ServerRequestFactoryInterface
    {
    public function createServerRequest(
    string $method,
    $uri,
    array $serverParams = []
    ): ServerRequestInterface;
    }

    View Slide

  82. namespace Psr\Http\Message;
    use Psr\Http\Message\StreamInterface;
    interface StreamFactoryInterface
    {
    public function createStream(string $content = ''): StreamInterface;
    public function createStreamFromFile(
    string $filename,
    string $mode = 'r'
    ): StreamInterface;
    public function createStreamFromResource($resource): StreamInterface;
    }

    View Slide

  83. namespace Psr\Http\Message;
    use Psr\Http\Message\StreamInterface;
    use Psr\Http\Message\UploadedFileInterface;
    interface UploadedFileFactoryInterface
    {
    public function createUploadedFile(
    StreamInterface $stream,
    int $size = null,
    int $error = \UPLOAD_ERR_OK,
    string $clientFilename = null,
    string $clientMediaType = null
    ): UploadedFileInterface;
    }

    View Slide

  84. namespace Psr\Http\Message;
    use Psr\Http\Message\UriInterface;
    interface UriFactoryInterface
    {
    public function createUri(string $uri = '') : UriInterface;
    }

    View Slide

  85. class ApiTheRightWay
    {
    private $client;
    public function __construct($client)
    {
    $this ->client = $client;
    }
    public function getStatus()
    {
    $request = new GuzzleHttp\Psr7\Request();
    $response = $this ->client ->send($request);
    return json_decode($response ->getBody() ->getContents(), true);
    }
    }

    View Slide

  86. class ApiTheRightWay
    {
    private $client;
    private $requestFactory;
    public function __construct($client, RequestFactoryInterface $requestFactory)
    {
    $this ->client = $client;
    $this ->requestFactory = $requestFactory;
    }
    public function getStatus()
    {
    $request = $this ->requestFactory ->createRequest();
    $response = $this ->client ->send($request);
    return json_decode($response ->getBody() ->getContents(), true);
    }
    }

    View Slide

  87. class ChaosMiddleware implements SinglePassHttpMiddleware
    {
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface
    {
    if (rand(0, 99) === 0) {
    return (new GuzzleHttp\Psr7\Response(404));
    }
    return $next($request);
    }
    }

    View Slide

  88. class ChaosMiddleware implements SinglePassHttpMiddleware
    {
    private $responseFactory;
    public function __construct(ResponseFactoryInterface $responseFactory)
    {
    $this ->responseFactory = $responseFactory;
    }
    public function __invoke(
    RequestInterface $request,
    callable $next
    ): ResponseInterface
    {
    if (rand(0, 99) === 0) {
    return $this ->responseFactory ->createResponse(404);
    }
    return $next($request);
    }
    }

    View Slide

  89. View Slide

  90. View Slide

  91. View Slide

  92. View Slide

  93. PSR-18
    HTTP Client

    View Slide

  94. namespace Psr\Http\Client;
    use Psr\Http\Message\RequestInterface;
    use Psr\Http\Message\ResponseInterface;
    interface ClientInterface
    {
    public function sendRequest(RequestInterface $request): ResponseInterface;
    }

    View Slide

  95. interface ClientInterface
    {
    /**
    * @return ResponseInterface
    */
    public function sendRequest(RequestInterface $request);
    }

    View Slide

  96. class ApiTheRightWay
    {
    private $client;
    private $requestFactory;
    public function __construct($client, RequestFactoryInterface $requestFactory)
    {
    $this ->client = $client;
    $this ->requestFactory = $requestFactory;
    }
    public function getStatus()
    {
    $request = $this ->requestFactory ->createRequest();
    $response = $this ->client ->send($request);
    return json_decode($response ->getBody() ->getContents(), true);
    }
    }

    View Slide

  97. class ApiTheRightWay
    {
    private $client;
    private $requestFactory;
    public function __construct(
    ClientInterface $client,
    RequestFactoryInterface $requestFactory
    ) {
    $this ->client = $client;
    $this ->requestFactory = $requestFactory;
    }
    public function getStatus()
    {
    $request = $this ->requestFactory ->createRequest();
    $response = $this ->client ->sendRequest($request);
    return json_decode($response ->getBody() ->getContents(), true);
    }
    }

    View Slide

  98. View Slide

  99. View Slide

  100. Learning the
    PHP-FIG HTTP Stack
    Beau Simensen
    Technical Strategy Consultant
    beausimensen.com • astrocasts.com • thatpodcast.io
    Thanks!

    View Slide