wip
This commit is contained in:
Executable
+28
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
/**
|
||||
* HTTP Client interface
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface Client
|
||||
{
|
||||
public const METHOD_GET = 'GET';
|
||||
|
||||
/**
|
||||
* send a request and return the response
|
||||
*
|
||||
* @param Client::METHOD_* $method
|
||||
* @param array<string, string> $headers
|
||||
*
|
||||
* @throws ClientException if anything goes wrong requesting the data
|
||||
*/
|
||||
public function request(string $method, string $url, array $headers = []): Response;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
use SimplePie\Exception as SimplePieException;
|
||||
|
||||
/**
|
||||
* Client exception class
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ClientException extends SimplePieException
|
||||
{
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SimplePie\File;
|
||||
use SimplePie\Misc;
|
||||
use SimplePie\Registry;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* HTTP Client based on \SimplePie\File
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class FileClient implements Client
|
||||
{
|
||||
/** @var Registry */
|
||||
private $registry;
|
||||
|
||||
/** @var array{timeout?: int, redirects?: int, useragent?: string, force_fsockopen?: bool, curl_options?: array<mixed>} */
|
||||
private $options;
|
||||
|
||||
/**
|
||||
* @param array{timeout?: int, redirects?: int, useragent?: string, force_fsockopen?: bool, curl_options?: array<mixed>} $options
|
||||
*/
|
||||
public function __construct(Registry $registry, array $options = [])
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* send a request and return the response
|
||||
*
|
||||
* @param Client::METHOD_* $method
|
||||
* @param array<string, string> $headers
|
||||
*
|
||||
* @throws ClientException if anything goes wrong requesting the data
|
||||
*/
|
||||
public function request(string $method, string $url, array $headers = []): Response
|
||||
{
|
||||
// @phpstan-ignore-next-line Enforce PHPDoc type.
|
||||
if ($method !== self::METHOD_GET) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'%s(): Argument #1 ($method) only supports method "%s".',
|
||||
__METHOD__,
|
||||
self::METHOD_GET
|
||||
), 1);
|
||||
}
|
||||
|
||||
try {
|
||||
$file = $this->registry->create(File::class, [
|
||||
$url,
|
||||
$this->options['timeout'] ?? 10,
|
||||
$this->options['redirects'] ?? 5,
|
||||
$headers,
|
||||
$this->options['useragent'] ?? Misc::get_default_useragent(),
|
||||
$this->options['force_fsockopen'] ?? false,
|
||||
$this->options['curl_options'] ?? []
|
||||
]);
|
||||
} catch (Throwable $th) {
|
||||
throw new ClientException($th->getMessage(), $th->getCode(), $th);
|
||||
}
|
||||
|
||||
if ($file->error !== null && $file->get_status_code() === 0) {
|
||||
throw new ClientException($file->error);
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,15 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* SimplePie
|
||||
*
|
||||
* A PHP-Based RSS and Atom Feed Framework.
|
||||
* Takes the hard work out of managing a complete RSS/Atom solution.
|
||||
*
|
||||
* Copyright (c) 2004-2022, Ryan Parman, Sam Sneddon, Ryan McCue, and contributors
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the SimplePie Team nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without specific prior
|
||||
* written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
|
||||
* AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* @package SimplePie
|
||||
* @copyright 2004-2016 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
* @author Ryan Parman
|
||||
* @author Sam Sneddon
|
||||
* @author Ryan McCue
|
||||
* @link http://simplepie.org/ SimplePie
|
||||
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
|
||||
*/
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
/**
|
||||
* HTTP Response Parser
|
||||
*
|
||||
* @package SimplePie
|
||||
* @subpackage HTTP
|
||||
* @template Psr7Compatible of bool
|
||||
*/
|
||||
class Parser
|
||||
{
|
||||
@@ -73,10 +34,15 @@ class Parser
|
||||
*/
|
||||
public $reason = '';
|
||||
|
||||
/**
|
||||
* @var Psr7Compatible whether headers are compatible with PSR-7 format.
|
||||
*/
|
||||
private $psr7Compatible;
|
||||
|
||||
/**
|
||||
* Key/value pairs of the headers
|
||||
*
|
||||
* @var array
|
||||
* @var (Psr7Compatible is true ? array<string, non-empty-array<string>> : array<string, string>)
|
||||
*/
|
||||
public $headers = [];
|
||||
|
||||
@@ -144,14 +110,14 @@ class Parser
|
||||
protected $position = 0;
|
||||
|
||||
/**
|
||||
* Name of the hedaer currently being parsed
|
||||
* Name of the header currently being parsed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* Value of the hedaer currently being parsed
|
||||
* Value of the header currently being parsed
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
@@ -161,11 +127,13 @@ class Parser
|
||||
* Create an instance of the class with the input data
|
||||
*
|
||||
* @param string $data Input data
|
||||
* @param Psr7Compatible $psr7Compatible Whether the data types are in format compatible with PSR-7.
|
||||
*/
|
||||
public function __construct($data)
|
||||
public function __construct(string $data, bool $psr7Compatible = false)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->data_length = strlen($this->data);
|
||||
$this->psr7Compatible = $psr7Compatible;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,7 +152,8 @@ class Parser
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->http_version = '';
|
||||
// Reset the parser state.
|
||||
$this->http_version = 0.0;
|
||||
$this->status_code = 0;
|
||||
$this->reason = '';
|
||||
$this->headers = [];
|
||||
@@ -218,15 +187,16 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse the HTTP version
|
||||
* @return void
|
||||
*/
|
||||
protected function http_version()
|
||||
{
|
||||
if (strpos($this->data, "\x0A") !== false && strtoupper(substr($this->data, 0, 5)) === 'HTTP/') {
|
||||
$len = strspn($this->data, '0123456789.', 5);
|
||||
$this->http_version = substr($this->data, 5, $len);
|
||||
$http_version = substr($this->data, 5, $len);
|
||||
$this->position += 5 + $len;
|
||||
if (substr_count($this->http_version, '.') <= 1) {
|
||||
$this->http_version = (float) $this->http_version;
|
||||
if (substr_count($http_version, '.') <= 1) {
|
||||
$this->http_version = (float) $http_version;
|
||||
$this->position += strspn($this->data, "\x09\x20", $this->position);
|
||||
$this->state = self::STATE_STATUS;
|
||||
} else {
|
||||
@@ -239,6 +209,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse the status code
|
||||
* @return void
|
||||
*/
|
||||
protected function status()
|
||||
{
|
||||
@@ -253,6 +224,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse the reason phrase
|
||||
* @return void
|
||||
*/
|
||||
protected function reason()
|
||||
{
|
||||
@@ -262,8 +234,39 @@ class Parser
|
||||
$this->state = self::STATE_NEW_LINE;
|
||||
}
|
||||
|
||||
private function add_header(string $name, string $value): void
|
||||
{
|
||||
if ($this->psr7Compatible) {
|
||||
// For PHPStan: should be enforced by template parameter but PHPStan is not smart enough.
|
||||
/** @var array<string, non-empty-array<string>> */
|
||||
$headers = &$this->headers;
|
||||
$headers[$name][] = $value;
|
||||
} else {
|
||||
// For PHPStan: should be enforced by template parameter but PHPStan is not smart enough.
|
||||
/** @var array<string, string>) */
|
||||
$headers = &$this->headers;
|
||||
$headers[$name] .= ', ' . $value;
|
||||
}
|
||||
}
|
||||
|
||||
private function replace_header(string $name, string $value): void
|
||||
{
|
||||
if ($this->psr7Compatible) {
|
||||
// For PHPStan: should be enforced by template parameter but PHPStan is not smart enough.
|
||||
/** @var array<string, non-empty-array<string>> */
|
||||
$headers = &$this->headers;
|
||||
$headers[$name] = [$value];
|
||||
} else {
|
||||
// For PHPStan: should be enforced by template parameter but PHPStan is not smart enough.
|
||||
/** @var array<string, string>) */
|
||||
$headers = &$this->headers;
|
||||
$headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deal with a new line, shifting data around as needed
|
||||
* @return void
|
||||
*/
|
||||
protected function new_line()
|
||||
{
|
||||
@@ -272,9 +275,9 @@ class Parser
|
||||
$this->name = strtolower($this->name);
|
||||
// We should only use the last Content-Type header. c.f. issue #1
|
||||
if (isset($this->headers[$this->name]) && $this->name !== 'content-type') {
|
||||
$this->headers[$this->name] .= ', ' . $this->value;
|
||||
$this->add_header($this->name, $this->value);
|
||||
} else {
|
||||
$this->headers[$this->name] = $this->value;
|
||||
$this->replace_header($this->name, $this->value);
|
||||
}
|
||||
}
|
||||
$this->name = '';
|
||||
@@ -292,6 +295,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse a header name
|
||||
* @return void
|
||||
*/
|
||||
protected function name()
|
||||
{
|
||||
@@ -312,6 +316,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse LWS, replacing consecutive LWS characters with a single space
|
||||
* @return void
|
||||
*/
|
||||
protected function linear_whitespace()
|
||||
{
|
||||
@@ -328,6 +333,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* See what state to move to while within non-quoted header values
|
||||
* @return void
|
||||
*/
|
||||
protected function value()
|
||||
{
|
||||
@@ -362,6 +368,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse a header value while outside quotes
|
||||
* @return void
|
||||
*/
|
||||
protected function value_char()
|
||||
{
|
||||
@@ -373,6 +380,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* See what state to move to while within quoted header values
|
||||
* @return void
|
||||
*/
|
||||
protected function quote()
|
||||
{
|
||||
@@ -404,6 +412,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse a header value while within quotes
|
||||
* @return void
|
||||
*/
|
||||
protected function quote_char()
|
||||
{
|
||||
@@ -415,6 +424,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse an escaped character within quotes
|
||||
* @return void
|
||||
*/
|
||||
protected function quote_escaped()
|
||||
{
|
||||
@@ -425,6 +435,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parse the body
|
||||
* @return void
|
||||
*/
|
||||
protected function body()
|
||||
{
|
||||
@@ -439,6 +450,7 @@ class Parser
|
||||
|
||||
/**
|
||||
* Parsed a "Transfer-Encoding: chunked" body
|
||||
* @return void
|
||||
*/
|
||||
protected function chunked()
|
||||
{
|
||||
@@ -459,6 +471,9 @@ class Parser
|
||||
}
|
||||
|
||||
$length = hexdec(trim($matches[1]));
|
||||
// For PHPStan: this will only be float when larger than PHP_INT_MAX.
|
||||
// But even on 32-bit systems, it would mean 2GiB chunk, which sounds unlikely.
|
||||
\assert(\is_int($length), "Length needs to be shorter than PHP_INT_MAX");
|
||||
if ($length === 0) {
|
||||
// Ignore trailer headers
|
||||
$this->state = self::STATE_EMIT;
|
||||
@@ -485,11 +500,11 @@ class Parser
|
||||
* Prepare headers (take care of proxies headers)
|
||||
*
|
||||
* @param string $headers Raw headers
|
||||
* @param integer $count Redirection count. Default to 1.
|
||||
* @param non-negative-int $count Redirection count. Default to 1.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function prepareHeaders($headers, $count = 1)
|
||||
public static function prepareHeaders(string $headers, int $count = 1)
|
||||
{
|
||||
$data = explode("\r\n\r\n", $headers, $count);
|
||||
$data = array_pop($data);
|
||||
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Psr\Http\Client\ClientExceptionInterface;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\RequestFactoryInterface;
|
||||
use Psr\Http\Message\UriFactoryInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* HTTP Client based on PSR-18 and PSR-17 implementations
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Psr18Client implements Client
|
||||
{
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $httpClient;
|
||||
|
||||
/**
|
||||
* @var RequestFactoryInterface
|
||||
*/
|
||||
private $requestFactory;
|
||||
|
||||
/**
|
||||
* @var UriFactoryInterface
|
||||
*/
|
||||
private $uriFactory;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $allowedRedirects = 5;
|
||||
|
||||
public function __construct(ClientInterface $httpClient, RequestFactoryInterface $requestFactory, UriFactoryInterface $uriFactory)
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
$this->requestFactory = $requestFactory;
|
||||
$this->uriFactory = $uriFactory;
|
||||
}
|
||||
|
||||
public function getHttpClient(): ClientInterface
|
||||
{
|
||||
return $this->httpClient;
|
||||
}
|
||||
|
||||
public function getRequestFactory(): RequestFactoryInterface
|
||||
{
|
||||
return $this->requestFactory;
|
||||
}
|
||||
|
||||
public function getUriFactory(): UriFactoryInterface
|
||||
{
|
||||
return $this->uriFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* send a request and return the response
|
||||
*
|
||||
* @param Client::METHOD_* $method
|
||||
* @param string $url
|
||||
* @param array<string,string|string[]> $headers
|
||||
*
|
||||
* @throws ClientException if anything goes wrong requesting the data
|
||||
*/
|
||||
public function request(string $method, string $url, array $headers = []): Response
|
||||
{
|
||||
if ($method !== self::METHOD_GET) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'%s(): Argument #1 ($method) only supports method "%s".',
|
||||
__METHOD__,
|
||||
self::METHOD_GET
|
||||
), 1);
|
||||
}
|
||||
|
||||
if (preg_match('/^http(s)?:\/\//i', $url)) {
|
||||
return $this->requestUrl($method, $url, $headers);
|
||||
}
|
||||
|
||||
return $this->requestLocalFile($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,string|string[]> $headers
|
||||
*/
|
||||
private function requestUrl(string $method, string $url, array $headers): Response
|
||||
{
|
||||
$permanentUrl = $url;
|
||||
$requestedUrl = $url;
|
||||
$remainingRedirects = $this->allowedRedirects;
|
||||
|
||||
$request = $this->requestFactory->createRequest(
|
||||
$method,
|
||||
$this->uriFactory->createUri($requestedUrl)
|
||||
);
|
||||
|
||||
foreach ($headers as $name => $value) {
|
||||
$request = $request->withHeader($name, $value);
|
||||
}
|
||||
|
||||
do {
|
||||
$followRedirect = false;
|
||||
|
||||
try {
|
||||
$response = $this->httpClient->sendRequest($request);
|
||||
} catch (ClientExceptionInterface $th) {
|
||||
throw new ClientException($th->getMessage(), $th->getCode(), $th);
|
||||
}
|
||||
|
||||
$statusCode = $response->getStatusCode();
|
||||
|
||||
// If we have a redirect
|
||||
if (in_array($statusCode, [300, 301, 302, 303, 307]) && $response->hasHeader('Location')) {
|
||||
// Prevent infinity redirect loops
|
||||
if ($remainingRedirects <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$remainingRedirects--;
|
||||
$followRedirect = true;
|
||||
|
||||
$requestedUrl = $response->getHeaderLine('Location');
|
||||
|
||||
if ($statusCode === 301) {
|
||||
$permanentUrl = $requestedUrl;
|
||||
}
|
||||
|
||||
$request = $request->withUri($this->uriFactory->createUri($requestedUrl));
|
||||
}
|
||||
} while ($followRedirect);
|
||||
|
||||
return new Psr7Response($response, $permanentUrl, $requestedUrl);
|
||||
}
|
||||
|
||||
private function requestLocalFile(string $path): Response
|
||||
{
|
||||
if (!is_readable($path)) {
|
||||
throw new ClientException(sprintf('file "%s" is not readable', $path));
|
||||
}
|
||||
|
||||
try {
|
||||
$raw = file_get_contents($path);
|
||||
} catch (Throwable $th) {
|
||||
throw new ClientException($th->getMessage(), $th->getCode(), $th);
|
||||
}
|
||||
|
||||
if ($raw === false) {
|
||||
throw new ClientException('file_get_contents() could not read the file', 1);
|
||||
}
|
||||
|
||||
return new RawTextResponse($raw, $path);
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
* HTTP Response based on a PSR-7 response
|
||||
*
|
||||
* This interface must be interoperable with Psr\Http\Message\ResponseInterface
|
||||
* @see https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class Psr7Response implements Response
|
||||
{
|
||||
/**
|
||||
* @var ResponseInterface
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $permanent_url;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $requested_url;
|
||||
|
||||
public function __construct(ResponseInterface $response, string $permanent_url, string $requested_url)
|
||||
{
|
||||
$this->response = $response;
|
||||
$this->permanent_url = $permanent_url;
|
||||
$this->requested_url = $requested_url;
|
||||
}
|
||||
|
||||
public function get_permanent_uri(): string
|
||||
{
|
||||
return $this->permanent_url;
|
||||
}
|
||||
|
||||
public function get_final_requested_uri(): string
|
||||
{
|
||||
return $this->requested_url;
|
||||
}
|
||||
|
||||
public function get_status_code(): int
|
||||
{
|
||||
return $this->response->getStatusCode();
|
||||
}
|
||||
|
||||
public function get_headers(): array
|
||||
{
|
||||
// The filtering is probably redundant but let’s make PHPStan happy.
|
||||
return array_filter($this->response->getHeaders(), function (array $header): bool {
|
||||
return count($header) >= 1;
|
||||
});
|
||||
}
|
||||
|
||||
public function has_header(string $name): bool
|
||||
{
|
||||
return $this->response->hasHeader($name);
|
||||
}
|
||||
|
||||
public function with_header(string $name, $value)
|
||||
{
|
||||
return new self($this->response->withHeader($name, $value), $this->permanent_url, $this->requested_url);
|
||||
}
|
||||
|
||||
public function get_header(string $name): array
|
||||
{
|
||||
return $this->response->getHeader($name);
|
||||
}
|
||||
|
||||
public function get_header_line(string $name): string
|
||||
{
|
||||
return $this->response->getHeaderLine($name);
|
||||
}
|
||||
|
||||
public function get_body_content(): string
|
||||
{
|
||||
return $this->response->getBody()->__toString();
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
/**
|
||||
* HTTP Response for rax text
|
||||
*
|
||||
* This interface must be interoperable with Psr\Http\Message\ResponseInterface
|
||||
* @see https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class RawTextResponse implements Response
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $raw_text;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $permanent_url;
|
||||
|
||||
/**
|
||||
* @var array<non-empty-array<string>>
|
||||
*/
|
||||
private $headers = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $requested_url;
|
||||
|
||||
public function __construct(string $raw_text, string $filepath)
|
||||
{
|
||||
$this->raw_text = $raw_text;
|
||||
$this->permanent_url = $filepath;
|
||||
$this->requested_url = $filepath;
|
||||
}
|
||||
|
||||
public function get_permanent_uri(): string
|
||||
{
|
||||
return $this->permanent_url;
|
||||
}
|
||||
|
||||
public function get_final_requested_uri(): string
|
||||
{
|
||||
return $this->requested_url;
|
||||
}
|
||||
|
||||
public function get_status_code(): int
|
||||
{
|
||||
return 200;
|
||||
}
|
||||
|
||||
public function get_headers(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function has_header(string $name): bool
|
||||
{
|
||||
return isset($this->headers[strtolower($name)]);
|
||||
}
|
||||
|
||||
public function get_header(string $name): array
|
||||
{
|
||||
return isset($this->headers[strtolower($name)]) ? $this->headers[$name] : [];
|
||||
}
|
||||
|
||||
public function with_header(string $name, $value)
|
||||
{
|
||||
$new = clone $this;
|
||||
|
||||
$newHeader = [
|
||||
strtolower($name) => (array) $value,
|
||||
];
|
||||
$new->headers = $newHeader + $this->headers;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
public function get_header_line(string $name): string
|
||||
{
|
||||
return isset($this->headers[strtolower($name)]) ? implode(", ", $this->headers[$name]) : '';
|
||||
}
|
||||
|
||||
public function get_body_content(): string
|
||||
{
|
||||
return $this->raw_text;
|
||||
}
|
||||
}
|
||||
Executable
+171
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
// SPDX-FileCopyrightText: 2004-2023 Ryan Parman, Sam Sneddon, Ryan McCue
|
||||
// SPDX-FileCopyrightText: 2014 PHP Framework Interoperability Group
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace SimplePie\HTTP;
|
||||
|
||||
/**
|
||||
* HTTP Response interface
|
||||
*
|
||||
* This interface must be interoperable with Psr\Http\Message\ResponseInterface
|
||||
* @see https://www.php-fig.org/psr/psr-7/#33-psrhttpmessageresponseinterface
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
interface Response
|
||||
{
|
||||
/**
|
||||
* Return the string representation of the permanent URI of the requested resource
|
||||
* (the first location after a prefix of (only) permanent redirects).
|
||||
*
|
||||
* Depending on which components of the URI are present, the resulting
|
||||
* string is either a full URI or relative reference according to RFC 3986,
|
||||
* Section 4.1. The method concatenates the various components of the URI,
|
||||
* using the appropriate delimiters:
|
||||
*
|
||||
* - If a scheme is present, it MUST be suffixed by ":".
|
||||
* - If an authority is present, it MUST be prefixed by "//".
|
||||
* - The path can be concatenated without delimiters. But there are two
|
||||
* cases where the path has to be adjusted to make the URI reference
|
||||
* valid as PHP does not allow to throw an exception in __toString():
|
||||
* - If the path is rootless and an authority is present, the path MUST
|
||||
* be prefixed by "/".
|
||||
* - If the path is starting with more than one "/" and no authority is
|
||||
* present, the starting slashes MUST be reduced to one.
|
||||
* - If a query is present, it MUST be prefixed by "?".
|
||||
* - If a fragment is present, it MUST be prefixed by "#".
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc3986#section-4.1
|
||||
*/
|
||||
public function get_permanent_uri(): string;
|
||||
|
||||
/**
|
||||
* Return the string representation of the final requested URL after following all redirects.
|
||||
*
|
||||
* Depending on which components of the URI are present, the resulting
|
||||
* string is either a full URI or relative reference according to RFC 3986,
|
||||
* Section 4.1. The method concatenates the various components of the URI,
|
||||
* using the appropriate delimiters:
|
||||
*
|
||||
* - If a scheme is present, it MUST be suffixed by ":".
|
||||
* - If an authority is present, it MUST be prefixed by "//".
|
||||
* - The path can be concatenated without delimiters. But there are two
|
||||
* cases where the path has to be adjusted to make the URI reference
|
||||
* valid as PHP does not allow to throw an exception in __toString():
|
||||
* - If the path is rootless and an authority is present, the path MUST
|
||||
* be prefixed by "/".
|
||||
* - If the path is starting with more than one "/" and no authority is
|
||||
* present, the starting slashes MUST be reduced to one.
|
||||
* - If a query is present, it MUST be prefixed by "?".
|
||||
* - If a fragment is present, it MUST be prefixed by "#".
|
||||
*
|
||||
* @see http://tools.ietf.org/html/rfc3986#section-4.1
|
||||
*/
|
||||
public function get_final_requested_uri(): string;
|
||||
|
||||
/**
|
||||
* Gets the response status code.
|
||||
*
|
||||
* The status code is a 3-digit integer result code of the server's attempt
|
||||
* to understand and satisfy the request.
|
||||
*
|
||||
* @return int Status code.
|
||||
*/
|
||||
public function get_status_code(): int;
|
||||
|
||||
/**
|
||||
* Retrieves all message header values.
|
||||
*
|
||||
* The keys represent the header name as it will be sent over the wire, and
|
||||
* each value is an array of strings associated with the header.
|
||||
*
|
||||
* // Represent the headers as a string
|
||||
* foreach ($message->get_headers() as $name => $values) {
|
||||
* echo $name . ': ' . implode(', ', $values);
|
||||
* }
|
||||
*
|
||||
* // Emit headers iteratively:
|
||||
* foreach ($message->get_headers() as $name => $values) {
|
||||
* foreach ($values as $value) {
|
||||
* header(sprintf('%s: %s', $name, $value), false);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @return array<non-empty-array<string>> Returns an associative array of the message's headers.
|
||||
* Each key MUST be a header name, and each value MUST be an array of
|
||||
* strings for that header.
|
||||
*/
|
||||
public function get_headers(): array;
|
||||
|
||||
/**
|
||||
* Checks if a header exists by the given case-insensitive name.
|
||||
*
|
||||
* @param string $name Case-insensitive header field name.
|
||||
* @return bool Returns true if any header names match the given header
|
||||
* name using a case-insensitive string comparison. Returns false if
|
||||
* no matching header name is found in the message.
|
||||
*/
|
||||
public function has_header(string $name): bool;
|
||||
|
||||
/**
|
||||
* Retrieves a message header value by the given case-insensitive name.
|
||||
*
|
||||
* This method returns an array of all the header values of the given
|
||||
* case-insensitive header name.
|
||||
*
|
||||
* If the header does not appear in the message, this method MUST return an
|
||||
* empty array.
|
||||
*
|
||||
* @param string $name Case-insensitive header field name.
|
||||
* @return string[] An array of string values as provided for the given
|
||||
* header. If the header does not appear in the message, this method MUST
|
||||
* return an empty array.
|
||||
*/
|
||||
public function get_header(string $name): array;
|
||||
|
||||
/**
|
||||
* Return an instance with the provided value replacing the specified header.
|
||||
*
|
||||
* 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
|
||||
* new and/or updated header and value.
|
||||
*
|
||||
* @param string $name Case-insensitive header field name.
|
||||
* @param string|non-empty-array<string> $value Header value(s).
|
||||
* @return static
|
||||
* @throws \InvalidArgumentException for invalid header names or values.
|
||||
*/
|
||||
public function with_header(string $name, $value);
|
||||
|
||||
/**
|
||||
* Retrieves a comma-separated string of the values for a single header.
|
||||
*
|
||||
* This method returns all of the header values of the given
|
||||
* case-insensitive header name as a string concatenated together using
|
||||
* a comma.
|
||||
*
|
||||
* NOTE: Not all header values may be appropriately represented using
|
||||
* comma concatenation. For such headers, use getHeader() instead
|
||||
* and supply your own delimiter when concatenating.
|
||||
*
|
||||
* If the header does not appear in the message, this method MUST return
|
||||
* an empty string.
|
||||
*
|
||||
* @param string $name Case-insensitive header field name.
|
||||
* @return string A string of values as provided for the given header
|
||||
* concatenated together using a comma. If the header does not appear in
|
||||
* the message, this method MUST return an empty string.
|
||||
*/
|
||||
public function get_header_line(string $name): string;
|
||||
|
||||
/**
|
||||
* get the body as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_body_content(): string;
|
||||
}
|
||||
Reference in New Issue
Block a user