├── ApiExceptions.class.php ├── ApiRequest.class.php ├── ApiResponse.class.php ├── README.md ├── apiclient.php └── demo ├── Area.class.php └── config.php /ApiExceptions.class.php: -------------------------------------------------------------------------------- 1 | message}\n"; 23 | } 24 | } 25 | 26 | // 400 Bad request when there was a problem with the request 27 | class BadRequestException extends Exception { 28 | // Redefine the exception so message isn't optional 29 | public function __construct($Url, $Response) { 30 | // some code 31 | $message = "

400 HTTP Error: Error bad request to $Url
Details: {$Response['details']}
Problem: {$Response['problem']}

"; 32 | 33 | // make sure everything is assigned properly 34 | parent::__construct($message, E_USER_ERROR, null); 35 | } 36 | 37 | // custom string representation of object 38 | public function __toString() { 39 | return __CLASS__ . ": {$this->message}\n"; 40 | } 41 | } 42 | 43 | 44 | class UnauthorizedRequestException extends Exception { 45 | // Redefine the exception so message isn't optional 46 | public function __construct() { 47 | // some code 48 | $message = "

401 HTTP Error: Error not authorized. Please check your API credentials

"; 49 | 50 | // make sure everything is assigned properly 51 | parent::__construct($message, E_USER_ERROR, null); 52 | } 53 | 54 | // custom string representation of object 55 | public function __toString() { 56 | return __CLASS__ . ": {$this->message}\n"; 57 | } 58 | } 59 | 60 | class InternalServerErrorException extends Exception { 61 | // Redefine the exception so message isn't optional 62 | public function __construct() { 63 | // some code 64 | $message = "

500 HTTP Error: Internal server error. The servers are currently experiencing difficulty

"; 65 | 66 | // make sure everything is assigned properly 67 | parent::__construct($message, E_USER_ERROR, null); 68 | } 69 | 70 | // custom string representation of object 71 | public function __toString() { 72 | return __CLASS__ . ": {$this->message}\n"; 73 | } 74 | } 75 | 76 | class UnknownErrorException extends Exception { 77 | // Redefine the exception so message isn't optional 78 | public function __construct( $Error = '') { 79 | 80 | if(is_wp_error( $Error ) ) { 81 | // Get all the errors WordPress returned 82 | $errors = $Error->errors; 83 | $Error = ''; // Reset the error message 84 | foreach( $errors AS $k => $v ) { 85 | $Error .= "Error $k: (" . implode(',', $v) . ")"; 86 | } 87 | } 88 | // some code 89 | $message = "An unknown error has occured while talking to the API: $Error"; 90 | 91 | // make sure everything is assigned properly 92 | parent::__construct($message, E_USER_ERROR, null); 93 | } 94 | 95 | // custom string representation of object 96 | public function __toString() { 97 | return __CLASS__ . ": {$this->message}\n"; 98 | } 99 | } 100 | 101 | class HttpRequestFailedException extends Exception { 102 | // Redefine the exception so message isn't optional 103 | public function __construct( $Url ) { 104 | // some code 105 | $message = "The provided URL failed to respond: $Url"; 106 | 107 | if( '' == $Url ) $message = "No URL provided!"; 108 | 109 | // make sure everything is assigned properly 110 | parent::__construct($message, E_USER_ERROR, null); 111 | } 112 | 113 | // custom string representation of object 114 | public function __toString() { 115 | return __CLASS__ . ": {$this->message}\n"; 116 | } 117 | } -------------------------------------------------------------------------------- /ApiRequest.class.php: -------------------------------------------------------------------------------- 1 | 'json', 34 | 'cache_enabled' => false, 35 | 'cache_lifetime' => 60, // In seconds. Default is 1 minute 36 | 'cache_prefix' => 'api_request_', 37 | 'check_for_update' => true, // Checks HTTP HEAD to see if there are updates since last cache 38 | 'force_caching' => false // If true the cache parameters in HEAD will be overridden by these config settings 39 | ); 40 | $Config = wp_parse_args( $Config, $default_config ); 41 | 42 | if( !$this->verifyParameters( $required_config, $Config ) ) 43 | throw new InvalidParametersException( $required_config ); 44 | 45 | $this->setConfig( $Config ); 46 | } 47 | 48 | /** 49 | * Performs the GET query against the specified endpoint 50 | * 51 | * @todo Add conditional code to fetch header data first 52 | * @throws UnkownErrorException 53 | * @throws BadRequestException 54 | * @throws UnauthorizedRequestException 55 | * @throws InternalServerErrorException 56 | * @param String $Url - Endpoint with URL paramters (for now) 57 | * @return ApiResponse 58 | */ 59 | public function get( $Url ) { 60 | /* 61 | * Several things to check before we go fetch the data 62 | * 63 | * - Is caching enabled? 64 | * --- Yes? Check to see if the transient is still valid 65 | * ------ Yes? Set it as the response 66 | * --------- Is check_for_update? (HTTP HEAD) 67 | * ------------ Boolean: fetch_url = false 68 | * ------------ Yes? Check HTTP HEAD 69 | * --------------- If timestamp newer than transient 70 | * ------------------ fetch_url = true 71 | * --------- If fetch_url 72 | * ------------ Go fetch!, Set transient 73 | * ------ No? Go fetch!, Set transient 74 | * --- No? Go fetch! 75 | * - Return response 76 | * 77 | * To make this easier to read some of the conditionals may be broken 78 | * apart in a not as efficient manner as possible 79 | */ 80 | 81 | if( $this->getConfig( 'cache_enabled' ) ) { 82 | $transient_name = $this->transientName( $Url ); 83 | 84 | // Grab the cached data 85 | $transient = get_transient( $transient_name ); 86 | 87 | if( !$transient ) { 88 | // Cached data is no longer valid refresh it 89 | $response = $this->performGet( $Url ); 90 | 91 | if( $response->isCachable() || $this->getConfig( 'force_caching' )) 92 | set_transient( $transient_name, $response, $this->getConfig( 'cache_lifetime' )); 93 | } else { 94 | // Cached data is valid do we need to check the HTTP HEAD data? 95 | if( $this->getConfig( 'check_for_update' )) { 96 | // Need to check HTTP HEAD to see if there are any updates 97 | // or if the expires heading has been exceeded 98 | $head = $this->performHead( $Url ); 99 | 100 | if( $head->isExpired() ) { 101 | // Cache is expired. Grab the data again. 102 | $response = $this->performGet( $Url ); 103 | 104 | if( $response->isCachable() || $this->getConfig( 'force_caching' )) { 105 | // Response is cachable (HTTP HEAD) or force_caching option is true 106 | set_transient( $transient_name, $response, $this->getConfig( 'cache_lifetime' )); 107 | } 108 | } 109 | } else { 110 | $response = $transient; 111 | } 112 | } 113 | } else { 114 | // Caching is not enabled. Always perform the GET 115 | $response = $this->performGet( $Url ); 116 | } 117 | 118 | return $response; 119 | } 120 | 121 | private function transientName( $Name ) { 122 | return $this->getConfig( 'cache_prefix' ) . md5($Name); 123 | } 124 | 125 | // Helper function 126 | private function performGet( $Url ) { 127 | $res = wp_remote_get( $Url ); 128 | //echo '
';var_dump( wp_remote_retrieve_headers($res));
129 |         if( is_wp_error( $res ) ) {
130 |           //  ( var_dump( ($res->errors) ));
131 |             
132 |             if( count( $res->errors ) == 1 ) {
133 |                 // If there was only 1 error let's see if I can deal with it
134 |                 $error = array_keys( $res->errors );
135 |                 $error = $error[0];
136 |                 
137 |                 switch( $error ) {
138 |                     case 'http_request_failed':
139 |                         throw new HttpRequestFailedException( $Url );
140 |                     default:
141 |                         // WordPress had some error that I do not know about
142 |                         throw new UnknownErrorException( $res );
143 |                 }
144 |                 echo '1 error';
145 |             } else {
146 |                 // WordPress had some error that I do not know about
147 |                 throw new UnknownErrorException( $res );
148 |             }
149 |             die();
150 |         } else {            
151 |             $response = new ApiResponse();
152 |             $response->setHttpCode( wp_remote_retrieve_response_code( $res ) );
153 |             $response->setHeaders( wp_remote_retrieve_headers($res) );
154 |             $response->setResponse( wp_remote_retrieve_body( $res ) );
155 |             if( $response->getHttpCode() == '400' ) {
156 |                 // 400 Bad request when there was a problem with the request
157 |                 throw new BadRequestException($Url, $response);
158 |             } else if ( $response->getHttpCode() == '401' ) {
159 |                 // 401 Unauthorized when you don't provide a valid key
160 |                 throw new UnauthorizedRequestException();
161 |             } else if ( $response->getHttpCode() == '500' ) {
162 |                 // 500 Internal Server Error
163 |                 throw new InternalServerErrorException();
164 |             }
165 |         
166 |             return $response;
167 |         }
168 |     }
169 |     
170 |     private function performHead( $Url ) {
171 |         $res = wp_remote_head( $Url );
172 |         //echo '
';var_dump( wp_remote_retrieve_headers($res));
173 |         if( is_wp_error( $res ) ) {
174 |             // WordPress had some error that I do not know about
175 |             throw new UnknownErrorException( $res );
176 |         } else {            
177 |             $response = new ApiResponse();
178 |             $response->setHttpCode( wp_remote_retrieve_response_code( $res ) );
179 |             $response->setHeaders( wp_remote_retrieve_headers($res) );
180 |            // $response->setResponse( wp_remote_retrieve_body( $res ) );
181 |             if( $response->getHttpCode() == '400' ) {
182 |                 // 400 Bad request when there was a problem with the request
183 |                 throw new BadRequestException($Url, $response);
184 |             } else if ( $response->getHttpCode() == '401' ) {
185 |                 // 401 Unauthorized when you don't provide a valid key
186 |                 throw new UnauthorizedRequestException();
187 |             } else if ( $response->getHttpCode() == '500' ) {
188 |                 // 500 Internal Server Error
189 |                 throw new InternalServerErrorException();
190 |             }
191 |         
192 |             return $response;
193 |         }
194 |     }
195 | 
196 |     /**
197 |      *
198 |      * @throws InvalidParametersException
199 |      * @param  $Endpoint
200 |      * @param  $Parameters
201 |      * @param  $RequiredParameters - Optional - Some endpoints do not have parameters
202 |      * @return 
203 |      */
204 |     public function buildUrl( $Endpoint, $Parameters = null, $RequiredParameters = null ) {
205 |         if(is_array($RequiredParameters) && !$this->verifyParameters( $RequiredParameters, $Parameters )) {
206 |             throw new InvalidParametersException( $RequiredParameters );
207 |         }
208 |         
209 |         $url = trailingslashit( $this->getConfig( 'api_entry' ) ) . $Endpoint;
210 |         
211 |         if( !is_null( $Parameters ) )
212 |             $url = add_query_arg( $Parameters, $url );
213 |         
214 |         return $url;
215 |     }
216 | 
217 |     /**
218 |      * Checks the input parameters against a list of required parameters to
219 |      * ensure at least one of the required parameters exists.
220 |      *
221 |      * NOTE: The Meetup API contains a list of parameters that are required for
222 |      * each endpoint with a default condition of "any of"
223 |      * 
224 |      * @param Array $RequiredList - Names of required parameters
225 |      * @param Array $Parameters - List of provided paramters
226 |      * @return Boolean
227 |      */
228 |     public function verifyParameters($RequiredList, $Parameters) {
229 |         if( !is_array( $Parameters ) ) return false;
230 |         $Parameters = array_keys($Parameters);
231 | 
232 |         /*
233 |          * Check to see if any of the required list is in the parameters array
234 |          * Since the Meetup API requires "any of" if a required key is found in
235 |          * parameters the verification will pass
236 |          */
237 |         foreach($RequiredList AS $r) {
238 |             if(in_array($r, $Parameters)) {
239 |                 return true;
240 |             }
241 |         }
242 |         return false;
243 |     }
244 |     
245 |     public function getConfig( $Var ) {
246 |         return ( isset( $this->mConfig[$Var] ) )? $this->mConfig[$Var] : null;
247 |     }
248 |     
249 |     /**
250 |      *Recursive function
251 |      * @param type $Var
252 |      * @param type $Value 
253 |      */
254 |     public function setConfig( $Var, $Value = null ) {
255 |         if(is_array( $Var ) ) {
256 |             foreach( $Var AS $k => $v ) {
257 |                 $this->setConfig( $k, $v );
258 |             }
259 |         } else {
260 |             $this->mConfig[$Var] = $Value;
261 |         }
262 |     }
263 | 
264 | } // end class
265 | 


--------------------------------------------------------------------------------
/ApiResponse.class.php:
--------------------------------------------------------------------------------
  1 | 
  8 |  * @license Nothing yet
  9 |  */
 10 | class ApiResponse implements ArrayAccess, Iterator{
 11 | 
 12 |     private $mHttpCode;
 13 | 
 14 |     private $mResponse;
 15 |     
 16 |     private $mHeaders;
 17 | 
 18 |     private $mError = false;
 19 |     
 20 | 
 21 |     public function __toString() {
 22 |         return "HTTP Code: " . $this->mHttpCode . "
Is Error: " . $this->mError . "
Response: " . $this->mResponse; 23 | } 24 | 25 | /** 26 | * Sets the HTTP response code from the API call 27 | * @param Integer $Code 28 | */ 29 | public function setHttpCode( $Code ) { 30 | $this->mHttpCode = $Code; 31 | 32 | // If the response was not a 200 there was an error. Set the error code 33 | if( '200' != $Code ) { 34 | $this->mError = true; 35 | } 36 | } 37 | 38 | /** 39 | * Returns the HTTP response code from the API query 40 | * @return Integer 41 | */ 42 | public function getHttpCode() { 43 | return $this->mHttpCode; 44 | } 45 | 46 | // Must be an array 47 | public function setHeaders( $Headers ) { 48 | $this->mHeaders = $Headers; 49 | } 50 | 51 | public function getHeader( $Header ) { 52 | return ( isset($this->mHeaders[$Header]) ) ?$this->mHeaders[$Header]: null; 53 | } 54 | 55 | public function getHeaders() { 56 | return $this->mHeaders; 57 | } 58 | 59 | public function isCachable() { 60 | // If header is not set then allow it 61 | // pragma - cache-control(explode) 62 | if( $this->getHeader( 'pragma' ) == 'no-cache' ) { 63 | // pragma no-cache is set 64 | return false; 65 | } 66 | 67 | $arr = explode( ',', $this->getHeader( 'cache-control' ) ); 68 | foreach( $arr AS $k => $v ) $arr[$k] = trim($v); 69 | if( in_array( 'no-cache', $arr )) { 70 | // cache-control no-cache is set 71 | return false; 72 | } 73 | 74 | return true; // By default it is cachable 75 | } 76 | 77 | public function isExpired() { 78 | // If header is not set then always expired? 79 | if( !is_null( $this->getHeader( 'expires' ) )) { 80 | $expires = strtotime( $this->getHeader( 'expires' )); 81 | return ( time() > $expires ); 82 | } 83 | return false; // By default never expires 84 | } 85 | 86 | /** 87 | * Brings in the response from the API call and converts it to a stdClass 88 | * 89 | * @param JSON $Response 90 | */ 91 | public function setResponse($Response) { 92 | $this->mResponse = json_decode(preg_replace('/[\x00-\x1F\x80-\xFF]/', '',$Response), true); 93 | //dBug($this->mResponse); 94 | } 95 | 96 | public function getResponse() { 97 | return $this->mResponse; 98 | } 99 | 100 | /** 101 | * Check if there was an error with the API query 102 | * 103 | * @return Boolean 104 | */ 105 | public function isError() { 106 | return $this->mError; 107 | } 108 | 109 | 110 | /* 111 | * ************************************************************************ 112 | * Below this point are overloaded PHP functions to enable arraylike access 113 | * You should not need to change anything beyond this line 114 | * ************************************************************************ 115 | */ 116 | 117 | /* 118 | * Array Access methods 119 | * Used directly from PHP docs 120 | * http://php.net/manual/en/class.arrayaccess.php 121 | */ 122 | public function offsetSet($offset, $value) { 123 | if (is_null($offset)) { 124 | $this->mResponse[] = $value; 125 | } else { 126 | $this->mResponse[$offset] = $value; 127 | } 128 | } 129 | public function offsetExists($offset) { 130 | return isset($this->mResponse[$offset]); 131 | } 132 | public function offsetUnset($offset) { 133 | unset($this->mResponse[$offset]); 134 | } 135 | public function offsetGet($offset) { 136 | return isset($this->mResponse[$offset]) ? $this->mResponse[$offset] : null; 137 | } 138 | 139 | /* 140 | * Iterator methods 141 | * Used directly from PHP docs 142 | * http://php.net/manual/en/language.oop5.iterations.php 143 | */ 144 | public function rewind() { 145 | reset($this->mResponse); 146 | } 147 | 148 | public function current() { 149 | return current($this->mResponse); 150 | } 151 | 152 | public function key() { 153 | return key($this->mResponse); 154 | } 155 | 156 | public function next() { 157 | return next($this->mResponse); 158 | } 159 | 160 | public function valid() { 161 | $key = key($this->mResponse); 162 | return ($key !== NULL && $key !== FALSE); 163 | } 164 | 165 | } // end class -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WordPress-API-Client 2 | ==================== 3 | 4 | An API client library for WordPress. Utilizes the WordPress HTTP API 5 | 6 | This client is fully function with regards to GETting resources, but does not POST. 7 | 8 | All the code was tossed up here sort of in progress so there is no real documentation to speak of yet. 9 | There is a demo folder with a couple files to give you an idea of how it works, however the files 10 | themselves are part of a different project and were only pulled in as a quick example until a real 11 | demo can be built. -------------------------------------------------------------------------------- /apiclient.php: -------------------------------------------------------------------------------- 1 | $Region ); 22 | if(is_array( $AdditionalParams ) ) $params = array_merge ( $params, $AdditionalParams ); 23 | 24 | //die( var_dump($params )); 25 | $url = $this->buildUrl( AGENT_API_AREA, $params ); 26 | 27 | $response = $this->get( $url )->getResponse(); 28 | 29 | // Make sure there are some results to display 30 | if( count( $response['results'] ) == 0 ) $response = null; 31 | 32 | return $response; 33 | } 34 | 35 | 36 | /** 37 | * Returns a list of all available regions 38 | * @return Mixed - Array or NULL 39 | */ 40 | public function getRegions() { 41 | $url = $this->buildUrl( AGENT_API_AREA ); 42 | 43 | $response = $this->get( $url )->getResponse(); 44 | 45 | // Make sure there are some results to display 46 | if( !isset( $response['meta'] ) ) return null; 47 | 48 | $response = $response['meta']['areas']; 49 | 50 | return $response; 51 | } 52 | } // end class -------------------------------------------------------------------------------- /demo/config.php: -------------------------------------------------------------------------------- 1 | 'http://myapi.com', 7 | 'cache_enabled' => true, 8 | 'cache_lifetime' => 600, // In seconds 9 | 'check_for_update' => false, 10 | 'force_caching' => true 11 | ); --------------------------------------------------------------------------------