| @@ -84,9 +84,9 @@ | ||
| 84 | 84 | * Token Expiry Timestamp |
| 85 | 85 | * |
| 86 | 86 | * @since 3.5.0 |
| 87 | 87 | * |
| 88 | - * @var int|false | |
| 88 | + * @var int|bool | |
| 89 | 89 | */ |
| 90 | 90 | public $token_expires = false; |
| 91 | 91 | |
| 92 | 92 | /** |
| @@ -116,9 +116,9 @@ | ||
| 116 | 116 | ?> |
| 117 | 117 | <div class="wpzinc-option"> |
| 118 | 118 | <div class="full"> |
| 119 | 119 | <a href="<?php echo esc_attr( $this->get_oauth_url() ); ?>" class="button button-primary"> |
| 120 | - <?php esc_html_e( 'Connect a Buffer Account', 'wp-to-buffer' ); ?> | |
| 120 | + <?php esc_html_e( 'Connect an additional Buffer Account', 'wp-to-buffer' ); ?> | |
| 121 | 121 | </a> |
| 122 | 122 | </div> |
| 123 | 123 | </div> |
| 124 | 124 | <?php |
| @@ -331,8 +331,13 @@ | ||
| 331 | 331 | * @param bool|int $token_expires Token Expiry. |
| 332 | 332 | */ |
| 333 | 333 | public function set_tokens( $access_token = '', $refresh_token = '', $token_expires = false ) { |
| 334 | 334 | |
| 335 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): Started.' ); | |
| 336 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): access_token = ' . $access_token ); | |
| 337 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): refresh_token = ' . $refresh_token ); | |
| 338 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: set_tokens(): token_expires = ' . $token_expires ); | |
| 339 | + | |
| 335 | 340 | $this->access_token = $access_token; |
| 336 | 341 | $this->refresh_token = $refresh_token; |
| 337 | 342 | $this->token_expires = $token_expires; |
| 338 | 343 | |
| @@ -388,18 +393,30 @@ | ||
| 388 | 393 | * @since 2.0.0 |
| 389 | 394 | */ |
| 390 | 395 | public function refresh_token() { |
| 391 | 396 | |
| 392 | - // Bail if no refresh token is available to use, otherwise we'll | |
| 393 | - // send a request to Buffer with an empty refresh_token, which | |
| 394 | - // will fail. | |
| 397 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Started.' ); | |
| 398 | + | |
| 399 | + // Bail if we don't have a refresh token. | |
| 395 | 400 | if ( empty( $this->refresh_token ) ) { |
| 396 | - return new \WP_Error( | |
| 397 | - $this->base->plugin->filter_name . '_api_refresh_token_error', | |
| 398 | - __( 'No refresh token available; cannot refresh access token.', 'wp-to-buffer' ) | |
| 399 | - ); | |
| 401 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Error: No refresh token available; cannot refresh.' ); | |
| 402 | + return new \WP_Error( 'missing_refresh_token', __( 'No refresh token exists', 'wp-to-buffer' ) ); | |
| 400 | 403 | } |
| 401 | 404 | |
| 405 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): access_token = ' . $this->access_token ); | |
| 406 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): refresh_token = ' . $this->refresh_token ); | |
| 407 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): token_expires = ' . $this->token_expires ); | |
| 408 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): token_expires = ' . ( $this->token_expires && time() > (int) $this->token_expires ? 'expired ' . ( time() - (int) $this->token_expires ) . 's ago' : 'expires in ' . ( (int) $this->token_expires - time() ) . 's' ) ); | |
| 409 | + | |
| 410 | + // Bail if the access token hasn't yet expired. | |
| 411 | + if ( strtotime( '+15 minutes' ) < $this->token_expires ) { | |
| 412 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Skipped: Access token not yet within the 15 minute refresh window.' ); | |
| 413 | + return false; | |
| 414 | + } | |
| 415 | + | |
| 416 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Requesting new access token.' ); | |
| 417 | + | |
| 418 | + // Send request. | |
| 402 | 419 | $result = $this->oauth_request( |
| 403 | 420 | $this->oauth_authorize_url . 'token', |
| 404 | 421 | array( |
| 405 | 422 | 'client_id' => $this->client_id, |
| @@ -409,14 +426,16 @@ | ||
| 409 | 426 | ); |
| 410 | 427 | |
| 411 | 428 | // If an error occured, log and return it now. |
| 412 | 429 | if ( is_wp_error( $result ) ) { |
| 430 | + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: refresh_token(): Error: [%s] %s', $result->get_error_code(), $result->get_error_message() ) ); | |
| 431 | + | |
| 413 | 432 | /** |
| 414 | 433 | * Perform any actions when refreshing an expired access token fails. |
| 415 | 434 | * |
| 416 | 435 | * @since 6.0.0 |
| 417 | 436 | * |
| 418 | - * @param \WP_Error $result Error from API. | |
| 437 | + * @param \WP_Error $result Error from API. | |
| 419 | 438 | * @param string $client_id OAuth Client ID. |
| 420 | 439 | * @param string $access_token Access Token. |
| 421 | 440 | * @param string $refresh_token Refresh Token. |
| 422 | 441 | */ |
| @@ -431,8 +450,14 @@ | ||
| 431 | 450 | 'refresh_token' => $result['refresh_token'], |
| 432 | 451 | 'token_expires' => strtotime( '+' . $result['expires_in'] . ' seconds' ), |
| 433 | 452 | ); |
| 434 | 453 | |
| 454 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): Success' ); | |
| 455 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New access_token = ' . $result['access_token'] ); | |
| 456 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New refresh_token = ' . $result['refresh_token'] ); | |
| 457 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New token_expires = ' . $result['token_expires'] ); | |
| 458 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: refresh_token(): New token_expires = ' . ( $result['token_expires'] && time() > (int) $result['token_expires'] ? 'expired ' . ( time() - (int) $result['token_expires'] ) . 's ago' : 'expires in ' . ( (int) $result['token_expires'] - time() ) . 's' ) ); | |
| 459 | + | |
| 435 | 460 | /** |
| 436 | 461 | * Perform any actions with the new access token, such as saving it. |
| 437 | 462 | * |
| 438 | 463 | * @since 6.0.0 |
| @@ -559,11 +584,14 @@ | ||
| 559 | 584 | // Return stored profiles if available and not forcing a refresh. |
| 560 | 585 | $option_name = $this->base->plugin->name . '-profiles-' . $account_id; |
| 561 | 586 | $profiles = get_option( $option_name ); |
| 562 | 587 | if ( ! $force && is_array( $profiles ) ) { |
| 588 | + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: profiles(): account=%s: returning %d cached profile(s).', $account_id, count( $profiles ) ) ); | |
| 563 | 589 | return $profiles; |
| 564 | 590 | } |
| 565 | 591 | |
| 592 | + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: profiles(): account=%s: fetching profiles from Buffer (force=%s).', $account_id, $force ? 'yes' : 'no' ) ); | |
| 593 | + | |
| 566 | 594 | // Build GraphQL query. |
| 567 | 595 | $query = ' |
| 568 | 596 | query GetChannels($organizationId: OrganizationId!) { |
| 569 | 597 | channels(input: { organizationId: $organizationId }) { |
| @@ -594,8 +622,9 @@ | ||
| 594 | 622 | ); |
| 595 | 623 | |
| 596 | 624 | // Check for errors. |
| 597 | 625 | if ( is_wp_error( $results ) ) { |
| 626 | + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: profiles(): account=%s: FAILED to fetch profiles: [%s] %s', $account_id, $results->get_error_code(), $results->get_error_message() ) ); | |
| 598 | 627 | return $results; |
| 599 | 628 | } |
| 600 | 629 | |
| 601 | 630 | // Build profiles array from results. |
| @@ -742,9 +771,9 @@ | ||
| 742 | 771 | switch ( $service ) { |
| 743 | 772 | |
| 744 | 773 | case 'instagram': |
| 745 | 774 | $metadata = array( |
| 746 | - 'type' => $params['post_type'] === 'story' ? 'story' : 'post', | |
| 775 | + 'type' => in_array( $params['post_type'], array( 'story', 'video_story' ), true ) ? 'story' : 'post', | |
| 747 | 776 | 'shouldShareToFeed' => true, |
| 748 | 777 | ); |
| 749 | 778 | |
| 750 | 779 | // First Comment. |
| @@ -750,8 +779,13 @@ | ||
| 750 | 779 | // First Comment. |
| 751 | 780 | if ( ! empty( $params['first_comment'] ) ) { |
| 752 | 781 | $metadata['firstComment'] = $params['first_comment']; |
| 753 | 782 | } |
| 783 | + | |
| 784 | + // Shop Grid Link. | |
| 785 | + if ( ! empty( $params['url'] ) ) { | |
| 786 | + $metadata['link'] = $params['url']; | |
| 787 | + } | |
| 754 | 788 | break; |
| 755 | 789 | |
| 756 | 790 | case 'facebook': |
| 757 | 791 | $metadata = array( |
| @@ -764,14 +798,10 @@ | ||
| 764 | 798 | } |
| 765 | 799 | |
| 766 | 800 | // OpenGraph / Link Attachment. |
| 767 | 801 | if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) { |
| 768 | - $assets = array( | |
| 769 | - array( | |
| 770 | - 'link' => array( | |
| 771 | - 'url' => $params['url'], | |
| 772 | - ), | |
| 773 | - ), | |
| 802 | + $metadata['linkAttachment'] = array( | |
| 803 | + 'url' => $params['url'], | |
| 774 | 804 | ); |
| 775 | 805 | } |
| 776 | 806 | |
| 777 | 807 | // Annotations. |
| @@ -787,14 +817,10 @@ | ||
| 787 | 817 | } |
| 788 | 818 | |
| 789 | 819 | // OpenGraph / Link Attachment. |
| 790 | 820 | if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) { |
| 791 | - $assets = array( | |
| 792 | - array( | |
| 793 | - 'link' => array( | |
| 794 | - 'url' => $params['url'], | |
| 795 | - ), | |
| 796 | - ), | |
| 821 | + $metadata['linkAttachment'] = array( | |
| 822 | + 'url' => $params['url'], | |
| 797 | 823 | ); |
| 798 | 824 | } |
| 799 | 825 | |
| 800 | 826 | // Annotations. |
| @@ -805,13 +831,11 @@ | ||
| 805 | 831 | |
| 806 | 832 | case 'twitter': |
| 807 | 833 | // First Comment. |
| 808 | 834 | if ( ! empty( $params['first_comment'] ) ) { |
| 809 | - $metadata = array( | |
| 810 | - 'thread' => array( | |
| 811 | - array( | |
| 812 | - 'text' => $params['first_comment'], | |
| 813 | - ), | |
| 835 | + $metadata['thread'] = array( | |
| 836 | + array( | |
| 837 | + 'text' => $params['first_comment'], | |
| 814 | 838 | ), |
| 815 | 839 | ); |
| 816 | 840 | } |
| 817 | 841 | |
| @@ -869,24 +893,18 @@ | ||
| 869 | 893 | |
| 870 | 894 | case 'threads': |
| 871 | 895 | // OpenGraph / Link Attachment. |
| 872 | 896 | if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) { |
| 873 | - $assets = array( | |
| 874 | - array( | |
| 875 | - 'link' => array( | |
| 876 | - 'url' => $params['url'], | |
| 877 | - ), | |
| 878 | - ), | |
| 897 | + $metadata['linkAttachment'] = array( | |
| 898 | + 'url' => $params['url'], | |
| 879 | 899 | ); |
| 880 | 900 | } |
| 881 | 901 | |
| 882 | 902 | // First Comment. |
| 883 | 903 | if ( ! empty( $params['first_comment'] ) ) { |
| 884 | - $metadata = array( | |
| 885 | - 'thread' => array( | |
| 886 | - array( | |
| 887 | - 'text' => $params['first_comment'], | |
| 888 | - ), | |
| 904 | + $metadata['thread'] = array( | |
| 905 | + array( | |
| 906 | + 'text' => $params['first_comment'], | |
| 889 | 907 | ), |
| 890 | 908 | ); |
| 891 | 909 | } |
| 892 | 910 | break; |
| @@ -893,24 +911,18 @@ | ||
| 893 | 911 | |
| 894 | 912 | case 'bluesky': |
| 895 | 913 | // OpenGraph / Link Attachment. |
| 896 | 914 | if ( $params['post_type'] === 'link' && ! empty( $params['url'] ) ) { |
| 897 | - $assets = array( | |
| 898 | - array( | |
| 899 | - 'link' => array( | |
| 900 | - 'url' => $params['url'], | |
| 901 | - ), | |
| 902 | - ), | |
| 915 | + $metadata['linkAttachment'] = array( | |
| 916 | + 'url' => $params['url'], | |
| 903 | 917 | ); |
| 904 | 918 | } |
| 905 | 919 | |
| 906 | 920 | // First Comment. |
| 907 | 921 | if ( ! empty( $params['first_comment'] ) ) { |
| 908 | - $metadata = array( | |
| 909 | - 'thread' => array( | |
| 910 | - array( | |
| 911 | - 'text' => $params['first_comment'], | |
| 912 | - ), | |
| 922 | + $metadata['thread'] = array( | |
| 923 | + array( | |
| 924 | + 'text' => $params['first_comment'], | |
| 913 | 925 | ), |
| 914 | 926 | ); |
| 915 | 927 | } |
| 916 | 928 | break; |
| @@ -917,13 +929,11 @@ | ||
| 917 | 929 | |
| 918 | 930 | case 'mastodon': |
| 919 | 931 | // First Comment. |
| 920 | 932 | if ( ! empty( $params['first_comment'] ) ) { |
| 921 | - $metadata = array( | |
| 922 | - 'thread' => array( | |
| 923 | - array( | |
| 924 | - 'text' => $params['first_comment'], | |
| 925 | - ), | |
| 933 | + $metadata['thread'] = array( | |
| 934 | + array( | |
| 935 | + 'text' => $params['first_comment'], | |
| 926 | 936 | ), |
| 927 | 937 | ); |
| 928 | 938 | } |
| 929 | 939 | |
| @@ -964,18 +974,31 @@ | ||
| 964 | 974 | 'image' => array( |
| 965 | 975 | 'url' => $media['image'], |
| 966 | 976 | 'thumbnailUrl' => $media['thumbnail'], |
| 967 | 977 | 'metadata' => array( |
| 968 | - 'altText' => $media['alt_text'], | |
| 969 | - 'dimensions' => array( | |
| 970 | - 'width' => $media['width'], | |
| 971 | - 'height' => $media['height'], | |
| 972 | - ), | |
| 978 | + 'altText' => $media['alt_text'], | |
| 973 | 979 | ), |
| 974 | 980 | ), |
| 975 | 981 | ); |
| 976 | 982 | } |
| 977 | 983 | break; |
| 984 | + | |
| 985 | + case 'video': | |
| 986 | + case 'video_story': | |
| 987 | + // Bail if no video URL is defined. | |
| 988 | + if ( empty( $params['video_url'] ) ) { | |
| 989 | + break; | |
| 990 | + } | |
| 991 | + | |
| 992 | + // Buffer fetches the video from a public URL, so we only send the URL. | |
| 993 | + $assets = array( | |
| 994 | + array( | |
| 995 | + 'video' => array( | |
| 996 | + 'url' => $params['video_url'], | |
| 997 | + ), | |
| 998 | + ), | |
| 999 | + ); | |
| 1000 | + break; | |
| 978 | 1001 | } |
| 979 | 1002 | |
| 980 | 1003 | // Include assets. Always overwrites the default empty array |
| 981 | 1004 | // initialised above, if the service branch built any. |
| @@ -1091,13 +1114,40 @@ | ||
| 1091 | 1114 | * @since 6.0.0 |
| 1092 | 1115 | * |
| 1093 | 1116 | * @param string $query GraphQL Query. |
| 1094 | 1117 | * @param array $variables GraphQL Variables. |
| 1095 | - * @param bool $is_retry Whether this is a retry following a token refresh. | |
| 1096 | 1118 | * @return \WP_Error|array |
| 1097 | 1119 | */ |
| 1098 | - private function graphql_query( $query, $variables = array(), $is_retry = false ) { | |
| 1120 | + private function graphql_query( $query, $variables = array() ) { | |
| 1099 | 1121 | |
| 1122 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): Started.' ); | |
| 1123 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): query = ' . $query ); | |
| 1124 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): variables = ' . print_r( $variables, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r | |
| 1125 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): access_token = ' . $this->access_token ); | |
| 1126 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): refresh_token = ' . $this->refresh_token ); | |
| 1127 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . $this->token_expires ); | |
| 1128 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . ( $this->token_expires && time() > (int) $this->token_expires ? 'expired ' . ( time() - (int) $this->token_expires ) . 's ago' : 'expires in ' . ( (int) $this->token_expires - time() ) . 's' ) ); | |
| 1129 | + | |
| 1130 | + // Check required parameters exist. | |
| 1131 | + if ( empty( $this->access_token ) ) { | |
| 1132 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): Error: No access token was specified' ); | |
| 1133 | + return new \WP_Error( 'missing_access_token', __( 'No access token was specified', 'wp-to-buffer' ) ); | |
| 1134 | + } | |
| 1135 | + | |
| 1136 | + // Fetch a new access token and refresh token. | |
| 1137 | + $result = $this->refresh_token(); | |
| 1138 | + | |
| 1139 | + // Bail if something went wrong. | |
| 1140 | + if ( is_wp_error( $result ) ) { | |
| 1141 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): Error: ' . $result->get_error_message() ); | |
| 1142 | + return $result; | |
| 1143 | + } | |
| 1144 | + | |
| 1145 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): access_token = ' . $this->access_token ); | |
| 1146 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): refresh_token = ' . $this->refresh_token ); | |
| 1147 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . $this->token_expires ); | |
| 1148 | + $this->base->get_class( 'log' )->add_to_debug_log( 'Buffer API: graphql_query(): token_expires = ' . ( $this->token_expires && time() > (int) $this->token_expires ? 'expired ' . ( time() - (int) $this->token_expires ) . 's ago' : 'expires in ' . ( (int) $this->token_expires - time() ) . 's' ) ); | |
| 1149 | + | |
| 1100 | 1150 | // Build body. |
| 1101 | 1151 | $body = array( 'query' => $query ); |
| 1102 | 1152 | if ( ! empty( $variables ) ) { |
| 1103 | 1153 | $body['variables'] = $variables; |
| @@ -1115,41 +1165,22 @@ | ||
| 1115 | 1165 | ); |
| 1116 | 1166 | |
| 1117 | 1167 | // If an error occured, return it now. |
| 1118 | 1168 | if ( is_wp_error( $result ) ) { |
| 1169 | + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: graphql_query(): Error: [%s] %s', $result->get_error_code(), $result->get_error_message() ) ); | |
| 1119 | 1170 | return $result; |
| 1120 | 1171 | } |
| 1121 | 1172 | |
| 1122 | - // Parse result. | |
| 1173 | + // Parse the response. | |
| 1123 | 1174 | $response = $this->parse_response( $result ); |
| 1124 | 1175 | |
| 1125 | - // If this is a retry, return the parsed response. | |
| 1126 | - // This prevents an infinite loop of retries when a token refresh fails. | |
| 1127 | - if ( $is_retry ) { | |
| 1128 | - return $response; | |
| 1176 | + // Log any API error, so failures that would otherwise be silent are captured. | |
| 1177 | + if ( is_wp_error( $response ) ) { | |
| 1178 | + $this->base->get_class( 'log' )->add_to_debug_log( sprintf( 'Buffer API: graphql_query(): API returned error: [%s] %s', $response->get_error_code(), $response->get_error_message() ) ); | |
| 1129 | 1179 | } |
| 1130 | 1180 | |
| 1131 | - // If the request was successful, return the response. | |
| 1132 | - if ( ! is_wp_error( $response ) ) { | |
| 1133 | - return $response; | |
| 1134 | - } | |
| 1181 | + return $response; | |
| 1135 | 1182 | |
| 1136 | - // If the error isn't an unauthenticated error, return it. | |
| 1137 | - if ( strtolower( $response->get_error_code() ) !== 'unauthenticated' ) { | |
| 1138 | - return $response; | |
| 1139 | - } | |
| 1140 | - | |
| 1141 | - // Attempt to refresh the token. | |
| 1142 | - $refresh_result = $this->refresh_token(); | |
| 1143 | - | |
| 1144 | - // Bail if the refresh token attempt failed. | |
| 1145 | - if ( is_wp_error( $refresh_result ) ) { | |
| 1146 | - return $refresh_result; | |
| 1147 | - } | |
| 1148 | - | |
| 1149 | - // Attempt the request again, now we have a new access token. | |
| 1150 | - return $this->graphql_query( $query, $variables, true ); | |
| 1151 | - | |
| 1152 | 1183 | } |
| 1153 | 1184 | |
| 1154 | 1185 | /** |
| 1155 | 1186 | * Returns the headers to use in an authenticated GraphQL API request. |
| @@ -1259,10 +1290,20 @@ | ||
| 1259 | 1290 | '403 Forbidden' |
| 1260 | 1291 | ); |
| 1261 | 1292 | } |
| 1262 | 1293 | |
| 1294 | + // Retain-and-retry on server errors: return without touching stored tokens. | |
| 1295 | + if ( $http_code >= 500 ) { | |
| 1296 | + return new \WP_Error( 'buffer_api_server_error', $http_code . ' server error' ); | |
| 1297 | + } | |
| 1298 | + | |
| 1263 | 1299 | // Decode response. |
| 1264 | 1300 | $body = json_decode( $http_body, true ); |
| 1301 | + | |
| 1302 | + // Bail if the response isn't valid JSON. | |
| 1303 | + if ( ! is_array( $body ) ) { | |
| 1304 | + return new \WP_Error( 'buffer_api_invalid_response', 'Invalid API response' ); | |
| 1305 | + } | |
| 1265 | 1306 | |
| 1266 | 1307 | // If an error is detected, return it. |
| 1267 | 1308 | if ( array_key_exists( 'error', $body ) ) { |
| 1268 | 1309 | return new \WP_Error( |