Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

23d971deeb3975a7d28246192fbbe7b7?s=128

Beau Simensen

March 08, 2019
Tweet

Transcript

  1. Learning the PHP-FIG HTTP Stack Beau Simensen Technical Strategy Consultant

    beausimensen.com • astrocasts.com • thatpodcast.io
  2. None
  3. None
  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()); }
  5. interface ClientInterface { /** * @return ResponseInterface */ public function

    request($method, $url, array $headers = [], $content = null); }
  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."
  7. interface ClientInterface { /** * @return ResponseInterface */ public function

    sendRequest(RequestInterface $request); }
  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."
  9. interface ClientInterface { /** * @return RequestInterface */ public function

    createRequest($method, $url, array $headers = [], $content = null); }
  10. –Benjamin Eberlei, 2012-03-24 "Just realized I forgot cookies."

  11. –Evert Pot, 2012-03-24 "If you're taking this approach, you will

    find that you probably forgot a lot of HTTP features :)"
  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
  13. Modeling HTTP Or "How PSR-7 happened"

  14. None
  15. use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; class MyHttpClient { public function send(RequestInterface

    $request): ResponseInterface { // ... } }
  16. None
  17. None
  18. None
  19. None
  20. namespace Symfony\Component\HttpFoundation; class Request { public function __construct( array $query

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

    return self ::createRequestFromFactory( $_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER ); } }
  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)); } }
  23. None
  24. None
  25. None
  26. None
  27. $request = Zend\Diactoros\ServerRequestFactory ::fromGlobals( $_SERVER, $_GET, $_POST, $_COOKIE, $_FILES );

  28. None
  29. HTTP Messages are HTTP Messages

  30. None
  31. PSR-7 HTTP Message Interfaces

  32. Guzzle http://guzzlephp.org/

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

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

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

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

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

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

  41. None
  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); } }
  43. interface DoublePassHttpMiddleware { public function __invoke( RequestInterface $request, ResponseInterface $response,

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

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

  46. interface Middleware()

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

  50. Completing the Stack

  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."
  52. PSR-15 HTTP Handlers

  53. interface DoublePassHttpMiddleware { public function __invoke( RequestInterface $request, ResponseInterface $response,

    callable $next ): ResponseInterface; } interface SinglePassHttpMiddleware { public function __invoke( RequestInterface $request, callable $next ): ResponseInterface; }
  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); } }
  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); } }
  56. interface DoublePassHttpMiddleware { public function __invoke( RequestInterface $request, ResponseInterface $response,

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

    callable $next ): ResponseInterface; } interface SinglePassHttpMiddleware { public function __invoke( RequestInterface $request, callable $next ): ResponseInterface; }
  58. –Beau Simensen, That Podcast Episode 45 “You're going to need

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

  60. –Scary Technical Question “But is the ResponseInterface instance even in

    a usable state?”
  61. –My personal soapbox for many, many years... “We either pass

    the object or we introduce factories.”
  62. Spoiler alert They chose factories.

  63. interface DoublePassHttpMiddleware { public function __invoke( RequestInterface $request, ResponseInterface $response,

    callable $next ): ResponseInterface; } interface SinglePassHttpMiddleware { public function __invoke( RequestInterface $request, callable $next ): ResponseInterface; }
  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; }
  65. namespace Psr\Http\Server; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; interface RequestHandlerInterface { public

    function handle(ServerRequestInterface $request): ResponseInterface; }
  66. None
  67. None
  68. None
  69. None
  70. None
  71. None
  72. None
  73. None
  74. None
  75. None
  76. None
  77. None
  78. PSR-17 HTTP Factories

  79. namespace Psr\Http\Message; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\UriInterface; interface RequestFactoryInterface { public

    function createRequest(string $method, $uri): RequestInterface; }
  80. namespace Psr\Http\Message; use Psr\Http\Message\ResponseInterface; interface ResponseFactoryInterface { public function createResponse(

    int $code = 200, string $reasonPhrase = '' ): ResponseInterface; }
  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; }
  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; }
  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; }
  84. namespace Psr\Http\Message; use Psr\Http\Message\UriInterface; interface UriFactoryInterface { public function createUri(string

    $uri = '') : UriInterface; }
  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); } }
  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); } }
  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); } }
  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); } }
  89. None
  90. None
  91. None
  92. None
  93. PSR-18 HTTP Client

  94. namespace Psr\Http\Client; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; interface ClientInterface { public

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

    sendRequest(RequestInterface $request); }
  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); } }
  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); } }
  98. None
  99. None
  100. Learning the PHP-FIG HTTP Stack Beau Simensen Technical Strategy Consultant

    beausimensen.com • astrocasts.com • thatpodcast.io Thanks!