PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 3.0.3
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v3.0.3
4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Request / RequestService.php
surecart / app / src / Request Last commit date
RequestCacheService.php 1 year ago RequestService.php 2 years ago RequestServiceProvider.php 3 years ago
RequestService.php
449 lines
1 <?php
2
3 namespace SureCart\Request;
4
5 use SureCart\Models\ApiToken;
6 use SureCart\Support\Errors\ErrorsService;
7
8 /**
9 * Provide api request functionality.
10 */
11 class RequestService {
12 /**
13 * Has this been cached yet for the request?
14 *
15 * @var boolean
16 */
17 protected static $cached = false;
18
19 /**
20 * Undocumented variable
21 *
22 * @var string
23 */
24 protected $token = '';
25
26 /**
27 * The base path for the request.
28 *
29 * @var string
30 */
31 protected $base_path;
32
33 /**
34 * Errors service container
35 *
36 * @var \SureCart\Support\Errors\ErrorsService;
37 */
38 protected $errors_service;
39
40 /**
41 * What type of cached request is this.
42 *
43 * @var string|null
44 */
45 protected $cache_status = 'none';
46
47 /**
48 * Is this request authorized?
49 *
50 * @var boolean
51 */
52 protected $authorized = true;
53
54 /**
55 * Number of retries.
56 *
57 * @var integer
58 */
59 protected $current_retries = 0;
60
61 /**
62 * Total number of retries.
63 *
64 * @var integer
65 */
66 protected $total_retries = 1;
67
68 /**
69 * Status codes to retry.
70 *
71 * @var array
72 */
73 protected $retry_status_codes = [ 409 ];
74
75 /**
76 * Constructor.
77 *
78 * @param string $token The rest api base path.
79 * @param string $base_path The rest api base path.
80 * @param \SureCart\Support\Errors\ErrorsService $errors_service The error handling service.
81 * @param boolean $authorized Is this request authorized.
82 *
83 * @return void
84 */
85 public function __construct( $token = '', $base_path = '/v1', $errors_service = null, $authorized = true ) {
86 // error handing service.
87 $this->errors_service = $errors_service ? $errors_service : new ErrorsService();
88 // set the token.
89 $this->token = $token;
90 // set the base path and url.
91 $this->base_path = $base_path;
92 $this->authorized = $authorized;
93 }
94
95 /**
96 * Set the API token on the fly.
97 *
98 * @param string $token API token.
99 *
100 * @return $this
101 */
102 public function setToken( $token ) {
103 $this->token = $token;
104 return $this;
105 }
106
107 /**
108 * Get the base url.
109 */
110 public function getBaseUrl() {
111 return untrailingslashit( SURECART_API_URL ) . trailingslashit( $this->base_path );
112 }
113
114 /**
115 * Should we get a cached request?
116 *
117 * @return boolean
118 */
119 public function shouldFindCache( $cachable, $cache_key, $args = [] ) {
120 // only for fetch requests.
121 if ( isset( $args['method'] ) && 'GET' !== $args['method'] ) {
122 return false;
123 }
124
125 // if the args are set, then do what they say.
126 if ( isset( $args['query']['cached'] ) ) {
127 return (bool) $args['query']['cached'];
128 }
129
130 // don't cache edit context.
131 if ( isset( $args['query']['context'] ) && 'edit' === $args['query']['context'] ) {
132 return false;
133 }
134
135 return (bool) $cachable && $cache_key;
136 }
137
138 /**
139 * Respond to the request.
140 *
141 * @param array $response Reponse data.
142 * @param array $args Request arguments.
143 * @param string $endpoint The endpoint to request.
144 *
145 * @return array Response data.
146 */
147 public function respond( $response, $args, $endpoint ) {
148 if ( is_array( $response ) ) {
149 foreach ( $response as $item ) {
150 $item->cache_status = $this->cache_status;
151 }
152 }
153
154 if ( is_object( $response ) ) {
155 $response->cache_status = $this->cache_status;
156 }
157
158 return apply_filters( 'surecart/request/response', $response, $args, $endpoint );
159 }
160
161 /**
162 * Set the response cache status.
163 *
164 * @param object $response Response object.
165 * @param string $status The response status.
166 *
167 * @return void
168 */
169 public function setResponseCacheStatus( $response, $status ) {
170 if ( is_array( $response ) ) {
171 foreach ( $response as $item ) {
172 $item->cached = $status;
173 }
174 } elseif ( is_object( $response ) ) {
175 $response->cached = $status;
176 }
177
178 return $response;
179 }
180
181 /**
182 * Make the request
183 *
184 * @param string $endpoint Endpoint to request.
185 * @param array $args Arguments for request.
186 * @param boolean $cachable Should this request be cached.
187 * @param string $cache_key The cache key to use.
188 *
189 * @return mixed
190 */
191 public function makeRequest( $endpoint, $args = [], $cachable = false, $cache_key = '' ) {
192 // use the cache service for this request.
193 $cache = new RequestCacheService( $endpoint, $args, $cache_key );
194
195 // check if we should get a cached version of this.
196 if ( $this->shouldFindCache( $cachable, $cache_key, $args ) ) {
197 // get from cache.
198 $response_body = $cache->getTransientCache();
199 // we have a cached response.
200 if ( false !== $response_body ) {
201 $this->cache_status = 'transient';
202 return $this->respond( $response_body, $args, $endpoint );
203 }
204 }
205
206 // make the uncached request.
207 $response_body = $this->makeUncachedRequest( $endpoint, $args );
208
209 if ( is_wp_error( $response_body ) ) {
210 return $response_body;
211 }
212
213 // set in object cache.
214 $cache->setCache( $response_body, 'object' );
215 if ( (bool) $cachable && $cache_key ) {
216 $cache->setCache( $response_body, 'transient' );
217 }
218
219 // return response.
220 return $this->respond( $response_body, $args, $endpoint );
221 }
222
223 /**
224 * Make the uncached request.
225 *
226 * @param string $endpoint Endpoint to request.
227 * @param array $args Arguments for request.
228 *
229 * @return mixed
230 */
231 public function makeUncachedRequest( $endpoint, $args = [] ) {
232 // must have a token for the request.
233 if ( $this->authorized && empty( $this->token ) ) {
234 return new \WP_Error( 'missing_token', __( 'Please connect your site to SureCart.', 'surecart' ) );
235 }
236
237 // make sure we send json.
238 if ( empty( $args['headers']['Content-Type'] ) ) {
239 $args['headers']['Content-Type'] = 'application/json';
240 }
241
242 // add auth.
243 if ( $this->authorized && ! empty( $this->token ) && empty( $args['headers']['Authorization'] ) ) {
244 $args['headers']['Authorization'] = "Bearer $this->token";
245 }
246
247 // add version header.
248 $args['headers']['X-SURECART-WP-PLUGIN-VERSION'] = \SureCart::plugin()->version();
249
250 // add referer header.
251 if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
252 $args['headers']['X-SURECART-REFERRER'] = esc_url_raw( $_SERVER['HTTP_REFERER'] );
253 }
254
255 // parse args.
256 $args = wp_parse_args(
257 $args,
258 [
259 'timeout' => 20,
260 'sslverify' => true,
261 ]
262 );
263
264 // filter args and endpoint.
265 $args = apply_filters( 'surecart/request/args', $args, $endpoint );
266 $endpoint = apply_filters( 'surecart/request/endpoint', $endpoint, $args );
267
268 // make url.
269 $url = trailingslashit( $this->getBaseUrl() ) . untrailingslashit( $endpoint );
270
271 // add query args.
272 if ( ! empty( $args['query'] ) ) {
273 $url = add_query_arg( $this->parseArgs( $args['query'] ), $url );
274 $url = preg_replace( '/%5B[0-9]+%5D/', '%5B%5D', $url );
275 unset( $args['query'] );
276 }
277
278 // json encode body.
279 if ( ! empty( $args['body'] ) ) {
280 if ( 'application/json' === $args['headers']['Content-Type'] ) {
281 $args['body'] = wp_json_encode( $this->parseArgs( $args['body'] ) );
282 }
283 }
284
285 // make request.
286 $response = $this->remoteRequest( $url, $args );
287
288 // bail early if it's a wp_error.
289 if ( is_wp_error( $response ) ) {
290 return $response;
291 }
292
293 $response_code = wp_remote_retrieve_response_code( $response );
294
295 // handle handle retries.
296 if ( in_array( $response_code, $this->retry_status_codes ) ) {
297 $this->current_retries++;
298 if ( $this->current_retries <= $this->total_retries ) {
299 call_user_func_array( [ $this, __METHOD__ ], func_get_args() );
300 }
301 }
302
303 $response_body = wp_remote_retrieve_body( $response );
304 $admin_notice = (array) wp_remote_retrieve_header( $response, 'X-SURECART-WP-ADMIN-NOTICE' );
305
306 if ( ! $this->authorized ) {
307 $api_token = (string) wp_remote_retrieve_header( $response, 'X-SURECART-API-TOKEN' );
308 ApiToken::save( $api_token );
309 }
310
311 if ( $admin_notice ) {
312 // we don't care if this fails.
313 try {
314 \SureCart::notices()->showResponseNotice( $admin_notice );
315 } catch ( \Exception $e ) {
316 error_log( $e->getMessage() );
317 }
318 }
319
320 // Handle invalid token first.
321 if ( $this->authorized && 401 === $response_code ) {
322 ApiToken::clear();
323 return new \WP_Error( 'invalid_token', __( 'Invalid API token.', 'surecart' ) );
324 }
325
326 // check for errors.
327 if ( ! in_array( $response_code, [ 200, 201 ], true ) ) {
328 $body = json_decode( $response_body, true );
329 if ( is_string( $body ) ) {
330 return new \WP_Error( 'error', $response_body );
331 }
332 return $this->errors_service->translate( $body, $response_code );
333 }
334
335 return json_decode( $response_body );
336 }
337
338 /**
339 * Make the remote request.
340 *
341 * @param string $url The url to request.
342 * @param array $args The args to pass to the request.
343 *
344 * @return mixed
345 */
346 public function remoteRequest( $url, $args = [] ) {
347 return wp_remote_request( esc_url_raw( $url ), $args );
348 }
349
350 /**
351 * Make a get request
352 *
353 * @param string $endpoint Endpoint for the request.
354 * @param array $args Request arguments.
355 *
356 * @return mixed
357 */
358 public function get( $endpoint, $args = [] ) {
359 $args['method'] = 'GET';
360 return $this->makeRequest( $endpoint, $args );
361 }
362
363 /**
364 * Make a post request
365 *
366 * @param string $endpoint Endpoint for the request.
367 * @param array $args Request arguments.
368 *
369 * @return mixed
370 */
371 public function post( $endpoint, $args = [] ) {
372 $args['method'] = 'POST';
373 return $this->makeRequest( $endpoint, $args );
374 }
375
376 /**
377 * Make a put request
378 *
379 * @param string $endpoint Endpoint for the request.
380 * @param array $args Request arguments.
381 *
382 * @return mixed
383 */
384 public function put( $endpoint, $args = [] ) {
385 $args['method'] = 'PUT';
386 return $this->makeRequest( $endpoint, $args );
387 }
388
389 /**
390 * Make a patch request
391 *
392 * @param string $endpoint Endpoint for the request.
393 * @param array $args Request arguments.
394 *
395 * @return mixed
396 */
397 public function patch( $endpoint, $args = [] ) {
398 $args['method'] = 'PATCH';
399 return $this->makeRequest( $endpoint, $args );
400 }
401
402 /**
403 * Make a delete request
404 *
405 * @param string $endpoint Endpoint for the request.
406 * @param array $args Request arguments.
407 *
408 * @return mixed
409 */
410 public function delete( $endpoint, $args = [] ) {
411 $args['method'] = 'DELETE';
412 return $this->makeRequest( $endpoint, $args );
413 }
414
415 /**
416 * Removes empty args
417 *
418 * @param array $args Array of arguments.
419 */
420 protected function parseArgs( $args = [] ) {
421 if ( ! is_array( $args ) ) {
422 return $args;
423 }
424 foreach ( $args as $key => $arg ) {
425 // unset null.
426 if ( null === $arg ) {
427 unset( $args[ $key ] );
428 }
429
430 // filter out wp params.
431 if ( in_array( $key, [ 'locale', 'rest_route' ], true ) ) {
432 unset( $args[ $key ] );
433 }
434
435 // convert bool to int to prevent getting unset.
436 if ( is_bool( $arg ) ) {
437 $args[ $key ] = $arg ? 1 : 0;
438 }
439
440 // url encode any strings.
441 if ( is_string( $arg ) ) {
442 $args[ $key ] = urlencode( $arg );
443 }
444 }
445
446 return $args;
447 }
448 }
449