URL = esc_url_raw( $url ); $this->METHOD = $method; $this->BODY = $body; $this->IS_AUTH = $is_auth; $this->OWNER = $owner; $this->data_format = null; $this->JSON_BODY = $is_json_body; $this->SSL_VERIFY = $ssl_verify; $this->_set_user_agent(); $this->prepare_args(); } /** * Only include necessary properties when printing this object using {@link var_dump}. * * @return array */ public function __debugInfo() { return array( 'ARGS' => $this->ARGS, 'URL' => $this->URL, 'METHOD' => $this->METHOD, 'BODY' => $this->BODY, 'IS_AUTH' => $this->IS_AUTH, 'IS_JSON_BODY' => $this->JSON_BODY, 'OWNER' => $this->OWNER, ); } /** * Sets the user agent string. */ private function _set_user_agent() { global $wp_version; $owner = $this->OWNER; $version = 'bloom' === $owner ? $GLOBALS['et_bloom']->plugin_version : ET_CORE_VERSION; if ( 'builder' === $owner ) { $owner = 'Divi Builder'; } if ( '' === $owner ) { $this->OWNER = $owner = 'ET_Core'; } else { $owner = ucfirst( $owner ); } $this->USER_AGENT = "WordPress/{$wp_version}; {$owner}/{$version}; " . esc_url_raw( get_bloginfo( 'url' ) ); } /** * Prepares the request arguments (to be passed to wp_remote_*()) */ public function prepare_args() { $this->ARGS = array( 'blocking' => $this->BLOCKING, 'body' => $this->BODY, 'cookies' => $this->COOKIES, 'headers' => $this->HEADERS, 'method' => $this->METHOD, 'sslverify' => $this->SSL_VERIFY, 'user-agent' => $this->USER_AGENT, 'timeout' => 30, ); } } /** * Simple object to hold HTTP response details. * * @since 1.1.0 * * @package ET\Core\HTTP */ class ET_Core_HTTPResponse { /** * @var array */ public $COOKIES; /** * @var string|array */ public $DATA; /** * @var bool */ public $ERROR = false; /** * The error message if `self::$ERROR` is `true`. * * @var string */ public $ERROR_MESSAGE; /** * @var array */ public $HEADERS; /** * @var array|WP_Error */ public $RAW_RESPONSE; /** * @var ET_Core_HTTPRequest */ public $REQUEST; /** * The response's HTTP status code. * * @var int */ public $STATUS_CODE; /** * @var string */ public $STATUS_MESSAGE; /** * ET_Core_HTTP_Response constructor. * * @param ET_Core_HTTPRequest $request * @param array|WP_Error $response */ public function __construct( $request, $response ) { $this->REQUEST = $request; $this->RAW_RESPONSE = $response; $this->_parse_response(); } /** * Parse response and save relevant details. */ private function _parse_response() { if ( is_wp_error( $this->RAW_RESPONSE ) ) { $this->ERROR = true; $this->ERROR_MESSAGE = $this->RAW_RESPONSE->get_error_message(); $this->STATUS_CODE = $this->RAW_RESPONSE->get_error_code(); $this->STATUS_MESSAGE = $this->ERROR_MESSAGE; return; } $this->DATA = $this->RAW_RESPONSE['body']; $this->HEADERS = $this->RAW_RESPONSE['headers']; $this->COOKIES = $this->RAW_RESPONSE['cookies']; $this->STATUS_CODE = $this->RAW_RESPONSE['response']['code']; $this->STATUS_MESSAGE = $this->RAW_RESPONSE['response']['message']; if ( $this->STATUS_CODE >= 400 ) { $this->ERROR = true; $this->ERROR_MESSAGE = $this->STATUS_MESSAGE; } } /** * Only include necessary properties when printing this object using {@link var_dump}. * * @return array */ public function __debugInfo() { return array( 'STATUS_CODE' => $this->STATUS_CODE, 'STATUS_MESSAGE' => $this->STATUS_MESSAGE, 'ERROR' => $this->ERROR, 'ERROR_MESSAGE' => $this->ERROR_MESSAGE, 'DATA' => $this->DATA, ); } /** * Only include necessary properties when serializing this object for * storage in the WP Transient Cache. * * @return array */ public function __sleep() { return array( 'ERROR', 'ERROR_MESSAGE', 'STATUS_CODE', 'STATUS_MESSAGE', 'DATA' ); } } /** * High level, generic, wrapper for making HTTP requests. It uses WordPress HTTP API under-the-hood. * * @since 1.1.0 * * @package ET\Core\HTTP */ class ET_Core_HTTPInterface { /** * How much time responses are cached (in seconds). * * @since 1.1.0 * @var int */ protected $cache_timeout; /** * @var ET_Core_HTTPRequest */ public $request; /** * @var ET_Core_HTTPResponse */ public $response; /** * ET_Core_API_HTTP_Interface constructor. * * @since 1.1.0 * * @param string $owner The name of the theme/plugin that created this class instance. Default: 'ET_Core'. * @param array $request_details Array of config values for the request. Optional. * @param bool $json Whether or not json responses are expected to be received. Default is `true`. */ public function __construct( $owner = 'ET_Core', $request_details = array(), $json = true ) { $this->expects_json = $json; $this->cache_timeout = 15 * MINUTE_IN_SECONDS; $this->owner = $owner; if ( ! empty( $request_details ) ) { list( $url, $method, $is_auth, $body ) = $request_details; $this->prepare_request( $url, $method, $is_auth, $body ); } } /** * Only include necessary properties when printing this object using {@link var_dump}. * * @return array */ public function __debugInfo() { return array( 'REQUEST' => $this->request, 'RESPONSE' => $this->response, ); } /** * Only include necessary properties when serializing this object for * storage in the WP Transient Cache. * * @return array */ public function __sleep() { return array( 'request', 'response' ); } /** * Creates an identifier key for a request based on the URL and body content. * * @internal * @since 1.1.0 * * @param string $url The request URL. * @param string|string[] $body The request body. * * @return string */ protected static function _get_cache_key_for_request( $url, $body ) { if ( is_array( $body ) ) { $url .= json_encode( $body ); } else if ( ! empty( $body ) ) { $url .= $body; } return 'et-core-http-response-' . md5( $url ); } /** * Writes request/response info to the error log for failed requests. * * @internal * @since 1.1.0 */ protected function _log_failed_request() { ob_start(); var_dump( $this ); $response_details = ob_get_contents(); ob_end_clean(); $class_name = get_class( $this ); $msg_part = "{$class_name} ERROR :: Remote request failed...\n\n"; $msg = "{$msg_part}Details: {$response_details}"; ET_Core_Logger::error( $msg ); } /** * Prepares request to send JSON data. */ protected function _setup_json_request() { $this->request->HEADERS['Accept'] = 'application/json'; if ( $this->request->JSON_BODY ) { $this->request->HEADERS['Content-Type'] = 'application/json'; $is_json = is_string( $this->request->BODY ) && in_array( $this->request->BODY[0], array( '[', '{' ) ); if ( $is_json || null === $this->request->BODY ) { return; } $this->request->BODY = json_encode( $this->request->BODY ); } } /** * Performs a remote HTTP request. Responses are cached for {@see self::$cache_timeout} seconds using * the {@link https://goo.gl/c0FSMH WP Transients API}. * * @since 1.1.0 */ public function make_remote_request() { $response = null; if ( $this->expects_json && ! isset( $this->request->HEADERS['Content-Type'] ) ) { $this->_setup_json_request(); } // Make sure we include any changes made after request object was instantiated. $this->request->prepare_args(); if ( 'POST' === $this->request->METHOD ) { $response = wp_remote_post( $this->request->URL, $this->request->ARGS ); } else if ( 'GET' === $this->request->METHOD && null === $this->request->data_format ) { $response = wp_remote_get( $this->request->URL, $this->request->ARGS ); } else if ( 'GET' === $this->request->METHOD && null !== $this->request->data_format ) { // WordPress sends data as query args for GET and HEAD requests and provides no way // to alter that behavior. Thus, we need to monkey patch it for now. See the mp'd class // for more details. require_once 'lib/WPHttp.php'; $wp_http = new ET_Core_LIB_WPHttp(); $this->request->ARGS['data_format'] = $this->request->data_format; $response = $wp_http->request( $this->request->URL, $this->request->ARGS ); } else if ( 'PUT' === $this->request->METHOD ) { $this->request->ARGS['method'] = 'PUT'; $response = wp_remote_request( $this->request->URL, $this->request->ARGS ); } $this->response = $response = new ET_Core_HTTPResponse( $this->request, $response ); if ( $response->ERROR || defined( 'ET_DEBUG' ) ) { $this->_log_failed_request(); } if ( $this->expects_json ) { $response->DATA = json_decode( $response->DATA, true ); } $this->request->COMPLETE = true; } /** * Replaces the current request object with a new instance. * * @param string $url * @param string $method * @param bool $is_auth * @param mixed? $body * @param bool $json_body * @param bool $ssl_verify */ public function prepare_request( $url, $method = 'GET', $is_auth = false, $body = null, $json_body = false, $ssl_verify = true ) { $this->request = new ET_Core_HTTPRequest( $url, $method, $this->owner, $is_auth, $body, $json_body, $ssl_verify ); } }