Phase 5: Content and SEO - Yoast SEO, Schema.org markup, Open Graph, favicon support, XML sitemap
This commit is contained in:
Executable
+37
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Application;
|
||||
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Bad_Request_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Forbidden_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Internal_Server_Error_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Not_Found_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Payment_Required_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Request_Timeout_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Service_Unavailable_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Too_Many_Requests_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Unauthorized_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Request;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Response;
|
||||
|
||||
interface Request_Handler_Interface {
|
||||
|
||||
/**
|
||||
* Executes the request to the API.
|
||||
*
|
||||
* @param Request $request The request to execute.
|
||||
*
|
||||
* @return Response The response from the API.
|
||||
*
|
||||
* @throws Bad_Request_Exception When the request fails for any other reason.
|
||||
* @throws Forbidden_Exception When the response code is 403.
|
||||
* @throws Internal_Server_Error_Exception When the response code is 500.
|
||||
* @throws Not_Found_Exception When the response code is 404.
|
||||
* @throws Payment_Required_Exception When the response code is 402.
|
||||
* @throws Request_Timeout_Exception When the response code is 408.
|
||||
* @throws Service_Unavailable_Exception When the response code is 503.
|
||||
* @throws Too_Many_Requests_Exception When the response code is 429.
|
||||
* @throws Unauthorized_Exception When the response code is 401.
|
||||
*/
|
||||
public function handle( Request $request ): Response;
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Application;
|
||||
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Bad_Request_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Forbidden_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Internal_Server_Error_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Not_Found_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Payment_Required_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Request_Timeout_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Service_Unavailable_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Too_Many_Requests_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\Unauthorized_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\WP_Request_Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Request;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Response;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Infrastructure\API_Client;
|
||||
|
||||
/**
|
||||
* Class Request_Handler
|
||||
* Handles the request to Yoast AI API.
|
||||
*
|
||||
* @makePublic
|
||||
*/
|
||||
class Request_Handler implements Request_Handler_Interface {
|
||||
|
||||
private const TIMEOUT = 60;
|
||||
|
||||
/**
|
||||
* The API client.
|
||||
*
|
||||
* @var API_Client
|
||||
*/
|
||||
private $api_client;
|
||||
|
||||
/**
|
||||
* The response parser.
|
||||
*
|
||||
* @var Response_Parser
|
||||
*/
|
||||
private $response_parser;
|
||||
|
||||
/**
|
||||
* Request_Handler constructor.
|
||||
*
|
||||
* @param API_Client $api_client The API client.
|
||||
* @param Response_Parser $response_parser The response parser.
|
||||
*/
|
||||
public function __construct( API_Client $api_client, Response_Parser $response_parser ) {
|
||||
$this->api_client = $api_client;
|
||||
$this->response_parser = $response_parser;
|
||||
}
|
||||
|
||||
// phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber -- PHPCS doesn't take into account exceptions thrown in called methods.
|
||||
|
||||
/**
|
||||
* Executes the request to the API.
|
||||
*
|
||||
* @param Request $request The request to execute.
|
||||
*
|
||||
* @return Response The response from the API.
|
||||
*
|
||||
* @throws Bad_Request_Exception When the request fails for any other reason.
|
||||
* @throws Forbidden_Exception When the response code is 403.
|
||||
* @throws Internal_Server_Error_Exception When the response code is 500.
|
||||
* @throws Not_Found_Exception When the response code is 404.
|
||||
* @throws Payment_Required_Exception When the response code is 402.
|
||||
* @throws Request_Timeout_Exception When the response code is 408.
|
||||
* @throws Service_Unavailable_Exception When the response code is 503.
|
||||
* @throws Too_Many_Requests_Exception When the response code is 429.
|
||||
* @throws Unauthorized_Exception When the response code is 401.
|
||||
* @throws WP_Request_Exception When the request fails for any other reason.
|
||||
*/
|
||||
public function handle( Request $request ): Response {
|
||||
$api_response = $this->api_client->perform_request(
|
||||
$request->get_action_path(),
|
||||
$request->get_body(),
|
||||
$request->get_headers(),
|
||||
$request->is_post()
|
||||
);
|
||||
|
||||
$response = $this->response_parser->parse( $api_response );
|
||||
|
||||
// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- false positive.
|
||||
switch ( $response->get_response_code() ) {
|
||||
case 200:
|
||||
return $response;
|
||||
case 401:
|
||||
throw new Unauthorized_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
case 402:
|
||||
throw new Payment_Required_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code(), null, $response->get_missing_licenses() );
|
||||
case 403:
|
||||
throw new Forbidden_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
case 404:
|
||||
throw new Not_Found_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
case 408:
|
||||
throw new Request_Timeout_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
case 429:
|
||||
throw new Too_Many_Requests_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code(), null, $response->get_missing_licenses() );
|
||||
case 500:
|
||||
throw new Internal_Server_Error_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
case 503:
|
||||
throw new Service_Unavailable_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
default:
|
||||
throw new Bad_Request_Exception( $response->get_message(), $response->get_response_code(), $response->get_error_code() );
|
||||
}
|
||||
// phpcs:enable WordPress.Security.EscapeOutput.ExceptionNotEscaped
|
||||
}
|
||||
|
||||
// phpcs:enable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Application;
|
||||
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Response;
|
||||
|
||||
interface Response_Parser_Interface {
|
||||
|
||||
/**
|
||||
* Parses the response from the API.
|
||||
*
|
||||
* @param array<int|string|array<string>> $response The response from the API.
|
||||
*
|
||||
* @return Response The parsed response.
|
||||
*/
|
||||
public function parse( $response ): Response;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Application;
|
||||
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Response;
|
||||
|
||||
/**
|
||||
* Class Response_Parser
|
||||
* Parses the response from the AI API and creates a Response object.
|
||||
*/
|
||||
class Response_Parser implements Response_Parser_Interface {
|
||||
|
||||
/**
|
||||
* Parses the response from the API.
|
||||
*
|
||||
* @param array<int|string|array<string>> $response The response from the API.
|
||||
*
|
||||
* @return Response The parsed response.
|
||||
*/
|
||||
public function parse( $response ): Response {
|
||||
$response_code = ( \wp_remote_retrieve_response_code( $response ) !== '' ) ? \wp_remote_retrieve_response_code( $response ) : 0;
|
||||
$response_message = \esc_html( \wp_remote_retrieve_response_message( $response ) );
|
||||
$error_code = '';
|
||||
$missing_licenses = [];
|
||||
|
||||
if ( $response_code !== 200 && $response_code !== 0 ) {
|
||||
$json_body = \json_decode( \wp_remote_retrieve_body( $response ) );
|
||||
if ( $json_body !== null ) {
|
||||
$response_message = ( $json_body->message ?? $response_message );
|
||||
$error_code = ( $json_body->error_code ?? $this->map_message_to_code( $response_message ) );
|
||||
if ( $response_code === 402 || $response_code === 429 ) {
|
||||
$missing_licenses = isset( $json_body->missing_licenses ) ? (array) $json_body->missing_licenses : [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Response( $response['body'], $response_code, $response_message, $error_code, $missing_licenses );
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the error message to a code.
|
||||
*
|
||||
* @param string $message The error message.
|
||||
*
|
||||
* @return string The mapped code.
|
||||
*/
|
||||
private function map_message_to_code( string $message ): string {
|
||||
if ( \strpos( $message, 'must NOT have fewer than 1 characters' ) !== false ) {
|
||||
return 'NOT_ENOUGH_CONTENT';
|
||||
}
|
||||
if ( \strpos( $message, 'Client timeout' ) !== false ) {
|
||||
return 'CLIENT_TIMEOUT';
|
||||
}
|
||||
if ( \strpos( $message, 'Server timeout' ) !== false ) {
|
||||
return 'SERVER_TIMEOUT';
|
||||
}
|
||||
|
||||
return 'UNKNOWN';
|
||||
}
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 400 - Bad request response.
|
||||
*/
|
||||
class Bad_Request_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 403 - Forbidden response.
|
||||
*/
|
||||
class Forbidden_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 500 - Internal server error response.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Internal_Server_Error_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 404 - not found response.
|
||||
*/
|
||||
class Not_Found_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class to manage a 402 - payment required response.
|
||||
*/
|
||||
class Payment_Required_Exception extends Remote_Request_Exception {
|
||||
|
||||
/**
|
||||
* The missing plugin licenses.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $missing_licenses;
|
||||
|
||||
/**
|
||||
* Payment_Required_Exception constructor.
|
||||
*
|
||||
* @param string $message The error message.
|
||||
* @param int $code The error status code.
|
||||
* @param string $error_identifier The error code identifier, used to identify a type of error.
|
||||
* @param Throwable| null $previous The previously thrown exception.
|
||||
* @param string[] $missing_licenses The missing plugin licenses.
|
||||
*/
|
||||
public function __construct( $message = '', $code = 0, $error_identifier = '', $previous = null, $missing_licenses = [] ) {
|
||||
$this->missing_licenses = $missing_licenses;
|
||||
parent::__construct( $message, $code, $error_identifier, $previous );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the missing plugin licences.
|
||||
*
|
||||
* @return string[] The missing plugin licenses.
|
||||
*/
|
||||
public function get_missing_licenses() {
|
||||
return $this->missing_licenses;
|
||||
}
|
||||
}
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Remote_Request_Exception
|
||||
*/
|
||||
abstract class Remote_Request_Exception extends Exception {
|
||||
|
||||
/**
|
||||
* A string error code that can be used to identify a particular type of error.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $error_identifier;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $message The error message.
|
||||
* @param int $code The error status code.
|
||||
* @param string $error_identifier The error code identifier, used to identify a type of error.
|
||||
* @param Throwable|null $previous The previously thrown exception.
|
||||
*/
|
||||
public function __construct( $message = '', $code = 0, $error_identifier = '', ?Throwable $previous = null ) {
|
||||
parent::__construct( $message, $code, $previous );
|
||||
$this->error_identifier = (string) $error_identifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the error identifier.
|
||||
*
|
||||
* @return string The error identifier.
|
||||
*/
|
||||
public function get_error_identifier(): string {
|
||||
return $this->error_identifier;
|
||||
}
|
||||
}
|
||||
wp-content/plugins/wordpress-seo/src/ai-http-request/domain/exceptions/request-timeout-exception.php
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 408 - request timeout exception
|
||||
*/
|
||||
class Request_Timeout_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 503 - service unavailable response.
|
||||
*/
|
||||
class Service_Unavailable_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 429 - Too many requests response.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Too_Many_Requests_Exception extends Payment_Required_Exception {
|
||||
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
/**
|
||||
* Class to manage a 401 - unauthorized response.
|
||||
*/
|
||||
class Unauthorized_Exception extends Remote_Request_Exception {
|
||||
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
// phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions;
|
||||
|
||||
use Throwable;
|
||||
/**
|
||||
* Class to manage an error response in wp_remote_*() requests.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class WP_Request_Exception extends Remote_Request_Exception {
|
||||
|
||||
/**
|
||||
* WP_Request_Exception constructor.
|
||||
*
|
||||
* @param string $message The error message.
|
||||
* @param Throwable| null $previous The previously thrown exception.
|
||||
*/
|
||||
public function __construct( $message = '', $previous = null ) {
|
||||
parent::__construct( $message, 400, 'WP_HTTP_REQUEST_ERROR', $previous );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain;
|
||||
|
||||
/**
|
||||
* Class Request
|
||||
* Represents a request to the AI Generator API.
|
||||
*/
|
||||
class Request {
|
||||
|
||||
/**
|
||||
* The action path for the request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $action_path;
|
||||
|
||||
/**
|
||||
* The body of the request.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* The headers for the request.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* Whether the request is a POST request.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $is_post;
|
||||
|
||||
/**
|
||||
* Constructor for the Request class.
|
||||
*
|
||||
* @param string $action_path The action path for the request.
|
||||
* @param array<string> $body The body of the request.
|
||||
* @param array<string> $headers The headers for the request.
|
||||
* @param bool $is_post Whether the request is a POST request. Default is true.
|
||||
*/
|
||||
public function __construct( string $action_path, array $body = [], array $headers = [], bool $is_post = true ) {
|
||||
$this->action_path = $action_path;
|
||||
$this->body = $body;
|
||||
$this->headers = $headers;
|
||||
$this->is_post = $is_post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action path for the request.
|
||||
*
|
||||
* @return string The action path for the request.
|
||||
*/
|
||||
public function get_action_path(): string {
|
||||
return $this->action_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the body of the request.
|
||||
*
|
||||
* @return array<string> The body of the request.
|
||||
*/
|
||||
public function get_body(): array {
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the headers for the request.
|
||||
*
|
||||
* @return array<string> The headers for the request.
|
||||
*/
|
||||
public function get_headers(): array {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the request is a POST request.
|
||||
*
|
||||
* @return bool True if the request is a POST request, false otherwise.
|
||||
*/
|
||||
public function is_post(): bool {
|
||||
return $this->is_post;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Domain;
|
||||
|
||||
/**
|
||||
* Class Response
|
||||
* Represents a response from the AI Generator API.
|
||||
*/
|
||||
class Response {
|
||||
|
||||
/**
|
||||
* The response body.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* The response code.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $response_code;
|
||||
|
||||
/**
|
||||
* The response message.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $message;
|
||||
|
||||
/**
|
||||
* The error code.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $error_code;
|
||||
|
||||
/**
|
||||
* The missing licenses.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private $missing_licenses;
|
||||
|
||||
/**
|
||||
* Response constructor.
|
||||
*
|
||||
* @param string $body The response body.
|
||||
* @param int $response_code The response code.
|
||||
* @param string $message The response message.
|
||||
* @param string $error_code The error code.
|
||||
* @param array<string> $missing_licenses The missing licenses.
|
||||
*/
|
||||
public function __construct( string $body, int $response_code, string $message, string $error_code = '', $missing_licenses = [] ) {
|
||||
$this->body = $body;
|
||||
$this->response_code = $response_code;
|
||||
$this->message = $message;
|
||||
$this->error_code = $error_code;
|
||||
$this->missing_licenses = $missing_licenses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response body.
|
||||
*
|
||||
* @return string The response body.
|
||||
*/
|
||||
public function get_body() {
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response code.
|
||||
*
|
||||
* @return int The response code.
|
||||
*/
|
||||
public function get_response_code(): int {
|
||||
return $this->response_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response message.
|
||||
*
|
||||
* @return string The response message.
|
||||
*/
|
||||
public function get_message(): string {
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error code.
|
||||
*
|
||||
* @return string The error code.
|
||||
*/
|
||||
public function get_error_code(): string {
|
||||
return $this->error_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the missing licenses.
|
||||
*
|
||||
* @return array<string> The missing licenses.
|
||||
*/
|
||||
public function get_missing_licenses(): array {
|
||||
return $this->missing_licenses;
|
||||
}
|
||||
}
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Infrastructure;
|
||||
|
||||
use Exception;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\WP_Request_Exception;
|
||||
|
||||
/**
|
||||
* Interface for the API client.
|
||||
*/
|
||||
|
||||
interface API_Client_Interface {
|
||||
|
||||
/**
|
||||
* Performs a request to the API.
|
||||
*
|
||||
* @param string $action_path The action path for the request.
|
||||
* @param array<string> $body The body of the request.
|
||||
* @param array<string> $headers The headers for the request.
|
||||
* @param bool $is_post Whether the request is a POST request.
|
||||
*
|
||||
* @return array<int|string|array<string>> The response from the API.
|
||||
*
|
||||
* @throws WP_Request_Exception When the wp_remote_post() returns an error.
|
||||
*/
|
||||
public function perform_request( string $action_path, $body, $headers, bool $is_post ): array;
|
||||
|
||||
/**
|
||||
* Gets the timeout of the requests in seconds.
|
||||
*
|
||||
* @return int The timeout of the suggestion requests in seconds.
|
||||
*/
|
||||
public function get_request_timeout(): int;
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\AI_HTTP_Request\Infrastructure;
|
||||
|
||||
use WPSEO_Utils;
|
||||
use Yoast\WP\SEO\AI_HTTP_Request\Domain\Exceptions\WP_Request_Exception;
|
||||
|
||||
/**
|
||||
* Class API_Client
|
||||
* Handles the API requests to the AI Generator API.
|
||||
*
|
||||
* @makePublic
|
||||
*/
|
||||
class API_Client implements API_Client_Interface {
|
||||
|
||||
/**
|
||||
* The base URL for the API.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $base_url = 'https://ai.yoa.st/api/v1';
|
||||
|
||||
/**
|
||||
* Performs a request to the API.
|
||||
*
|
||||
* @param string $action_path The action path for the request.
|
||||
* @param array<string> $body The body of the request.
|
||||
* @param array<string> $headers The headers for the request.
|
||||
* @param bool $is_post Whether the request is a POST request.
|
||||
*
|
||||
* @return array<int|string|array<string>> The response from the API.
|
||||
*
|
||||
* @throws WP_Request_Exception When the wp_remote_post() returns an error.
|
||||
*/
|
||||
public function perform_request( string $action_path, $body, $headers, bool $is_post ): array {
|
||||
// Our API expects JSON.
|
||||
// The request times out after 30 seconds.
|
||||
$headers = \array_merge( $headers, [ 'Content-Type' => 'application/json' ] );
|
||||
$arguments = [
|
||||
'timeout' => $this->get_request_timeout(),
|
||||
'headers' => $headers,
|
||||
];
|
||||
|
||||
if ( $is_post ) {
|
||||
// phpcs:ignore Yoast.Yoast.JsonEncodeAlternative.Found -- Reason: We don't want the debug/pretty possibility.
|
||||
$arguments['body'] = WPSEO_Utils::format_json_encode( $body );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter: 'Yoast\WP\SEO\ai_api_url' - Replaces the default URL for the AI API with a custom one.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param string $url The default URL for the AI API.
|
||||
*/
|
||||
$url = \apply_filters( 'Yoast\WP\SEO\ai_api_url', $this->base_url );
|
||||
$response = ( $is_post ) ? \wp_remote_post( $url . $action_path, $arguments ) : \wp_remote_get( $url . $action_path, $arguments );
|
||||
|
||||
if ( \is_wp_error( $response ) ) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- false positive.
|
||||
throw new WP_Request_Exception( $response->get_error_message() );
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the timeout of the requests in seconds.
|
||||
*
|
||||
* @return int The timeout of the suggestion requests in seconds.
|
||||
*/
|
||||
public function get_request_timeout(): int {
|
||||
/**
|
||||
* Filter: 'Yoast\WP\SEO\ai_suggestions_timeout' - Replaces the default timeout with a custom one, for testing purposes.
|
||||
*
|
||||
* @since 22.7
|
||||
* @internal
|
||||
*
|
||||
* @param int $timeout The default timeout in seconds.
|
||||
*/
|
||||
return (int) \apply_filters( 'Yoast\WP\SEO\ai_suggestions_timeout', 60 );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user