PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.10.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / includes / integrations / class-google-api-base-client.php

class-google-api-base-client.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.10.0, at includes/integrations/class-google-api-base-client.php

218 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Google API Base Client Class
5 *
6 * Abstract base class for Google API integrations providing common functionality
7 * for HTTP requests, rate limiting, error handling, and response processing.
8 * Follows ThinkRank patterns established by Claude_Client and OpenAI_Client.
9 *
10 * @package ThinkRank\Integrations
11 * @since 1.0.0
12 */
13
14 declare(strict_types=1);
15
16 namespace ThinkRank\Integrations;
17
18 // Prevent direct access
19 if (!defined('ABSPATH')) {
20 exit;
21 }
22
23 /**
24 * Google API Base Client Class
25 *
26 * Single Responsibility: Provide common Google API functionality
27 * Following ThinkRank HTTP client patterns from Claude_Client and OpenAI_Client
28 *
29 * @since 1.0.0
30 */
31 abstract class Google_API_Base_Client {
32
33 /**
34 * API key
35 *
36 * @var string
37 */
38 protected string $api_key;
39
40 /**
41 * OAuth Access Token
42 *
43 * @var string|null
44 */
45 protected ?string $access_token = null;
46
47 /**
48 * Request timeout in seconds
49 *
50 * @var int
51 */
52 protected int $timeout;
53
54 /**
55 * Rate limit configuration
56 *
57 * @var array
58 */
59 protected array $rate_limits;
60
61 /**
62 * Constructor
63 *
64 * @param string $api_key Google API key
65 * @param int $timeout Request timeout in seconds
66 * @param string|null $access_token OAuth Access Token (optional)
67 */
68 public function __construct(string $api_key, int $timeout = 300, ?string $access_token = null) {
69 $this->api_key = $api_key;
70 $this->timeout = $timeout;
71 $this->access_token = $access_token;
72 $this->rate_limits = $this->get_rate_limits();
73 }
74
75 /**
76 * Make HTTP request to Google API
77 * Following Claude_Client and OpenAI_Client patterns
78 *
79 * @param string $url Full API URL
80 * @param array $params Request parameters
81 * @param string $method HTTP method
82 * @return array Response data
83 * @throws \Exception If request fails
84 */
85 protected function make_request(string $url, array $params = [], string $method = 'GET'): array {
86 // Check rate limiting before making request
87 $this->check_rate_limit();
88
89 $args = [
90 'timeout' => $this->timeout,
91 'headers' => [
92 'User-Agent' => 'ThinkRank/' . THINKRANK_VERSION,
93 ],
94 'method' => $method
95 ];
96
97 // Add OAuth Authorization header if token exists
98 if (!empty($this->access_token)) {
99 $args['headers']['Authorization'] = 'Bearer ' . $this->access_token;
100 }
101
102 if ($method === 'GET' && !empty($params)) {
103 $url .= '?' . http_build_query($params);
104 } elseif ($method === 'POST') {
105 $args['body'] = wp_json_encode($params);
106 $args['headers']['Content-Type'] = 'application/json';
107 }
108
109 $response = wp_remote_request($url, $args);
110
111 if (is_wp_error($response)) {
112 throw new \Exception('API request failed: ' . esc_html($response->get_error_message()));
113 }
114
115 $status_code = wp_remote_retrieve_response_code($response);
116 $response_body = wp_remote_retrieve_body($response);
117
118 if ($status_code >= 400) {
119 $error_data = json_decode($response_body, true);
120 $error_message = $error_data['error']['message'] ?? 'Unknown API error';
121 throw new \Exception(sprintf('Google API error (%d): %s', (int) $status_code, esc_html($error_message)), (int) $status_code);
122 }
123
124 $data = json_decode($response_body, true);
125
126 if (json_last_error() !== JSON_ERROR_NONE) {
127 throw new \Exception('Invalid JSON response from Google API');
128 }
129
130 // Update rate limit tracking after successful request
131 $this->update_rate_limit();
132
133 return $data;
134 }
135
136 /**
137 * Test API connection
138 * Must be implemented by concrete classes
139 *
140 * @return array Connection test results
141 */
142 abstract public function test_connection(): array;
143
144 /**
145 * Get rate limit configuration
146 * Must be implemented by concrete classes
147 *
148 * @return array Rate limit configuration
149 */
150 abstract protected function get_rate_limits(): array;
151
152 /**
153 * Check if request is within rate limits
154 * Following ThinkRank rate limiting patterns from existing classes
155 *
156 * @throws \Exception If rate limit exceeded
157 */
158 protected function check_rate_limit(): void {
159 $rate_limit_key = $this->get_rate_limit_key();
160 $current_time = time();
161
162 // Reset counter if it's a new day
163 if ($current_time >= $this->rate_limits['reset_time']) {
164 $this->reset_rate_limit_counter();
165 }
166
167 $current_count = get_transient($rate_limit_key . '_count') ?: 0;
168
169 if ($current_count >= $this->rate_limits['max_requests_per_day']) {
170 throw new \Exception(esc_html($this->get_rate_limit_error_message()));
171 }
172 }
173
174 /**
175 * Update rate limit counter after successful request
176 * Following ThinkRank transient patterns
177 */
178 protected function update_rate_limit(): void {
179 $rate_limit_key = $this->get_rate_limit_key();
180 $current_count = get_transient($rate_limit_key . '_count') ?: 0;
181 $new_count = $current_count + 1;
182
183 // Set transient to expire at end of day
184 $seconds_until_tomorrow = strtotime('tomorrow') - time();
185 set_transient($rate_limit_key . '_count', $new_count, $seconds_until_tomorrow);
186 }
187
188 /**
189 * Reset rate limit counter for new day
190 */
191 private function reset_rate_limit_counter(): void {
192 $rate_limit_key = $this->get_rate_limit_key();
193 delete_transient($rate_limit_key . '_count');
194 delete_transient($rate_limit_key . '_reset');
195
196 // Set new reset time for tomorrow
197 $seconds_until_tomorrow = strtotime('tomorrow') - time();
198 set_transient($rate_limit_key . '_reset', strtotime('tomorrow'), $seconds_until_tomorrow);
199 $this->rate_limits['reset_time'] = strtotime('tomorrow');
200 }
201
202 /**
203 * Get rate limit transient key
204 * Following ThinkRank option naming patterns
205 *
206 * @return string Rate limit key
207 */
208 abstract protected function get_rate_limit_key(): string;
209
210 /**
211 * Get rate limit error message
212 * Must be implemented by concrete classes
213 *
214 * @return string Error message
215 */
216 abstract protected function get_rate_limit_error_message(): string;
217 }
218