--- /dev/null
+<?php
+/**
+ * Zend Framework (http://framework.zend.com/)
+ *
+ * @see http://github.com/zendframework/zend-diactoros for the canonical source repository
+ * @copyright Copyright (c) 2015-2016 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
+ */
+
+namespace Zend\Diactoros;
+
+use InvalidArgumentException;
+use Psr\Http\Message\ServerRequestInterface;
+use Psr\Http\Message\StreamInterface;
+use Psr\Http\Message\UploadedFileInterface;
+use Psr\Http\Message\UriInterface;
+
+/**
+ * Server-side HTTP request
+ *
+ * Extends the Request definition to add methods for accessing incoming data,
+ * specifically server parameters, cookies, matched path parameters, query
+ * string arguments, body parameters, and upload file information.
+ *
+ * "Attributes" are discovered via decomposing the request (and usually
+ * specifically the URI path), and typically will be injected by the application.
+ *
+ * Requests are considered immutable; all methods that might change state are
+ * implemented such that they retain the internal state of the current
+ * message and return a new instance that contains the changed state.
+ */
+class ServerRequest implements ServerRequestInterface
+{
+ use RequestTrait;
+
+ /**
+ * @var array
+ */
+ private $attributes = [];
+
+ /**
+ * @var array
+ */
+ private $cookieParams = [];
+
+ /**
+ * @var null|array|object
+ */
+ private $parsedBody;
+
+ /**
+ * @var array
+ */
+ private $queryParams = [];
+
+ /**
+ * @var array
+ */
+ private $serverParams;
+
+ /**
+ * @var array
+ */
+ private $uploadedFiles;
+
+ /**
+ * @param array $serverParams Server parameters, typically from $_SERVER
+ * @param array $uploadedFiles Upload file information, a tree of UploadedFiles
+ * @param null|string|UriInterface $uri URI for the request, if any.
+ * @param null|string $method HTTP method for the request, if any.
+ * @param string|resource|StreamInterface $body Message body, if any.
+ * @param array $headers Headers for the message, if any.
+ * @param array $cookies Cookies for the message, if any.
+ * @param array $queryParams Query params for the message, if any.
+ * @param null|array|object $parsedBody The deserialized body parameters, if any.
+ * @param string $protocol HTTP protocol version.
+ * @throws InvalidArgumentException for any invalid value.
+ */
+ public function __construct(
+ array $serverParams = [],
+ array $uploadedFiles = [],
+ $uri = null,
+ $method = null,
+ $body = 'php://input',
+ array $headers = [],
+ array $cookies = [],
+ array $queryParams = [],
+ $parsedBody = null,
+ $protocol = '1.1'
+ ) {
+ $this->validateUploadedFiles($uploadedFiles);
+
+ if ($body === 'php://input') {
+ $body = new PhpInputStream();
+ }
+
+ $this->initialize($uri, $method, $body, $headers);
+ $this->serverParams = $serverParams;
+ $this->uploadedFiles = $uploadedFiles;
+ $this->cookieParams = $cookies;
+ $this->queryParams = $queryParams;
+ $this->parsedBody = $parsedBody;
+ $this->protocol = $protocol;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getServerParams()
+ {
+ return $this->serverParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getUploadedFiles()
+ {
+ return $this->uploadedFiles;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withUploadedFiles(array $uploadedFiles)
+ {
+ $this->validateUploadedFiles($uploadedFiles);
+ $new = clone $this;
+ $new->uploadedFiles = $uploadedFiles;
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCookieParams()
+ {
+ return $this->cookieParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withCookieParams(array $cookies)
+ {
+ $new = clone $this;
+ $new->cookieParams = $cookies;
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getQueryParams()
+ {
+ return $this->queryParams;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withQueryParams(array $query)
+ {
+ $new = clone $this;
+ $new->queryParams = $query;
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getParsedBody()
+ {
+ return $this->parsedBody;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withParsedBody($data)
+ {
+ $new = clone $this;
+ $new->parsedBody = $data;
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAttribute($attribute, $default = null)
+ {
+ if (! array_key_exists($attribute, $this->attributes)) {
+ return $default;
+ }
+
+ return $this->attributes[$attribute];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withAttribute($attribute, $value)
+ {
+ $new = clone $this;
+ $new->attributes[$attribute] = $value;
+ return $new;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function withoutAttribute($attribute)
+ {
+ $new = clone $this;
+ unset($new->attributes[$attribute]);
+ return $new;
+ }
+
+ /**
+ * Proxy to receive the request method.
+ *
+ * This overrides the parent functionality to ensure the method is never
+ * empty; if no method is present, it returns 'GET'.
+ *
+ * @return string
+ */
+ public function getMethod()
+ {
+ if (empty($this->method)) {
+ return 'GET';
+ }
+ return $this->method;
+ }
+
+ /**
+ * Set the request method.
+ *
+ * Unlike the regular Request implementation, the server-side
+ * normalizes the method to uppercase to ensure consistency
+ * and make checking the method simpler.
+ *
+ * This methods returns a new instance.
+ *
+ * @param string $method
+ * @return self
+ */
+ public function withMethod($method)
+ {
+ $this->validateMethod($method);
+ $new = clone $this;
+ $new->method = $method;
+ return $new;
+ }
+
+ /**
+ * Recursively validate the structure in an uploaded files array.
+ *
+ * @param array $uploadedFiles
+ * @throws InvalidArgumentException if any leaf is not an UploadedFileInterface instance.
+ */
+ private function validateUploadedFiles(array $uploadedFiles)
+ {
+ foreach ($uploadedFiles as $file) {
+ if (is_array($file)) {
+ $this->validateUploadedFiles($file);
+ continue;
+ }
+
+ if (! $file instanceof UploadedFileInterface) {
+ throw new InvalidArgumentException('Invalid leaf in uploaded files structure');
+ }
+ }
+ }
+}