PSR-7: HTTP Message Interface
<?php // test.php?city=SG
if (isset($_POST['submit'])) {
echo 'You typed: ' . $_POST['myText'];
}
?>
City: <?php echo ($_GET['city'] ?? 'None'); ?><br />
<form method="POST">
Text: <input type="text" name="myText"><br />
<input type="submit" />
</form>
<?php // localhost/city/SG
class TestController extends AbstractActionController
{
public function indexAction()
{
$request = $this->getRequest();
$usertext = $request->isPost() ? $request->getPost()['myText'] : '';
return new ViewModel([
'city' => $this->params('city', 'None'),
'form' => new TestForm(),
'usertext' => $usertext,
]);
}
}
* 15 min
- Many ways of doing this. Code cannot be reused. Diff frameworks, diff way of doing things
- Every framework comes up with their own abstractions of HTTP request/response, new things to learn,
cannot switch easily
- Can you see a similarity? Receive request, process, return response
What do frameworks abstract?
Form post
Access $_SERVER variables
Extract request uri for routing purposes
Handle file uploads
Input and output streams
Returning of response
* End at 5 mins
- Every framework comes up with proprietary classes and ways to handle these
which cannot be reused easily outside the framework
- Some projects create adapters for common libraries
Credits: http://wiki.hashphp.org/HttpPrimer
- HTTP model
- Red for request, Green for response
- Audience: Can you abstract out the parts of a request/response?
- Request: Url, Verb, Protocol
- Response: Protocol, Status Code, Reason Phrase
Every HTTP request message has a specific form:
POST /path HTTP/1.1
Host: example.com
foo=bar&baz=bat
HTTP response messages have a similar structure:
HTTP/1.1 200 OK
Content-Type: text/plain
This is the response body
- Big overview of PSR-7
- Every "box" has an interface
- ServerRequestInterface handles PHP superglobals, routing parameters, message body
- UriInterface to model parts of an URI
- UploadedFileInterface to model file uploads
- Immutable value objects
MessageInterface
$headers = $message->getHeaders(); // [<headerName> => <array>]
$headerValues = $message->getHeader('Cookie'); // array, may be more than 1
$headerLine = $message->getHeaderLine('Accept'); // string
$body = $message->getBody(); // StreamInterface
$newMessage = $message->withHeader('Content-Type', 'application/json');
$newerMessage = $newMessage->withBody($body->getContents() . 'appended content');
- End at 10 mins
- MessageInterface is extended by RequestInterface and ResponseInterface
- Note that withHeader returns a new instance - immutability
- Objects are passed by reference in PHP hence using of value objects here
RequestInterface
$method = $request->getMethod(); // GET, POST, etc.
$uri = $request->getUri(); // UriInterface
$body = new Stream();
$body->write('{"id":1}');
$newRequest = $request
->withMethod('GET')
->withUri('http://example.com/api/v1/users')
->withHeader('Accept', 'application/json')
->withBody($body);
ResponseInterface
$status = $response->getStatusCode(); // 200, 404, 500
$reason = $response->getReasonPhrase(); // OK, Not Found, Server Error
$data = json_decode((string) $response->getBody(), true);
$data['timestamp'] = gmdate('c');
$newResponse = $response->withBody(json_encode($data));
$newerResponse = $newResponse->withStatus(200);
ServerRequestInterface
$queryParams = $request->getQueryParams();
$parsedBody = $request->getParsedBody(); // deserialized body params
$uploadedFiles = $request->getUploadedFiles(); // array of UploadedFileInterface
// Get `id` passed from route, eg. localhost/user/100.
// Application must parse route and inject into request attributes
$id = $request->getAttribute('id'); // 100
StreamInterface
interface StreamInterface
{
public function __toString(); // important
public function close();
public function detach();
public function getSize();
public function tell();
public function eof();
public function isSeekable();
public function seek($offset, $whence = SEEK_SET);
public function rewind();
public function isWritable();
public function write($string);
public function isReadable();
public function read($length);
public function getContents();
public function getMetadata($key = null);
}
File Uploads with $_FILES
[
'files' => [
0 => [
'name' => 'file0.txt',
'type' => 'text/plain',
/* etc. */
],
1 => [
'name' => 'file1.html',
'type' => 'text/html',
/* etc. */
],
],
];
- Audience: Is this correct? Anyone worked directly with $_FILES before?
- This is what we expect to see for 2 uploaded files
File Uploads with $_FILES
[ // This is what $_FILES in PHP gives us :P
'files' => [
'name' => [
0 => 'file0.txt',
1 => 'file1.html',
],
'type' => [
0 => 'text/plain',
1 => 'text/html',
],
/* etc. */
],
];
UploadedFileInterface
$uploadedFiles = $request->getUploadedFiles(); // array of UploadedFileInterface
foreach ($uploadedFiles as $file) {
echo $file->getClientFilename() . ', ' . $file->getClientMediaType();
$size = $file->getSize();
$contents = $file->getStream(); // StreamInterface
$file->moveTo($newFilePath);
}
// Printout:
// file0.txt, text/plain
// file1.txt, text/html
What's in an URI?
authority path
┌───────────────┴───────────────┐┌───┴────┐
http://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
└─┬┘ └───────┬───────┘ └────┬────┘ └┬┘ └─────────┬─────────┘ └──┬──┘
scheme user information host port query fragment
Credits:
https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Examples
Trying to compose a URI via $_SERVER
array( // not all variables shown here
'HTTP_HOST' => 'localhost',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml',
'SERVER_NAME' => 'localhost',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'REMOTE_ADDR' => '127.0.0.1',
'REQUEST_SCHEME' => 'http',
'REMOTE_PORT' => '53746',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => 'key1=value1',
'REQUEST_URI' => '/test.php?key1=value1',
'SCRIPT_NAME' => '/test.php',
'PHP_SELF' => '/test.php',
'REQUEST_TIME' => 1470751673,
)
- Every framework likely has its OWN way to compose the URI
UriInterface
$uri = $request->getUri();
$scheme = $uri->getScheme();
$userInfo = $uri->getUserInfo();
$host = $uri->getHost();
$port = $uri->getPort();
$path = $uri->getPath();
$query = $uri->getQuery();
$fragment = $uri->getFragment();
$newUri = $uri->withHost('example.com');
- Info overload?
- What is so exciting about this? Future of PHP?