Add PSR HTTP Message Interfaces and Dependencies
- Implemented StreamInterface, UploadedFileInterface, and UriInterface as per PSR standards. - Added getallheaders function to retrieve HTTP headers in a compatible manner. - Included LICENSE files for ralouphie/getallheaders and symfony/deprecation-contracts. - Introduced function for triggering deprecation notices in Symfony.
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface ATFPP\AI_Translate\Services\HTTP\Contracts\Stream_Request_Handler
|
||||
*
|
||||
* @since 0.6.0
|
||||
* @package ai-services
|
||||
*/
|
||||
|
||||
namespace ATFPP\AI_Translate\Services\HTTP\Contracts;
|
||||
|
||||
use ATFPP\AI_Translate\Services\HTTP\Stream_Response;
|
||||
use Felix_Arntz\ATFPP\WP_OOP_Plugin_Lib\HTTP\Contracts\Request;
|
||||
use Felix_Arntz\ATFPP\WP_OOP_Plugin_Lib\HTTP\Exception\Request_Exception;
|
||||
|
||||
/**
|
||||
* Interface for a request handler that can stream responses.
|
||||
*
|
||||
* @since 0.6.0
|
||||
*/
|
||||
interface Stream_Request_Handler {
|
||||
|
||||
/**
|
||||
* Sends an HTTP request and streams the response.
|
||||
*
|
||||
* @since 0.6.0
|
||||
*
|
||||
* @param Request $request The request to send.
|
||||
* @return Stream_Response The stream response.
|
||||
*
|
||||
* @throws Request_Exception Thrown if the request fails.
|
||||
*/
|
||||
public function request_stream( Request $request ): Stream_Response;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* Interface ATFPP\AI_Translate\Services\HTTP\Contracts\With_Stream
|
||||
*
|
||||
* @since 0.3.0
|
||||
* @package ai-services
|
||||
*/
|
||||
|
||||
namespace ATFPP\AI_Translate\Services\HTTP\Contracts;
|
||||
|
||||
use Generator;
|
||||
|
||||
/**
|
||||
* Interface for a class that contains a readable stream.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*/
|
||||
interface With_Stream {
|
||||
|
||||
/**
|
||||
* Returns a generator that reads individual chunks of decoded JSON data from the streamed response body.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @return Generator The generator for the response stream.
|
||||
*/
|
||||
public function read_stream(): Generator;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/**
|
||||
* Class ATFPP\AI_Translate\Services\HTTP\HTTP_With_Streams
|
||||
*
|
||||
* @since 0.3.0
|
||||
* @package ai-services
|
||||
*/
|
||||
|
||||
namespace ATFPP\AI_Translate\Services\HTTP;
|
||||
|
||||
use ATFPP\AI_Translate\Services\HTTP\Contracts\Stream_Request_Handler;
|
||||
use Felix_Arntz\ATFPP\WP_OOP_Plugin_Lib\HTTP\Contracts\Request;
|
||||
use Felix_Arntz\ATFPP\WP_OOP_Plugin_Lib\HTTP\Exception\Request_Exception;
|
||||
use Felix_Arntz\ATFPP\WP_OOP_Plugin_Lib\HTTP\HTTP;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
|
||||
/**
|
||||
* Extended HTTP class with support for streaming responses.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*/
|
||||
final class HTTP_With_Streams extends HTTP implements Stream_Request_Handler {
|
||||
|
||||
/**
|
||||
* Guzzle client instance.
|
||||
*
|
||||
* Used for streaming requests, as WordPress Core's Requests API does not support this.
|
||||
*
|
||||
* @since 0.3.0
|
||||
* @var Client
|
||||
*/
|
||||
private $guzzle;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @param array<string, mixed> $default_options Optional. Default options to use for all requests. Default empty
|
||||
* array.
|
||||
*/
|
||||
public function __construct( array $default_options = array() ) {
|
||||
parent::__construct( $default_options );
|
||||
|
||||
$this->guzzle = new Client();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP request and streams the response.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @param Request $request The request to send.
|
||||
* @return Stream_Response The stream response.
|
||||
*
|
||||
* @throws Request_Exception Thrown if the request fails.
|
||||
*/
|
||||
public function request_stream( Request $request ): Stream_Response {
|
||||
$request_args = $this->build_request_args( $request );
|
||||
|
||||
$request_options = array(
|
||||
'allow_redirects' => $request_args['options']['redirection'] > 0 ? array( 'max' => $request_args['options']['redirection'] ) : false,
|
||||
'timeout' => (float) $request_args['options']['timeout'],
|
||||
'stream' => true,
|
||||
);
|
||||
if ( isset( $request_args['data'] ) ) {
|
||||
if ( in_array( $request_args['type'], array( Request::HEAD, Request::GET, Request::DELETE ), true ) ) {
|
||||
$request_options['query'] = $request_args['data'];
|
||||
} else {
|
||||
if ( ! is_string( $request_args['data'] ) ) {
|
||||
$request_args['data'] = http_build_query( $request_args['data'], '', '&' );
|
||||
}
|
||||
$request_options['body'] = $request_args['data'];
|
||||
}
|
||||
}
|
||||
if ( isset( $request_args['headers'] ) ) {
|
||||
if ( ! isset( $request_args['headers']['User-Agent'] ) ) {
|
||||
$request_args['headers']['User-Agent'] = $request_args['options']['user-agent'];
|
||||
}
|
||||
} else {
|
||||
$request_args['headers'] = array(
|
||||
'User-Agent' => $request_args['options']['user-agent'],
|
||||
);
|
||||
}
|
||||
$request_options['headers'] = $request_args['headers'];
|
||||
|
||||
try {
|
||||
$response = $this->guzzle->request(
|
||||
$request_args['type'],
|
||||
$request_args['url'],
|
||||
$request_options
|
||||
);
|
||||
} catch ( ClientException $e ) {
|
||||
throw new Request_Exception(
|
||||
htmlspecialchars( $e->getMessage() ) // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||
);
|
||||
}
|
||||
|
||||
$headers = $this->sanitize_headers( $response->getHeaders() );
|
||||
|
||||
return new Stream_Response( $response->getStatusCode(), $response->getBody(), $headers );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Class ATFPP\AI_Translate\Services\HTTP\Stream_Response
|
||||
*
|
||||
* @since 0.3.0
|
||||
* @package ai-services
|
||||
*/
|
||||
|
||||
namespace ATFPP\AI_Translate\Services\HTTP;
|
||||
|
||||
use ATFPP\AI_Translate\Services\HTTP\Contracts\With_Stream;
|
||||
use Felix_Arntz\ATFPP\WP_OOP_Plugin_Lib\HTTP\Generic_Response;
|
||||
use ATFPP\AI_Translate_Dependencies\Psr\Http\Message\StreamInterface;
|
||||
use Generator;
|
||||
use InvalidArgumentException;
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Class for a HTTP response that uses streaming.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @implements IteratorAggregate<Generator>
|
||||
*/
|
||||
class Stream_Response extends Generic_Response implements With_Stream, IteratorAggregate {
|
||||
|
||||
/**
|
||||
* The stream to read from.
|
||||
*
|
||||
* @since 0.3.0
|
||||
* @var StreamInterface
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @param int $status The HTTP status code received with the response.
|
||||
* @param StreamInterface $stream The response body stream to read from.
|
||||
* @param array<string, string> $headers The headers received with the response.
|
||||
*
|
||||
* @throws InvalidArgumentException Thrown if the $stream parameter has an invalid type.
|
||||
*/
|
||||
public function __construct( int $status, StreamInterface $stream, array $headers ) {
|
||||
parent::__construct( $status, '', $headers );
|
||||
|
||||
$this->stream = $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a generator that reads individual chunks of decoded JSON data from the streamed response body.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @return Generator The generator for the response stream.
|
||||
*/
|
||||
public function read_stream(): Generator {
|
||||
while ( ! $this->stream->eof() ) {
|
||||
$line = $this->read_line( $this->stream );
|
||||
$data = json_decode( $line, true );
|
||||
if ( ! $data ) {
|
||||
continue;
|
||||
}
|
||||
yield $data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves an iterator reading individual chunks of decoded JSON data from the streamed response body.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @return Generator The iterator for the response stream.
|
||||
*/
|
||||
public function getIterator(): Generator {
|
||||
return $this->read_stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a line from the stream.
|
||||
*
|
||||
* @since 0.3.0
|
||||
*
|
||||
* @param StreamInterface $stream The stream to read from.
|
||||
* @return string The line read from the stream.
|
||||
*/
|
||||
private function read_line( $stream ): string {
|
||||
$buffer = '';
|
||||
|
||||
while ( ! $stream->eof() ) {
|
||||
$buffer .= $stream->read( 1 );
|
||||
|
||||
if ( strlen( $buffer ) === 1 && '{' !== $buffer ) {
|
||||
$buffer = '';
|
||||
}
|
||||
|
||||
if ( json_decode( $buffer ) !== null ) {
|
||||
return $buffer;
|
||||
}
|
||||
}
|
||||
|
||||
return rtrim( $buffer, ']' );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user