From 70c8e7e82d8071745236e149c66238cdc923106f Mon Sep 17 00:00:00 2001
From: Thomas Flori <thflori@gmail.com>
Date: Sun, 5 Aug 2018 12:13:06 +0200
Subject: [PATCH] remove code duplicates by protected methods with overwritten
 visibility

---
 src/ChangeableMessageTrait.php | 122 ++-----------------------
 src/ClientRequest.php          |  77 ++--------------
 src/MessageTrait.php           | 158 +++++++++++++++++++++++++--------
 src/Request.php                | 102 +++++++++++++++++----
 src/Response.php               |  31 +++++--
 src/ServerResponse.php         |  25 +-----
 6 files changed, 245 insertions(+), 270 deletions(-)

diff --git a/src/ChangeableMessageTrait.php b/src/ChangeableMessageTrait.php
index 98c9463..4d14ef2 100644
--- a/src/ChangeableMessageTrait.php
+++ b/src/ChangeableMessageTrait.php
@@ -2,123 +2,13 @@
 
 namespace Tal;
 
-use Psr\Http\Message\StreamInterface;
-
 trait ChangeableMessageTrait
 {
-    use MessageTrait;
-
-    /**
-     * Sets the specified HTTP protocol version.
-     *
-     * The version string MUST contain only the HTTP version number (e.g.,
-     * "1.1", "1.0").
-     *
-     * @param string $version HTTP protocol version
-     * @return static
-     */
-    public function setProtocolVersion($version)
-    {
-        $this->protocol = $version;
-        return $this;
-    }
-
-    /**
-     * Sets the provided value replacing the specified header.
-     *
-     * While header names are case-insensitive, the casing of the header will
-     * be preserved by this function, and returned from getHeaders().
-     *
-     * @param string $header Case-insensitive header field name.
-     * @param string|string[] $value Header value(s).
-     * @return static
-     * @throws \InvalidArgumentException for invalid header names or values.
-     */
-    public function setHeader($header, $value)
-    {
-        if (!is_array($value)) {
-            $value = [$value];
-        }
-
-        $value = $this->trimHeaderValues($value);
-        $normalized = strtolower($header);
-
-        if (isset($this->headerNames[$normalized])) {
-            unset($this->headers[$this->headerNames[$normalized]]);
-        }
-        $this->headerNames[$normalized] = $header;
-        $this->headers[$header] = $value;
-
-        return $this;
-    }
-
-    /**
-     * Adds the specified header appended with the given value.
-     *
-     * Existing values for the specified header will be maintained. The new
-     * value(s) will be appended to the existing list. If the header did not
-     * exist previously, it will be added.
-     *
-     * @param string $header Case-insensitive header field name to add.
-     * @param string|string[] $value Header value(s).
-     * @return static
-     * @throws \InvalidArgumentException for invalid header names or values.
-     */
-    public function addHeader($header, $value)
-    {
-        if (!is_array($value)) {
-            $value = [$value];
-        }
-
-        $value = $this->trimHeaderValues($value);
-        $normalized = strtolower($header);
-
-        if (isset($this->headerNames[$normalized])) {
-            $header = $this->headerNames[$normalized];
-            $this->headers[$header] = array_merge($this->headers[$header], $value);
-        } else {
-            $this->headerNames[$normalized] = $header;
-            $this->headers[$header] = $value;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Deletes the specified header.
-     *
-     * Header resolution MUST be done without case-sensitivity.
-     *
-     * @param string $header Case-insensitive header field name to remove.
-     * @return static
-     */
-    public function deleteHeader($header)
-    {
-        $normalized = strtolower($header);
-
-        if (!isset($this->headerNames[$normalized])) {
-            return $this;
-        }
-
-        $header = $this->headerNames[$normalized];
-
-        unset($this->headers[$header], $this->headerNames[$normalized]);
-
-        return $this;
-    }
-
-    /**
-     * Sets the specified message body.
-     *
-     * The body MUST be a StreamInterface object.
-     *
-     * @param StreamInterface $body Body.
-     * @return static
-     * @throws \InvalidArgumentException When the body is not valid.
-     */
-    public function setBody(StreamInterface $body)
-    {
-        $this->stream = $body;
-        return $this;
+    use MessageTrait {
+        setProtocolVersion as public;
+        setHeader as public;
+        addHeader as public;
+        deleteHeader as public;
+        setBody as public;
     }
 }
diff --git a/src/ClientRequest.php b/src/ClientRequest.php
index 18d2c5c..309c21d 100644
--- a/src/ClientRequest.php
+++ b/src/ClientRequest.php
@@ -2,7 +2,6 @@
 
 namespace Tal;
 
-use InvalidArgumentException;
 use Psr\Http\Message\UriInterface;
 use Tal\Psr7Extended\ClientRequestInterface;
 
@@ -10,85 +9,21 @@ class ClientRequest extends Request implements ClientRequestInterface
 {
     use ChangeableMessageTrait;
 
-    /**
-     * Sets the specific request-target.
-     *
-     * If the request needs a non-origin-form request-target — e.g., for
-     * specifying an absolute-form, authority-form, or asterisk-form —
-     * this method may be used to create an instance with the specified
-     * request-target, verbatim.
-     *
-     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
-     *     request-target forms allowed in request messages)
-     * @param mixed $requestTarget
-     * @return static
-     */
+    /** {@inheritDoc} */
     public function setRequestTarget($requestTarget)
     {
-        if (preg_match('#\s#', $requestTarget)) {
-            throw new InvalidArgumentException(
-                'Invalid request target provided; cannot contain whitespace'
-            );
-        }
-
-        $this->requestTarget = $requestTarget;
-        return $this;
+        return parent::setRequestTarget($requestTarget);
     }
 
-    /**
-     * Sets the provided HTTP method.
-     *
-     * While HTTP method names are typically all uppercase characters, HTTP
-     * method names are case-sensitive and thus implementations SHOULD NOT
-     * modify the given string.
-     *
-     * This method MUST be implemented in such a way as to retain the
-     * immutability of the message, and MUST return an instance that has the
-     * changed request method.
-     *
-     * @param string $method Case-sensitive method.
-     * @return static
-     * @throws \InvalidArgumentException for invalid HTTP methods.
-     */
+    /** {@inheritDoc} */
     public function setMethod($method)
     {
-        $this->method = strtoupper($method);
-        return $this;
+        return parent::setMethod($method);
     }
 
-    /**
-     * Sets the provided URI.
-     *
-     * This method MUST update the Host header of the returned request by
-     * default if the URI contains a host component. If the URI does not
-     * contain a host component, any pre-existing Host header MUST be carried
-     * over to the returned request.
-     *
-     * You can opt-in to preserving the original state of the Host header by
-     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
-     * `true`, this method interacts with the Host header in the following ways:
-     *
-     * - If the Host header is missing or empty, and the new URI contains
-     *   a host component, this method MUST update the Host header in the returned
-     *   request.
-     * - If the Host header is missing or empty, and the new URI does not contain a
-     *   host component, this method MUST NOT update the Host header in the returned
-     *   request.
-     * - If a Host header is present and non-empty, this method MUST NOT update
-     *   the Host header in the returned request.
-     *
-     * @link http://tools.ietf.org/html/rfc3986#section-4.3
-     * @param UriInterface $uri New request URI to use.
-     * @param bool $preserveHost Preserve the original state of the Host header.
-     * @return static
-     */
+    /** {@inheritDoc} */
     public function setUri(UriInterface $uri, $preserveHost = false)
     {
-        $this->uri = $uri;
-
-        if (!$preserveHost) {
-            $this->updateHostFromUri();
-        }
-        return $this;
+        return parent::setUri($uri, $preserveHost);
     }
 }
diff --git a/src/MessageTrait.php b/src/MessageTrait.php
index 6f53690..041dbb4 100644
--- a/src/MessageTrait.php
+++ b/src/MessageTrait.php
@@ -34,8 +34,7 @@ trait MessageTrait
         }
 
         $new = clone $this;
-        $new->protocol = $version;
-        return $new;
+        return $new->setProtocolVersion($version);
     }
 
     public function getHeaders()
@@ -68,42 +67,14 @@ trait MessageTrait
 
     public function withHeader($header, $value)
     {
-        if (!is_array($value)) {
-            $value = [$value];
-        }
-
-        $value = $this->trimHeaderValues($value);
-        $normalized = strtolower($header);
-
         $new = clone $this;
-        if (isset($new->headerNames[$normalized])) {
-            unset($new->headers[$new->headerNames[$normalized]]);
-        }
-        $new->headerNames[$normalized] = $header;
-        $new->headers[$header] = $value;
-
-        return $new;
+        return $new->setHeader($header, $value);
     }
 
     public function withAddedHeader($header, $value)
     {
-        if (!is_array($value)) {
-            $value = [$value];
-        }
-
-        $value = $this->trimHeaderValues($value);
-        $normalized = strtolower($header);
-
         $new = clone $this;
-        if (isset($new->headerNames[$normalized])) {
-            $header = $this->headerNames[$normalized];
-            $new->headers[$header] = array_merge($this->headers[$header], $value);
-        } else {
-            $new->headerNames[$normalized] = $header;
-            $new->headers[$header] = $value;
-        }
-
-        return $new;
+        return $new->addHeader($header, $value);
     }
 
     public function withoutHeader($header)
@@ -114,12 +85,8 @@ trait MessageTrait
             return $this;
         }
 
-        $header = $this->headerNames[$normalized];
-
         $new = clone $this;
-        unset($new->headers[$header], $new->headerNames[$normalized]);
-
-        return $new;
+        return $new->deleteHeader($header);
     }
 
     public function getBody()
@@ -138,8 +105,7 @@ trait MessageTrait
         }
 
         $new = clone $this;
-        $new->stream = $body;
-        return $new;
+        return $new->setBody($body);
     }
 
     protected function setHeaders(array $headers)
@@ -182,4 +148,118 @@ trait MessageTrait
             return trim($value, " \t");
         }, $values);
     }
+
+    /**
+     * Sets the specified HTTP protocol version.
+     *
+     * The version string MUST contain only the HTTP version number (e.g.,
+     * "1.1", "1.0").
+     *
+     * @param string $version HTTP protocol version
+     * @return static
+     */
+    protected function setProtocolVersion($version)
+    {
+        $this->protocol = $version;
+        return $this;
+    }
+
+    /**
+     * Sets the provided value replacing the specified header.
+     *
+     * While header names are case-insensitive, the casing of the header will
+     * be preserved by this function, and returned from getHeaders().
+     *
+     * @param string $header Case-insensitive header field name.
+     * @param string|string[] $value Header value(s).
+     * @return static
+     * @throws \InvalidArgumentException for invalid header names or values.
+     */
+    protected function setHeader($header, $value)
+    {
+        if (!is_array($value)) {
+            $value = [$value];
+        }
+
+        $value = $this->trimHeaderValues($value);
+        $normalized = strtolower($header);
+
+        if (isset($this->headerNames[$normalized])) {
+            unset($this->headers[$this->headerNames[$normalized]]);
+        }
+        $this->headerNames[$normalized] = $header;
+        $this->headers[$header] = $value;
+
+        return $this;
+    }
+
+    /**
+     * Adds the specified header appended with the given value.
+     *
+     * Existing values for the specified header will be maintained. The new
+     * value(s) will be appended to the existing list. If the header did not
+     * exist previously, it will be added.
+     *
+     * @param string $header Case-insensitive header field name to add.
+     * @param string|string[] $value Header value(s).
+     * @return static
+     * @throws \InvalidArgumentException for invalid header names or values.
+     */
+    protected function addHeader($header, $value)
+    {
+        if (!is_array($value)) {
+            $value = [$value];
+        }
+
+        $value = $this->trimHeaderValues($value);
+        $normalized = strtolower($header);
+
+        if (isset($this->headerNames[$normalized])) {
+            $header = $this->headerNames[$normalized];
+            $this->headers[$header] = array_merge($this->headers[$header], $value);
+        } else {
+            $this->headerNames[$normalized] = $header;
+            $this->headers[$header] = $value;
+        }
+
+        return $this;
+    }
+
+    /**
+     * Deletes the specified header.
+     *
+     * Header resolution MUST be done without case-sensitivity.
+     *
+     * @param string $header Case-insensitive header field name to remove.
+     * @return static
+     */
+    protected function deleteHeader($header)
+    {
+        $normalized = strtolower($header);
+
+        if (!isset($this->headerNames[$normalized])) {
+            return $this;
+        }
+
+        $header = $this->headerNames[$normalized];
+
+        unset($this->headers[$header], $this->headerNames[$normalized]);
+
+        return $this;
+    }
+
+    /**
+     * Sets the specified message body.
+     *
+     * The body MUST be a StreamInterface object.
+     *
+     * @param StreamInterface $body Body.
+     * @return static
+     * @throws \InvalidArgumentException When the body is not valid.
+     */
+    protected function setBody(StreamInterface $body)
+    {
+        $this->stream = $body;
+        return $this;
+    }
 }
diff --git a/src/Request.php b/src/Request.php
index 1890882..a2ba489 100644
--- a/src/Request.php
+++ b/src/Request.php
@@ -76,15 +76,8 @@ abstract class Request implements RequestInterface
 
     public function withRequestTarget($requestTarget)
     {
-        if (preg_match('#\s#', $requestTarget)) {
-            throw new InvalidArgumentException(
-                'Invalid request target provided; cannot contain whitespace'
-            );
-        }
-
         $new = clone $this;
-        $new->requestTarget = $requestTarget;
-        return $new;
+        return $new->setRequestTarget($requestTarget);
     }
 
     public function getMethod()
@@ -95,8 +88,7 @@ abstract class Request implements RequestInterface
     public function withMethod($method)
     {
         $new = clone $this;
-        $new->method = strtoupper($method);
-        return $new;
+        return $new->setMethod($method);
     }
 
     public function getUri()
@@ -111,13 +103,7 @@ abstract class Request implements RequestInterface
         }
 
         $new = clone $this;
-        $new->uri = $uri;
-
-        if (!$preserveHost) {
-            $new->updateHostFromUri();
-        }
-
-        return $new;
+        return $new->setUri($uri, $preserveHost);
     }
 
     protected function updateHostFromUri()
@@ -142,4 +128,86 @@ abstract class Request implements RequestInterface
         // See: http://tools.ietf.org/html/rfc7230#section-5.4
         $this->headers = [$header => [$host]] + $this->headers;
     }
+
+    /**
+     * Sets the specific request-target.
+     *
+     * If the request needs a non-origin-form request-target — e.g., for
+     * specifying an absolute-form, authority-form, or asterisk-form —
+     * this method may be used to create an instance with the specified
+     * request-target, verbatim.
+     *
+     * @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
+     *     request-target forms allowed in request messages)
+     * @param mixed $requestTarget
+     * @return static
+     */
+    protected function setRequestTarget($requestTarget)
+    {
+        if (preg_match('#\s#', $requestTarget)) {
+            throw new InvalidArgumentException(
+                'Invalid request target provided; cannot contain whitespace'
+            );
+        }
+
+        $this->requestTarget = $requestTarget;
+        return $this;
+    }
+
+    /**
+     * Sets the provided HTTP method.
+     *
+     * While HTTP method names are typically all uppercase characters, HTTP
+     * method names are case-sensitive and thus implementations SHOULD NOT
+     * modify the given string.
+     *
+     * This method MUST be implemented in such a way as to retain the
+     * immutability of the message, and MUST return an instance that has the
+     * changed request method.
+     *
+     * @param string $method Case-sensitive method.
+     * @return static
+     * @throws \InvalidArgumentException for invalid HTTP methods.
+     */
+    protected function setMethod($method)
+    {
+        $this->method = strtoupper($method);
+        return $this;
+    }
+
+    /**
+     * Sets the provided URI.
+     *
+     * This method MUST update the Host header of the returned request by
+     * default if the URI contains a host component. If the URI does not
+     * contain a host component, any pre-existing Host header MUST be carried
+     * over to the returned request.
+     *
+     * You can opt-in to preserving the original state of the Host header by
+     * setting `$preserveHost` to `true`. When `$preserveHost` is set to
+     * `true`, this method interacts with the Host header in the following ways:
+     *
+     * - If the Host header is missing or empty, and the new URI contains
+     *   a host component, this method MUST update the Host header in the returned
+     *   request.
+     * - If the Host header is missing or empty, and the new URI does not contain a
+     *   host component, this method MUST NOT update the Host header in the returned
+     *   request.
+     * - If a Host header is present and non-empty, this method MUST NOT update
+     *   the Host header in the returned request.
+     *
+     * @link http://tools.ietf.org/html/rfc3986#section-4.3
+     * @param UriInterface $uri New request URI to use.
+     * @param bool $preserveHost Preserve the original state of the Host header.
+     * @return static
+     */
+    protected function setUri(UriInterface $uri, $preserveHost = false)
+    {
+        $this->uri = $uri;
+
+        if (!$preserveHost) {
+            $this->updateHostFromUri();
+        }
+        return $this;
+    }
 }
diff --git a/src/Response.php b/src/Response.php
index 30774b1..20b8ff4 100644
--- a/src/Response.php
+++ b/src/Response.php
@@ -124,11 +124,32 @@ abstract class Response implements ResponseInterface
     public function withStatus($code, $reasonPhrase = '')
     {
         $new = clone $this;
-        $new->statusCode = (int) $code;
-        if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
-            $reasonPhrase = self::$phrases[$new->statusCode];
+        return $new->setStatus($code, $reasonPhrase);
+    }
+
+    /**
+     * Sets the specified status code and, optionally, reason phrase.
+     *
+     * If no reason phrase is specified, implementations MAY choose to default
+     * to the RFC 7231 or IANA recommended reason phrase for the response's
+     * status code.
+     *
+     * @link http://tools.ietf.org/html/rfc7231#section-6
+     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
+     * @param int $code The 3-digit integer result code to set.
+     * @param string $reasonPhrase The reason phrase to use with the
+     *     provided status code; if none is provided, implementations MAY
+     *     use the defaults as suggested in the HTTP specification.
+     * @return static
+     * @throws \InvalidArgumentException For invalid status code arguments.
+     */
+    protected function setStatus($code, $reasonPhrase = '')
+    {
+        $this->statusCode = (int) $code;
+        if ($reasonPhrase == '' && isset(static::$phrases[$this->statusCode])) {
+            $reasonPhrase = static::$phrases[$this->statusCode];
         }
-        $new->reasonPhrase = $reasonPhrase;
-        return $new;
+        $this->reasonPhrase = $reasonPhrase;
+        return $this;
     }
 }
diff --git a/src/ServerResponse.php b/src/ServerResponse.php
index 594e19d..7df088c 100644
--- a/src/ServerResponse.php
+++ b/src/ServerResponse.php
@@ -2,36 +2,17 @@
 
 namespace Tal;
 
+use Psr\Http\Message\StreamInterface;
 use Tal\Psr7Extended\ServerResponseInterface;
 
 class ServerResponse extends Response implements ServerResponseInterface
 {
     use ChangeableMessageTrait;
 
-    /**
-     * Sets the specified status code and, optionally, reason phrase.
-     *
-     * If no reason phrase is specified, implementations MAY choose to default
-     * to the RFC 7231 or IANA recommended reason phrase for the response's
-     * status code.
-     *
-     * @link http://tools.ietf.org/html/rfc7231#section-6
-     * @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
-     * @param int $code The 3-digit integer result code to set.
-     * @param string $reasonPhrase The reason phrase to use with the
-     *     provided status code; if none is provided, implementations MAY
-     *     use the defaults as suggested in the HTTP specification.
-     * @return static
-     * @throws \InvalidArgumentException For invalid status code arguments.
-     */
+    /** {@inheritDoc} */
     public function setStatus($code, $reasonPhrase = '')
     {
-        $this->statusCode = (int) $code;
-        if ($reasonPhrase == '' && isset(static::$phrases[$this->statusCode])) {
-            $reasonPhrase = static::$phrases[$this->statusCode];
-        }
-        $this->reasonPhrase = $reasonPhrase;
-        return $this;
+        return parent::setStatus($code, $reasonPhrase);
     }
 
     /**
-- 
GitLab