PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / ai / http-request / application / response-parser.php

response-parser.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.5, at src/ai/http-request/application/response-parser.php

89 lines 3.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
4
5 namespace Yoast\WP\SEO\AI\HTTP_Request\Application;
6
7 use Yoast\WP\SEO\AI\HTTP_Request\Domain\Response;
8
9 /**
10 * Class Response_Parser
11 * Parses the response from the AI API and creates a Response object.
12 */
13 class Response_Parser implements Response_Parser_Interface {
14
15 /**
16 * Parses the response from the API.
17 *
18 * @param array<int|string|array<string>> $response The response from the API.
19 *
20 * @return Response The parsed response.
21 */
22 public function parse( $response ): Response {
23 $response_code = ( \wp_remote_retrieve_response_code( $response ) !== '' ) ? \wp_remote_retrieve_response_code( $response ) : 0;
24 $response_message = \esc_html( \wp_remote_retrieve_response_message( $response ) );
25 $error_code = '';
26 $missing_licenses = [];
27
28 if ( $response_code !== 200 && $response_code !== 0 ) {
29 $json_body = \json_decode( \wp_remote_retrieve_body( $response ) );
30 if ( $json_body !== null ) {
31 $response_message = ( $json_body->message ?? $response_message );
32 $error_code = ( $json_body->error_code ?? $this->map_message_to_code( $response_message ) );
33 if ( $response_code === 402 || $response_code === 429 ) {
34 $missing_licenses = isset( $json_body->missing_licenses ) ? (array) $json_body->missing_licenses : [];
35 }
36 }
37 }
38
39 $headers = $this->normalize_headers( \wp_remote_retrieve_headers( $response ) );
40
41 return new Response( $response['body'], $response_code, $response_message, $error_code, $missing_licenses, $headers );
42 }
43
44 /**
45 * Normalizes wp_remote_retrieve_headers() output to a plain array keyed by lower-cased header name.
46 *
47 * @param object|array<string, string|array<string>>|string $raw_headers The raw return value of wp_remote_retrieve_headers(), which is a Requests_Utility_CaseInsensitiveDictionary, an array, or empty string.
48 *
49 * @return array<string, string|array<string>> The normalized headers.
50 */
51 private function normalize_headers( $raw_headers ): array {
52 // wp_remote_retrieve_headers() returns a CaseInsensitiveDictionary in WP core; getAll() yields a plain array.
53 if ( \is_object( $raw_headers ) && \method_exists( $raw_headers, 'getAll' ) ) {
54 $raw_headers = $raw_headers->getAll();
55 }
56 if ( ! \is_array( $raw_headers ) ) {
57 return [];
58 }
59
60 $normalized = [];
61 foreach ( $raw_headers as $name => $value ) {
62 $normalized[ \strtolower( (string) $name ) ] = $value;
63 }
64
65 return $normalized;
66 }
67
68 /**
69 * Maps the error message to a code.
70 *
71 * @param string $message The error message.
72 *
73 * @return string The mapped code.
74 */
75 private function map_message_to_code( string $message ): string {
76 if ( \strpos( $message, 'must NOT have fewer than 1 characters' ) !== false ) {
77 return 'NOT_ENOUGH_CONTENT';
78 }
79 if ( \strpos( $message, 'Client timeout' ) !== false ) {
80 return 'CLIENT_TIMEOUT';
81 }
82 if ( \strpos( $message, 'Server timeout' ) !== false ) {
83 return 'SERVER_TIMEOUT';
84 }
85
86 return 'UNKNOWN';
87 }
88 }
89