PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.18
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.18
1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 All 163 releases
woocommerce-pos / includes / Sync / Retry_After_Mirror.php

Retry_After_Mirror.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.18, at includes/Sync/Retry_After_Mirror.php

104 lines 3.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Retry-After response body mirror.
4 *
5 * @package WCPOS\WooCommercePOS\Sync
6 */
7
8 namespace WCPOS\WooCommercePOS\Sync;
9
10 use WCPOS\WooCommercePOS\API as Root_API;
11 use WP_HTTP_Response;
12 use WP_REST_Request;
13
14 /** Mirrors Retry-After into WCPOS REST error bodies. */
15 final class Retry_After_Mirror {
16 /** Register the response filter once. */
17 public static function register_hooks(): void {
18 if ( false === has_filter( 'rest_post_dispatch', array( self::class, 'filter_response' ) ) ) {
19 add_filter( 'rest_post_dispatch', array( self::class, 'filter_response' ), PHP_INT_MAX - 2, 3 );
20 }
21 }
22
23 /**
24 * Normalize delay-seconds or an HTTP-date to whole seconds.
25 *
26 * @param string $value Retry-After header value.
27 * @param int|null $now Current Unix timestamp.
28 */
29 public static function seconds_from_header( string $value, ?int $now = null ): ?int {
30 if ( 1 === preg_match( '/^[0-9]+$/D', $value ) ) {
31 $digits = ltrim( $value, '0' );
32 $digits = '' === $digits ? '0' : $digits;
33 if ( strlen( $digits ) > strlen( (string) PHP_INT_MAX ) || ( strlen( $digits ) === strlen( (string) PHP_INT_MAX ) && strcmp( $digits, (string) PHP_INT_MAX ) > 0 ) ) {
34 return null;
35 }
36 return (int) $digits;
37 }
38 $target = strtotime( $value );
39 if ( false === $target || ! in_array( $value, array( gmdate( 'D, d M Y H:i:s \G\M\T', $target ), gmdate( 'l, d-M-y H:i:s \G\M\T', $target ), gmdate( 'D M j H:i:s Y', $target ), gmdate( 'D M j H:i:s Y', $target ) ), true ) ) {
40 return null;
41 }
42 return max( 0, (int) ceil( $target - ( $now ?? time() ) ) );
43 }
44
45 /**
46 * Mirror Retry-After for WCPOS error responses.
47 *
48 * @param mixed $response REST response.
49 * @param mixed $server REST server.
50 * @param WP_REST_Request $request REST request.
51 *
52 * @return mixed
53 */
54 public static function filter_response( $response, $server, WP_REST_Request $request ) {
55 $route = strtolower( '/' . ltrim( $request->get_route(), '/' ) );
56 if ( ! $response instanceof WP_HTTP_Response || $response->get_status() < 400 || ! self::is_wcpos_request( $request, $route ) ) {
57 return $response;
58 }
59 $data = $response->get_data();
60 $headers = array_change_key_case( $response->get_headers(), CASE_LOWER );
61 if ( ! is_array( $data ) ) {
62 return $response;
63 }
64 if (
65 ! isset( $headers['retry-after'] )
66 && 503 === $response->get_status()
67 && 'wcpos_sync_unavailable' === ( $data['code'] ?? null )
68 && Health::RETRY_AFTER_SECONDS === ( $data['data']['retry_after_seconds'] ?? null )
69 ) {
70 $response->header( 'Retry-After', (string) Health::RETRY_AFTER_SECONDS );
71 return $response;
72 }
73 if ( ! isset( $headers['retry-after'] ) ) {
74 return $response;
75 }
76 if ( ! isset( $data['data'] ) ) {
77 $data['data'] = array();
78 }
79 if ( ! is_array( $data['data'] ) || array_key_exists( 'retry_after_seconds', $data['data'] ) ) {
80 return $response;
81 }
82 $seconds = self::seconds_from_header( (string) $headers['retry-after'] );
83 if ( null !== $seconds ) {
84 $data['data']['retry_after_seconds'] = $seconds;
85 $response->set_data( $data );
86 }
87 return $response;
88 }
89 /**
90 * Whether this is a WCPOS namespace route or explicitly marked request.
91 *
92 * @param WP_REST_Request $request REST request.
93 * @param string $route Normalized REST route.
94 */
95 private static function is_wcpos_request( WP_REST_Request $request, string $route ): bool {
96 foreach ( Root_API::ROUTE_NAMESPACES as $namespace ) {
97 if ( 0 === strpos( $route, '/' . strtolower( $namespace ) . '/' ) ) {
98 return true;
99 }
100 }
101 return '1' === trim( (string) $request->get_header( 'X-WCPOS' ) );
102 }
103 }
104