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