* * For the full copyright and license information, please view * the LICENSE file that was distributed with this source code. */ namespace CodeIgniter\HTTP; use CodeIgniter\HTTP\Exceptions\HTTPException; /** * Message Trait * Additional methods to make a PSR-7 Message class * compliant with the framework's own MessageInterface. * * @see https://github.com/php-fig/http-message/blob/master/src/MessageInterface.php */ trait MessageTrait { /** * List of all HTTP request headers. * * @var array */ protected $headers = []; /** * Holds a map of lower-case header names * and their normal-case key as it is in $headers. * Used for case-insensitive header access. * * @var array */ protected $headerMap = []; // -------------------------------------------------------------------- // Body // -------------------------------------------------------------------- /** * Sets the body of the current message. * * @param string $data * * @return $this */ public function setBody($data): self { $this->body = $data; return $this; } /** * Appends data to the body of the current message. * * @param string $data * * @return $this */ public function appendBody($data): self { $this->body .= (string) $data; return $this; } // -------------------------------------------------------------------- // Headers // -------------------------------------------------------------------- /** * Populates the $headers array with any headers the server knows about. */ public function populateHeaders(): void { $contentType = $_SERVER['CONTENT_TYPE'] ?? getenv('CONTENT_TYPE'); if (! empty($contentType)) { $this->setHeader('Content-Type', $contentType); } unset($contentType); foreach (array_keys($_SERVER) as $key) { if (sscanf($key, 'HTTP_%s', $header) === 1) { // take SOME_HEADER and turn it into Some-Header $header = str_replace('_', ' ', strtolower($header)); $header = str_replace(' ', '-', ucwords($header)); $this->setHeader($header, $_SERVER[$key]); // Add us to the header map so we can find them case-insensitively $this->headerMap[strtolower($header)] = $header; } } } /** * Returns an array containing all Headers. * * @return array An array of the Header objects */ public function headers(): array { // If no headers are defined, but the user is // requesting it, then it's likely they want // it to be populated so do that... if (empty($this->headers)) { $this->populateHeaders(); } return $this->headers; } /** * Returns a single Header object. If multiple headers with the same * name exist, then will return an array of header objects. * * @param string $name * * @return array|Header|null */ public function header($name) { $origName = $this->getHeaderName($name); return $this->headers[$origName] ?? null; } /** * Sets a header and it's value. * * @param array|string|null $value * * @return $this */ public function setHeader(string $name, $value): self { $origName = $this->getHeaderName($name); if (isset($this->headers[$origName]) && is_array($this->headers[$origName]->getValue())) { if (! is_array($value)) { $value = [$value]; } foreach ($value as $v) { $this->appendHeader($origName, $v); } } else { $this->headers[$origName] = new Header($origName, $value); $this->headerMap[strtolower($origName)] = $origName; } return $this; } /** * Removes a header from the list of headers we track. * * @return $this */ public function removeHeader(string $name): self { $origName = $this->getHeaderName($name); unset($this->headers[$origName], $this->headerMap[strtolower($name)]); return $this; } /** * Adds an additional header value to any headers that accept * multiple values (i.e. are an array or implement ArrayAccess) * * @return $this */ public function appendHeader(string $name, ?string $value): self { $origName = $this->getHeaderName($name); array_key_exists($origName, $this->headers) ? $this->headers[$origName]->appendValue($value) : $this->setHeader($name, $value); return $this; } /** * Adds an additional header value to any headers that accept * multiple values (i.e. are an array or implement ArrayAccess) * * @return $this */ public function prependHeader(string $name, string $value): self { $origName = $this->getHeaderName($name); $this->headers[$origName]->prependValue($value); return $this; } /** * Takes a header name in any case, and returns the * normal-case version of the header. */ protected function getHeaderName(string $name): string { return $this->headerMap[strtolower($name)] ?? $name; } /** * Sets the HTTP protocol version. * * @return $this * * @throws HTTPException For invalid protocols */ public function setProtocolVersion(string $version): self { if (! is_numeric($version)) { $version = substr($version, strpos($version, '/') + 1); } // Make sure that version is in the correct format $version = number_format((float) $version, 1); if (! in_array($version, $this->validProtocolVersions, true)) { throw HTTPException::forInvalidHTTPProtocol($version); } $this->protocolVersion = $version; return $this; } }