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.

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
  2. $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()); }
  3. interface ClientInterface { /** * @return ResponseInterface */ public function

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

    createRequest($method, $url, array $headers = [], $content = null); }
  7. –Evert Pot, 2012-03-24 "If you're taking this approach, you will

    find that you probably forgot a lot of HTTP features :)"
  8. –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
  9. namespace Symfony\Component\HttpFoundation; class Request { public function __construct( array $query

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

    return self ::createRequestFromFactory( $_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER ); } }
  11. 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)); } }
  12. 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); } }
  13. interface DoublePassHttpMiddleware { public function __invoke( RequestInterface $request, ResponseInterface $response,

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

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

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

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

    callable $next ): ResponseInterface; } interface SinglePassHttpMiddleware { public function __invoke( RequestInterface $request, callable $next ): ResponseInterface; }
  21. –My personal soapbox for many, many years... “We either pass

    the object or we introduce factories.”
  22. interface DoublePassHttpMiddleware { public function __invoke( RequestInterface $request, ResponseInterface $response,

    callable $next ): ResponseInterface; } interface SinglePassHttpMiddleware { public function __invoke( RequestInterface $request, callable $next ): ResponseInterface; }
  23. namespace Psr\Http\Server; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; interface MiddlewareInterface { public

    function process( ServerRequestInterface $request, RequestHandlerInterface $handler ): ResponseInterface; }
  24. 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; }
  25. 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; }
  26. 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; }
  27. 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); } }
  28. 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); } }
  29. 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); } }
  30. 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); } }
  31. 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); } }
  32. 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); } }
  33. Learning the PHP-FIG HTTP Stack Beau Simensen Technical Strategy Consultant

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