Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.39% covered (warning)
54.39%
31 / 57
20.00% covered (danger)
20.00%
1 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_IXR_Client
54.72% covered (warning)
54.72%
29 / 53
20.00% covered (danger)
20.00%
1 / 5
43.84
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 query
70.37% covered (warning)
70.37%
19 / 27
0.00% covered (danger)
0.00%
0 / 1
9.66
 get_jetpack_error
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 get_response_header
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 get_last_response
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * IXR_Client
4 *
5 * @package automattic/jetpack-connection
6 *
7 * @since 1.7.0
8 * @since-jetpack 1.5
9 * @since-jetpack 7.7 Moved to the jetpack-connection package.
10 */
11
12use Automattic\Jetpack\Connection\Client;
13use Automattic\Jetpack\Connection\Manager;
14
15/**
16 * Disable direct access.
17 */
18if ( ! defined( 'ABSPATH' ) ) {
19    exit( 0 );
20}
21
22if ( ! class_exists( IXR_Client::class ) ) {
23    require_once ABSPATH . WPINC . '/class-IXR.php';
24}
25
26/**
27 * A Jetpack implementation of the WordPress core IXR client.
28 */
29class Jetpack_IXR_Client extends IXR_Client {
30    /**
31     * Jetpack args, used for the remote requests.
32     *
33     * @var array
34     */
35    public $jetpack_args = null;
36
37    /**
38     * Remote Response Headers.
39     *
40     * @var array
41     */
42    public $response_headers = null;
43
44    /**
45     * Holds the raw remote response from the latest call to query().
46     *
47     * @var null|array|WP_Error
48     */
49    public $last_response = null;
50
51    /**
52     * Constructor.
53     * Initialize a new Jetpack IXR client instance.
54     *
55     * @param array       $args    Jetpack args, used for the remote requests.
56     * @param string|bool $path    Path to perform the reuqest to.
57     * @param int         $port    Port number.
58     * @param int         $timeout The connection timeout, in seconds.
59     */
60    public function __construct( $args = array(), $path = false, $port = 80, $timeout = 15 ) {
61        $connection = new Manager();
62
63        $defaults = array(
64            'url'     => $connection->xmlrpc_api_url(),
65            'user_id' => 0,
66            'headers' => array(),
67        );
68
69        $args            = wp_parse_args( $args, $defaults );
70        $args['headers'] = array_merge( array( 'Content-Type' => 'text/xml' ), (array) $args['headers'] );
71
72        $this->jetpack_args = $args;
73
74        $this->IXR_Client( $args['url'], $path, $port, $timeout );
75    }
76
77    /**
78     * Perform the IXR request.
79     *
80     * @param mixed ...$args IXR method and args.
81     *
82     * @return bool True if request succeeded, false otherwise.
83     */
84    public function query( ...$args ) {
85        $method  = array_shift( $args );
86        $request = new IXR_Request( $method, $args );
87        $xml     = trim( $request->getXml() );
88
89        $response = Client::remote_request( $this->jetpack_args, $xml );
90
91        // Store response headers.
92        $this->response_headers = wp_remote_retrieve_headers( $response );
93
94        $this->last_response = $response;
95        if ( is_array( $this->last_response ) && isset( $this->last_response['http_response'] ) ) {
96            // If the expected array response is received, format the data as plain arrays.
97            $this->last_response            = $this->last_response['http_response']->to_array();
98            $this->last_response['headers'] = $this->last_response['headers']->getAll();
99        }
100
101        if ( is_wp_error( $response ) ) {
102            $this->error = new IXR_Error( -10520, sprintf( 'Jetpack: [%s] %s', $response->get_error_code(), $response->get_error_message() ) );
103            return false;
104        }
105
106        if ( ! $response ) {
107            $this->error = new IXR_Error( -10520, 'Jetpack: Unknown Error' );
108            return false;
109        }
110
111        if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
112            $this->error = new IXR_Error( -32300, 'transport error - HTTP status code was not 200' );
113            return false;
114        }
115
116        $content = wp_remote_retrieve_body( $response );
117
118        // Now parse what we've got back.
119        $this->message = new IXR_Message( $content );
120        if ( ! $this->message->parse() ) {
121            // XML error.
122            $this->error = new IXR_Error( -32700, 'parse error. not well formed' );
123            return false;
124        }
125
126        // Is the message a fault?
127        if ( 'fault' === $this->message->messageType ) {
128            $this->error = new IXR_Error( $this->message->faultCode, $this->message->faultString );
129            return false;
130        }
131
132        // Message must be OK.
133        return true;
134    }
135
136    /**
137     * Retrieve the Jetpack error from the result of the last request.
138     *
139     * @param int    $fault_code   Fault code.
140     * @param string $fault_string Fault string.
141     * @return WP_Error Error object.
142     */
143    public function get_jetpack_error( $fault_code = null, $fault_string = null ) {
144        if ( $fault_code === null ) {
145            $fault_code = $this->error->code;
146        }
147
148        if ( $fault_string === null ) {
149            $fault_string = $this->error->message;
150        }
151
152        if ( preg_match( '#jetpack:\s+\[(\w+)\]\s*(.*)?$#i', $fault_string, $match ) ) {
153            $code    = $match[1];
154            $message = $match[2];
155            $status  = $fault_code;
156            return new WP_Error( $code, $message, $status );
157        }
158
159        return new WP_Error( "IXR_{$fault_code}", $fault_string );
160    }
161
162    /**
163     * Retrieve a response header if set.
164     *
165     * @param  string $name  header name.
166     * @return string|bool Header value if set, false if not set.
167     */
168    public function get_response_header( $name ) {
169        if ( isset( $this->response_headers[ $name ] ) ) {
170            return $this->response_headers[ $name ];
171        }
172        // case-insensitive header names: http://www.ietf.org/rfc/rfc2616.txt.
173        if ( isset( $this->response_headers[ strtolower( $name ) ] ) ) {
174            return $this->response_headers[ strtolower( $name ) ];
175        }
176        return false;
177    }
178
179    /**
180     * Retrieve the raw response for the last query() call.
181     *
182     * @return null|array|WP_Error
183     */
184    public function get_last_response() {
185        return $this->last_response;
186    }
187}