write($message); return self::fromStream($stream); } /** * Deserialize a request stream to a request instance. * * @param StreamInterface $stream * @return Request * @throws UnexpectedValueException when errors occur parsing the message. */ public static function fromStream(StreamInterface $stream) { if (! $stream->isReadable() || ! $stream->isSeekable()) { throw new InvalidArgumentException('Message stream must be both readable and seekable'); } $stream->rewind(); list($method, $requestTarget, $version) = self::getRequestLine($stream); $uri = self::createUriFromRequestTarget($requestTarget); list($headers, $body) = self::splitStream($stream); return (new Request($uri, $method, $body, $headers)) ->withProtocolVersion($version) ->withRequestTarget($requestTarget); } /** * Serialize a request message to a string. * * @param RequestInterface $request * @return string */ public static function toString(RequestInterface $request) { $httpMethod = $request->getMethod(); if (empty($httpMethod)) { throw new UnexpectedValueException('Object can not be serialized because HTTP method is empty'); } $headers = self::serializeHeaders($request->getHeaders()); $body = (string) $request->getBody(); $format = '%s %s HTTP/%s%s%s'; if (! empty($headers)) { $headers = "\r\n" . $headers; } if (! empty($body)) { $headers .= "\r\n\r\n"; } return sprintf( $format, $httpMethod, $request->getRequestTarget(), $request->getProtocolVersion(), $headers, $body ); } /** * Retrieve the components of the request line. * * Retrieves the first line of the stream and parses it, raising an * exception if it does not follow specifications; if valid, returns a list * with the method, target, and version, in that order. * * @param StreamInterface $stream * @return array */ private static function getRequestLine(StreamInterface $stream) { $requestLine = self::getLine($stream); if (! preg_match( '#^(?P[!\#$%&\'*+.^_`|~a-zA-Z0-9-]+) (?P[^\s]+) HTTP/(?P[1-9]\d*\.\d+)$#', $requestLine, $matches )) { throw new UnexpectedValueException('Invalid request line detected'); } return [$matches['method'], $matches['target'], $matches['version']]; } /** * Create and return a Uri instance based on the provided request target. * * If the request target is of authority or asterisk form, an empty Uri * instance is returned; otherwise, the value is used to create and return * a new Uri instance. * * @param string $requestTarget * @return Uri */ private static function createUriFromRequestTarget($requestTarget) { if (preg_match('#^https?://#', $requestTarget)) { return new Uri($requestTarget); } if (preg_match('#^(\*|[^/])#', $requestTarget)) { return new Uri(); } return new Uri($requestTarget); } }