├── composer.json ├── README.md ├── LICENSE └── Client.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "carlalexander/wp-api-client", 3 | "description": "PHP client for the WordPress JSON REST API", 4 | "homepage": "https://carlalexander.ca", 5 | "license": "GPL-3.0+", 6 | "require": { 7 | "php": ">=5.2.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WordPress API Client 2 | 3 | This is a PHP client for the [WordPress JSON REST API](https://github.com/WP-API/WP-API). It's designed to work with WordPress 4 | without the need for an external library. This is done by leveraging the [WordPress HTTP API](http://codex.wordpress.org/HTTP_API) through 5 | the `WP_Http`class. 6 | 7 | ## Background 8 | 9 | The client was created as part of an [article](http://carlalexander.ca/designing-class-wordpress-api-client) on how to design a class. 10 | 11 | ## Current limitations 12 | 13 | This is still a work in progress. The client currently only supports the `get_users` method. You can also only authenticate using 14 | [basic authentication](https://github.com/WP-API/Basic-Auth). 15 | 16 | ## Usage 17 | 18 | ```php 19 | $client = WP_API_Client::create('http://your.wordpress.org', 'your_token'); 20 | 21 | $users = $client->get_users(); 22 | ``` 23 | 24 | ## Bugs 25 | 26 | For bugs or feature requests, please [create an issue](https://github.com/carlalexander/wp-api-client/issues/new). 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Carl Alexander 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Client.php: -------------------------------------------------------------------------------- 1 | http = $http; 64 | $this->base_url = $base_url; 65 | $this->token = $token; 66 | } 67 | 68 | /** 69 | * Retrieve a subset of the site's users. 70 | * 71 | * @param array $filters 72 | * @param string $context 73 | * @param integer $page 74 | * 75 | * @return array|WP_Error 76 | */ 77 | public function get_users(array $filters = array(), $context = 'view', $page = 1) 78 | { 79 | return $this->get($this->build_url(self::ENDPOINT_USERS, array('filter' => $filters, 'context' => $context, 'page' => $page))); 80 | } 81 | 82 | /** 83 | * Adds the response error to the given WP_Error instance. 84 | * 85 | * @param mixed $response 86 | * @param mixed $key 87 | * @param WP_Error $error 88 | */ 89 | private function add_response_error($response, $key, WP_Error $error) 90 | { 91 | if (!is_array($response)) { 92 | return; 93 | } 94 | 95 | $error->add( 96 | isset($response['code']) ? $response['code'] : '', 97 | isset($response['message']) ? $response['message'] : '' 98 | ); 99 | } 100 | 101 | /** 102 | * Builds the WordPress HTTP transport arguments. 103 | * 104 | * @param array $args 105 | * 106 | * @return array 107 | */ 108 | private function build_args(array $args = array()) 109 | { 110 | return array_merge_recursive($args, 111 | array( 112 | 'headers' => array( 113 | 'Authorization' => 'Basic '.$this->token, 114 | ), 115 | )); 116 | } 117 | 118 | /** 119 | * Builds a full API request URL from the given endpoint URL and query string arguments. 120 | * 121 | * @param string $endpoint 122 | * @param array $query 123 | * 124 | * @return string 125 | */ 126 | private function build_url($endpoint, array $query = array()) 127 | { 128 | $url = $this->base_url.$endpoint; 129 | 130 | if (!empty($query)) { 131 | $url .= '?'.http_build_query($query); 132 | } 133 | 134 | return $url; 135 | } 136 | 137 | /** 138 | * Converts the given response to a WP_Error object. 139 | * 140 | * @param array $response 141 | * 142 | * @return WP_Error 143 | */ 144 | private function convert_response_to_error(array $response) 145 | { 146 | $response = $this->decode_response($response); 147 | $error = new WP_Error(); 148 | 149 | if ($response instanceof WP_Error) { 150 | $error = $response; 151 | } elseif (is_array($response)) { 152 | array_walk($response, array($this, 'add_response_error'), $error); 153 | } 154 | 155 | return $error; 156 | } 157 | 158 | /** 159 | * Decodes the JSON object returned in given response. Returns a WP_Error on error. 160 | * 161 | * @param array $response 162 | * 163 | * @return array|WP_Error 164 | */ 165 | private function decode_response(array $response) 166 | { 167 | $decoded = array(); 168 | $headers = $this->get_response_headers($response); 169 | 170 | if (!isset($headers['content-type']) || false === stripos($headers['content-type'], 'application/json')) { 171 | return new WP_Error('invalid_response', 'The content-type of the response needs to be "application/json".'); 172 | } 173 | 174 | if (isset($response['body'])) { 175 | $decoded = json_decode($response['body'], true); 176 | } 177 | 178 | if (null === $decoded) { 179 | return new WP_Error('invalid_json', 'The JSON response couldn\'t be decoded.'); 180 | } 181 | 182 | return $decoded; 183 | } 184 | 185 | /** 186 | * Performs a GET request using the WordPress HTTP transport. Returns a WP_Error 187 | * on error. 188 | * 189 | * @param string $url 190 | * @param array $args 191 | * 192 | * @return array|WP_Error 193 | */ 194 | private function get($url, array $args = array()) 195 | { 196 | $response = $this->http->get($url, $this->build_args($args)); 197 | 198 | if (is_array($response) && $this->is_successful($response)) { 199 | $response = $this->decode_response($response); 200 | } elseif (is_array($response) && !$this->is_successful($response)) { 201 | $response = $this->convert_response_to_error($response); 202 | } 203 | 204 | return $response; 205 | } 206 | 207 | /** 208 | * Extracts the response headers from the given response. 209 | * 210 | * @param array $response 211 | * 212 | * @return array 213 | */ 214 | private function get_response_headers(array $response) 215 | { 216 | if (!isset($response['headers']) || !is_array($response['headers'])) { 217 | return array(); 218 | } 219 | 220 | return $response['headers']; 221 | } 222 | 223 | /** 224 | * Extracts the status code from the given response. 225 | * 226 | * @param array $response 227 | * 228 | * @return int|null 229 | */ 230 | private function get_response_status_code(array $response) 231 | { 232 | if (!isset($response['response']) || !isset($response['response']['code'])) { 233 | return null; 234 | } 235 | 236 | return $response['response']['code']; 237 | } 238 | 239 | /** 240 | * Checks if the given response is considered successful as per the HTTP specification. 241 | * This means that the response has a 2xx status code. 242 | * 243 | * @param array $response 244 | * 245 | * @return bool 246 | */ 247 | private function is_successful(array $response) 248 | { 249 | $status_code = $this->get_response_status_code($response); 250 | 251 | if (null === $status_code) { 252 | return false; 253 | } 254 | 255 | return $status_code >= 200 && $status_code < 300; 256 | } 257 | } 258 | --------------------------------------------------------------------------------