| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* Datafeedr Api Client Library. |
| 5 |
* |
| 6 |
* @version 0.1b.7382 |
| 7 |
* @copyright Datafeedr 2007 ~ 2014 - All Rights Reserved |
| 8 |
* |
| 9 |
* @mainpage |
| 10 |
* |
| 11 |
* Example of use: |
| 12 |
* |
| 13 |
* @code |
| 14 |
* $api = new DatafeedrApi('<access id>', '<secret key>'); |
| 15 |
* |
| 16 |
* foreach($api->getMerchants() as $merchant) |
| 17 |
* print $merchant['name']; |
| 18 |
* |
| 19 |
* $search = $api->searchRequest(); |
| 20 |
* $search->addFilter("name LIKE shoe"); |
| 21 |
* $search->addFilter("price < 100"); |
| 22 |
* $search->addSort("price", DatafeedrApi::SORT_ASCENDING); |
| 23 |
* $search->setLimit(25); |
| 24 |
* |
| 25 |
* $products = $search->execute(); |
| 26 |
* |
| 27 |
* foreach($products as $product) |
| 28 |
* print $product['name']; |
| 29 |
* |
| 30 |
* @endcode |
| 31 |
* |
| 32 |
* |
| 33 |
* |
| 34 |
**/ |
| 35 |
|
| 36 |
if(!class_exists('DatafeedrApi', false)) { |
| 37 |
|
| 38 |
/** |
| 39 |
* Datafeedr API core class. |
| 40 |
**/ |
| 41 |
class DatafeedrApi |
| 42 |
{ |
| 43 |
protected $_accessId; |
| 44 |
protected $_secretKey; |
| 45 |
|
| 46 |
protected $_transport; |
| 47 |
protected $_timeout; |
| 48 |
protected $_url; |
| 49 |
protected $_returnObjects; |
| 50 |
|
| 51 |
protected $_status; |
| 52 |
|
| 53 |
const SORT_DESCENDING = -1; |
| 54 |
const SORT_ASCENDING = +1; |
| 55 |
|
| 56 |
const DEFAULT_URL = 'http://api.datafeedr.com'; |
| 57 |
const DEFAULT_TIMEOUT = 30; |
| 58 |
|
| 59 |
const VERSION = '0.1b.7382'; |
| 60 |
|
| 61 |
/** |
| 62 |
* Constructor. |
| 63 |
* |
| 64 |
* @param string $accessId Access ID. |
| 65 |
* @param object $secretKey Secret key. |
| 66 |
* @param string|callable $transport (optional) HTTP transport function. |
| 67 |
* @param int $timeout (optional) HTTP connection timeout, in seconds. |
| 68 |
* @param bool $returnObjects (optional) if TRUE, responses are objects, otherwise associative arrays. |
| 69 |
* |
| 70 |
* The optional $transport parameter tells how HTTP requests should be made. |
| 71 |
* It can be either a string that describes one of built-in transports ("curl", "file" or "socket"), |
| 72 |
* or a callable object that should accept an url, an array of headers and a string of post data and |
| 73 |
* should return an array [int http response status, string response body]. |
| 74 |
* |
| 75 |
**/ |
| 76 |
public function __construct($accessId, $secretKey, $transport = 'curl', $timeout = 0, $returnObjects = FALSE) { |
| 77 |
$this->_accessId = $accessId; |
| 78 |
$this->_secretKey = $secretKey; |
| 79 |
|
| 80 |
$this->_errors = array( |
| 81 |
1 => 'DatafeedrBadRequestError', |
| 82 |
2 => 'DatafeedrAuthenticationError', |
| 83 |
3 => 'DatafeedrLimitExceededError', |
| 84 |
4 => 'DatafeedrQueryError', |
| 85 |
7 => 'DatafeedrExternalError', |
| 86 |
9 => 'DatafeedrServerError', |
| 87 |
); |
| 88 |
|
| 89 |
$this->_url = self::DEFAULT_URL; |
| 90 |
$this->_timeout = $timeout ? $timeout : self::DEFAULT_TIMEOUT; |
| 91 |
$this->_returnObjects = $returnObjects; |
| 92 |
|
| 93 |
switch($transport) { |
| 94 |
case 'curl': |
| 95 |
$this->_transport = array($this, '_transportCurl'); |
| 96 |
break; |
| 97 |
case 'file': |
| 98 |
$this->_transport = array($this, '_transportFile'); |
| 99 |
break; |
| 100 |
case 'socket': |
| 101 |
$this->_transport = array($this, '_transportSocket'); |
| 102 |
break; |
| 103 |
default: |
| 104 |
if(!is_callable($transport)) { |
| 105 |
throw new DatafeedrError("Transport must be a function"); |
| 106 |
} |
| 107 |
$this->_transport = $transport; |
| 108 |
} |
| 109 |
} |
| 110 |
|
| 111 |
/** |
| 112 |
* Return status information. |
| 113 |
* |
| 114 |
* @return array |
| 115 |
* |
| 116 |
**/ |
| 117 |
public function getStatus() { |
| 118 |
$this->apiCall('status'); |
| 119 |
return $this->_status; |
| 120 |
} |
| 121 |
|
| 122 |
/** |
| 123 |
* Return status information from the last request. |
| 124 |
* |
| 125 |
* If no Api request have been made, return NULL |
| 126 |
* |
| 127 |
* @return array|null |
| 128 |
* |
| 129 |
**/ |
| 130 |
public function lastStatus() { |
| 131 |
return $this->_status; |
| 132 |
} |
| 133 |
|
| 134 |
/** |
| 135 |
* Return the list of networks. |
| 136 |
* |
| 137 |
* @param int|array $networkId (optional) Network id or an array of network ids |
| 138 |
* @param bool $includeEmpty (optional) If FALSE, omit networks with 0 products |
| 139 |
* @param array $fields (optional) list of fields to retrieve |
| 140 |
* @return array |
| 141 |
* |
| 142 |
**/ |
| 143 |
public function getNetworks($networkId = NULL, $includeEmpty = FALSE, $fields = NULL) { |
| 144 |
$request = array(); |
| 145 |
if($networkId) { |
| 146 |
$request['_ids'] = $this->_intarray($networkId); |
| 147 |
} |
| 148 |
$request['skip_empty'] = intval(!$includeEmpty); |
| 149 |
if($fields) { |
| 150 |
$request['fields'] = $fields; |
| 151 |
} |
| 152 |
$response = $this->apiCall('networks', $request); |
| 153 |
return $this->_get($response, 'networks'); |
| 154 |
} |
| 155 |
|
| 156 |
/** |
| 157 |
* Return the list of merchants. |
| 158 |
* |
| 159 |
* @param int|array $networkId (optional) Network id or array of network ids |
| 160 |
* @param bool $includeEmpty (optional) If FALSE, omit merchants with 0 products |
| 161 |
* @param array $fields (optional) list of fields to retrieve |
| 162 |
* @return array |
| 163 |
* |
| 164 |
**/ |
| 165 |
public function getMerchants($networkId = NULL, $includeEmpty = FALSE, $fields = NULL) { |
| 166 |
$request = array(); |
| 167 |
if($networkId) { |
| 168 |
$request['source_ids'] = $this->_intarray($networkId); |
| 169 |
} |
| 170 |
$request['skip_empty'] = intval(!$includeEmpty); |
| 171 |
if($fields) { |
| 172 |
$request['fields'] = $fields; |
| 173 |
} |
| 174 |
$response = $this->apiCall('merchants', $request); |
| 175 |
return $this->_get($response, 'merchants'); |
| 176 |
} |
| 177 |
|
| 178 |
/** |
| 179 |
* Return the list of merchants by their ids. |
| 180 |
* |
| 181 |
* @param int|array $merchantId Merchant id or array of network ids |
| 182 |
* @param bool $includeEmpty (optional) If FALSE, omit merchants with 0 products |
| 183 |
* @param array $fields (optional) list of fields to retrieve |
| 184 |
* @return array |
| 185 |
* |
| 186 |
**/ |
| 187 |
public function getMerchantsById($merchantId, $includeEmpty = FALSE, $fields = NULL) { |
| 188 |
$request = array(); |
| 189 |
$request['_ids'] = $this->_intarray($merchantId); |
| 190 |
$request['skip_empty'] = intval(!$includeEmpty); |
| 191 |
if($fields) { |
| 192 |
$request['fields'] = $fields; |
| 193 |
} |
| 194 |
$response = $this->apiCall('merchants', $request); |
| 195 |
return $this->_get($response, 'merchants'); |
| 196 |
} |
| 197 |
|
| 198 |
/** |
| 199 |
* Return the list of searchable fields. |
| 200 |
* |
| 201 |
* @param int|array $networkId (optional) Network id or array of network ids |
| 202 |
* @return array |
| 203 |
* |
| 204 |
**/ |
| 205 |
public function getFields($networkId = NULL) { |
| 206 |
$request = array(); |
| 207 |
if($networkId) { |
| 208 |
$request['source_ids'] = $this->_intarray($networkId); |
| 209 |
} |
| 210 |
$response = $this->apiCall('fields', $request); |
| 211 |
return $this->_get($response, 'fields'); |
| 212 |
} |
| 213 |
|
| 214 |
/** |
| 215 |
* Return the list of products by their ids. |
| 216 |
* |
| 217 |
* @param int|array $productId Product id or an array of products ids. |
| 218 |
* @param array $fields (optional) list of fields to retrieve. |
| 219 |
* @return array |
| 220 |
* |
| 221 |
**/ |
| 222 |
public function getProducts($productId, $fields = NULL) { |
| 223 |
$request = array(); |
| 224 |
$request['_ids'] = $this->_intarray($productId); |
| 225 |
$request['string_ids'] = 1; |
| 226 |
if($fields) { |
| 227 |
$request['fields'] = $fields; |
| 228 |
} |
| 229 |
$response = $this->apiCall('get', $request); |
| 230 |
return $this->_get($response, 'products'); |
| 231 |
} |
| 232 |
|
| 233 |
/** |
| 234 |
* Return the list of Zanox merchant ids ("zmids"). |
| 235 |
* |
| 236 |
* @param int|array $merchantId Merchant id or an array of merchant ids. |
| 237 |
* @param int $adspaceId Zanox adspace Id. |
| 238 |
* @param string $connectId Zanox connection Id. |
| 239 |
* @return array |
| 240 |
* |
| 241 |
**/ |
| 242 |
public function getZanoxMerchantIds($merchantId, $adspaceId, $connectId) { |
| 243 |
$request = array(); |
| 244 |
$request['merchant_ids'] = $this->_intarray($merchantId); |
| 245 |
$request['adspace_id'] = $adspaceId; |
| 246 |
$request['connect_id'] = $connectId; |
| 247 |
$response = $this->apiCall('zanox_merchant_ids', $request); |
| 248 |
return $this->_get($response, 'zanox_merchant_ids'); |
| 249 |
} |
| 250 |
|
| 251 |
/** |
| 252 |
* Create a new DatafeedrSearchRequest object. |
| 253 |
* |
| 254 |
* @return DatafeedrSearchRequest |
| 255 |
* |
| 256 |
**/ |
| 257 |
public function searchRequest() { |
| 258 |
return new DatafeedrSearchRequest($this); |
| 259 |
} |
| 260 |
|
| 261 |
/** |
| 262 |
* Create a new DatafeedrMerchantSearchRequest object. |
| 263 |
* |
| 264 |
* @return DatafeedrMerchantSearchRequest |
| 265 |
* |
| 266 |
**/ |
| 267 |
public function merchantSearchRequest() { |
| 268 |
return new DatafeedrMerchantSearchRequest($this); |
| 269 |
} |
| 270 |
|
| 271 |
/** |
| 272 |
* Create a new DatafeedrAmazonSearchRequest object. |
| 273 |
* |
| 274 |
* @return DatafeedrAmazonSearchRequest |
| 275 |
* |
| 276 |
**/ |
| 277 |
public function amazonSearchRequest($awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale="US") { |
| 278 |
return new DatafeedrAmazonSearchRequest($this, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale); |
| 279 |
} |
| 280 |
|
| 281 |
/** |
| 282 |
* Create a new DatafeedrAmazonLookupRequest object. |
| 283 |
* |
| 284 |
* @return DatafeedrAmazonLookupRequest |
| 285 |
* |
| 286 |
**/ |
| 287 |
public function amazonLookupRequest($awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale="US") { |
| 288 |
return new DatafeedrAmazonLookupRequest($this, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale); |
| 289 |
} |
| 290 |
|
| 291 |
/** |
| 292 |
* Perform the raw Api call. |
| 293 |
* |
| 294 |
* @param string $action Api action. |
| 295 |
* @param array $request (optional) Request data. |
| 296 |
* |
| 297 |
* @return array |
| 298 |
* |
| 299 |
**/ |
| 300 |
public function apiCall($action, $request = NULL) { |
| 301 |
if(!$request) { |
| 302 |
$request = array(); |
| 303 |
} |
| 304 |
|
| 305 |
$request['aid'] = $this->_accessId; |
| 306 |
$request['timestamp'] = gmdate('Y-m-d H:i:s'); |
| 307 |
|
| 308 |
$message = $request['aid'] .$action . $request['timestamp']; |
| 309 |
$request['signature'] = hash_hmac('sha256', $message, $this->_secretKey, FALSE); |
| 310 |
|
| 311 |
$postdata = json_encode($request); |
| 312 |
$url = $this->_url . '/' . $action; |
| 313 |
$headers = array( |
| 314 |
'Content-Type: application/json', |
| 315 |
'Accept: application/json', |
| 316 |
'Content-Length: '. strlen($postdata), |
| 317 |
'Connection: close', |
| 318 |
'User-Agent: datafeedr.php.' . self::VERSION |
| 319 |
); |
| 320 |
list($status, $response) = call_user_func($this->_transport, $url, $headers, $postdata); |
| 321 |
if(strlen($response)) { |
| 322 |
$response = json_decode($response, !$this->_returnObjects); |
| 323 |
} |
| 324 |
|
| 325 |
$error = $this->_get($response, 'error'); |
| 326 |
if($error) { |
| 327 |
$type = $this->_get($response, 'type'); |
| 328 |
$cls = isset($this->_errors[$type]) ? $this->_errors[$type] : 'DatafeedrError'; |
| 329 |
throw new $cls($this->_get($response, 'message'), $error); |
| 330 |
} |
| 331 |
|
| 332 |
if($status != 200) { |
| 333 |
throw new DatafeedrHTTPError("Status $status"); |
| 334 |
} |
| 335 |
|
| 336 |
$this->_status = $this->_get($response, 'status'); |
| 337 |
return $response; |
| 338 |
} |
| 339 |
|
| 340 |
protected function _intarray($id_or_ids) { |
| 341 |
if(is_numeric($id_or_ids)) { |
| 342 |
return array($id_or_ids); |
| 343 |
} |
| 344 |
if(is_array($id_or_ids)) { |
| 345 |
return array_values($id_or_ids); |
| 346 |
} |
| 347 |
return array(); |
| 348 |
} |
| 349 |
|
| 350 |
protected function _get($obj, $prop, $default=NULL) { |
| 351 |
if(is_array($obj) && isset($obj[$prop])) { |
| 352 |
return $obj[$prop]; |
| 353 |
} |
| 354 |
if(is_object($obj) && isset($obj->$prop)) { |
| 355 |
return $obj->$prop; |
| 356 |
} |
| 357 |
return $default; |
| 358 |
} |
| 359 |
|
| 360 |
|
| 361 |
/** |
| 362 |
* Perform a HTTP post request by the means of the curl library. |
| 363 |
* |
| 364 |
* @param string $url Request url. |
| 365 |
* @param array $headers Array of headers. |
| 366 |
* @param string $postdata Post data. |
| 367 |
* @return array (int http status, string response body) |
| 368 |
* |
| 369 |
**/ |
| 370 |
protected function _transportCurl($url, $headers, $postdata) { |
| 371 |
$ch = curl_init(); |
| 372 |
curl_setopt($ch, CURLOPT_URL, $url); |
| 373 |
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_timeout); |
| 374 |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
| 375 |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
| 376 |
curl_setopt($ch, CURLOPT_POST, 1); |
| 377 |
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); |
| 378 |
|
| 379 |
$response = curl_exec($ch); |
| 380 |
$status = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE)); |
| 381 |
$errno = curl_errno($ch); |
| 382 |
$errmsg = curl_error($ch); |
| 383 |
|
| 384 |
curl_close($ch); |
| 385 |
|
| 386 |
if($errno) { |
| 387 |
throw new DatafeedrHTTPError($errmsg, $errno); |
| 388 |
} |
| 389 |
|
| 390 |
return array($status, $response); |
| 391 |
} |
| 392 |
|
| 393 |
/** |
| 394 |
* Perform a HTTP post request using file functions. |
| 395 |
* |
| 396 |
* @param string $url Request url. |
| 397 |
* @param array $headers Array of headers. |
| 398 |
* @param string $postdata Post data. |
| 399 |
* @return array (int http status, string response body) |
| 400 |
* |
| 401 |
**/ |
| 402 |
protected function _transportFile($url, $headers, $postdata) { |
| 403 |
$options = array('http' => array( |
| 404 |
'method' => 'POST', |
| 405 |
'content' => $postdata, |
| 406 |
'header' => implode("\r\n", $headers), |
| 407 |
'ignore_errors' => TRUE, |
| 408 |
'timeout' => $this->_timeout, |
| 409 |
)); |
| 410 |
$context = stream_context_create($options); |
| 411 |
$response = file_get_contents($url, false, $context); |
| 412 |
|
| 413 |
$status = 200; |
| 414 |
if(isset($http_response_header) && isset($http_response_header[0])) { |
| 415 |
if(preg_match('/HTTP.+?(\d\d\d)/', $http_response_header[0], $match)) { |
| 416 |
$status = intval($match[1]); |
| 417 |
} |
| 418 |
} else if($response === false) { |
| 419 |
throw new DatafeedrHTTPError("HTTP error: invalid response"); |
| 420 |
} |
| 421 |
return array($status, $response); |
| 422 |
} |
| 423 |
|
| 424 |
/** |
| 425 |
* Perform a HTTP post request using sockets. |
| 426 |
* |
| 427 |
* @param string $url Request url. |
| 428 |
* @param array $headers Array of headers. |
| 429 |
* @param string $postdata Post data. |
| 430 |
* @return array (int http status, string response body) |
| 431 |
* |
| 432 |
**/ |
| 433 |
protected function _transportSocket($url, $headers, $postdata) { |
| 434 |
$parts = parse_url($url); |
| 435 |
$errno = 0; |
| 436 |
$errmsg = ''; |
| 437 |
|
| 438 |
$fp = fsockopen($parts['host'], 80, $errno, $errmsg, $this->_timeout); |
| 439 |
if(!$fp) { |
| 440 |
throw new DatafeedrHTTPError($errmsg, $errno); |
| 441 |
} |
| 442 |
|
| 443 |
fwrite($fp, "POST " . $parts['path'] . " HTTP/1.1\r\n"); |
| 444 |
fwrite($fp, implode("\r\n", $headers) . "\r\n\r\n"); |
| 445 |
fwrite($fp, $postdata); |
| 446 |
|
| 447 |
$buf = ''; |
| 448 |
while(!feof($fp)) { |
| 449 |
$buf .= fgets($fp, 1024); |
| 450 |
} |
| 451 |
fclose($fp); |
| 452 |
|
| 453 |
$buf = explode("\r\n\r\n", $buf, 2); |
| 454 |
if(count($buf) != 2) { |
| 455 |
throw new DatafeedrHTTPError("Invalid response"); |
| 456 |
} |
| 457 |
if(preg_match('/HTTP.+?(\d\d\d)/', $buf[0], $match)) { |
| 458 |
$status = intval($match[1]); |
| 459 |
} else { |
| 460 |
throw new DatafeedrHTTPError("Invalid status"); |
| 461 |
} |
| 462 |
return array($status, $buf[1]); |
| 463 |
} |
| 464 |
} |
| 465 |
} |
| 466 |
|
| 467 |
if(!class_exists('DatafeedrSearchRequestBase', false)) { |
| 468 |
/** |
| 469 |
* Generic Datafeedr API search request. |
| 470 |
**/ |
| 471 |
class DatafeedrSearchRequestBase |
| 472 |
{ |
| 473 |
protected $_api; |
| 474 |
protected $_lastResponse; |
| 475 |
|
| 476 |
/** |
| 477 |
* Constructor. |
| 478 |
* |
| 479 |
* @param object $api DatafeedrApi object. |
| 480 |
* |
| 481 |
**/ |
| 482 |
public function __construct($api) { |
| 483 |
$this->_api = $api; |
| 484 |
} |
| 485 |
|
| 486 |
/** |
| 487 |
* Get the number of found products. |
| 488 |
* |
| 489 |
* @return int |
| 490 |
* |
| 491 |
**/ |
| 492 |
public function getFoundCount() { |
| 493 |
return $this->_responseItem('found_count', 0); |
| 494 |
} |
| 495 |
|
| 496 |
/** |
| 497 |
* Get the number of products that can be retrieved from the server. |
| 498 |
* |
| 499 |
* @return int |
| 500 |
* |
| 501 |
**/ |
| 502 |
public function getResultCount() { |
| 503 |
return $this->_responseItem('result_count', 0); |
| 504 |
} |
| 505 |
|
| 506 |
/** |
| 507 |
* Return the response from the last search. |
| 508 |
* |
| 509 |
* @return array |
| 510 |
* |
| 511 |
**/ |
| 512 |
public function getResponse() { |
| 513 |
return $this->_lastResponse; |
| 514 |
} |
| 515 |
|
| 516 |
protected function _responseItem($prop, $default) { |
| 517 |
if(is_null($this->_lastResponse)) { |
| 518 |
throw new DatafeedrError("Reading from an empty request"); |
| 519 |
} |
| 520 |
if(is_object($this->_lastResponse) && isset($this->_lastResponse->$prop)) { |
| 521 |
return $this->_lastResponse->$prop; |
| 522 |
} |
| 523 |
if(is_array($this->_lastResponse) && isset($this->_lastResponse[$prop])) { |
| 524 |
return $this->_lastResponse[$prop]; |
| 525 |
} |
| 526 |
return $default; |
| 527 |
} |
| 528 |
|
| 529 |
function _apiCall($action, $request = NULL) { |
| 530 |
$this->_lastResponse = $this->_api->apiCall($action, $request); |
| 531 |
} |
| 532 |
|
| 533 |
} |
| 534 |
} |
| 535 |
|
| 536 |
if(!class_exists('DatafeedrSearchRequest', false)) { |
| 537 |
/** |
| 538 |
* Search request for Datafeedr API. |
| 539 |
**/ |
| 540 |
class DatafeedrSearchRequest extends DatafeedrSearchRequestBase |
| 541 |
{ |
| 542 |
/** |
| 543 |
* Constructor. |
| 544 |
* |
| 545 |
* @param object $api DatafeedrApi object. |
| 546 |
* |
| 547 |
**/ |
| 548 |
public function __construct($api) { |
| 549 |
parent::__construct($api); |
| 550 |
|
| 551 |
$this->_query = array(); |
| 552 |
$this->_sort = array(); |
| 553 |
$this->_fields = array(); |
| 554 |
$this->_limit = 0; |
| 555 |
$this->_offset = 0; |
| 556 |
$this->_priceGroups = 0; |
| 557 |
$this->_excludeDuplicates = ""; |
| 558 |
} |
| 559 |
|
| 560 |
/** |
| 561 |
* Add a query filter. |
| 562 |
* |
| 563 |
* @param string $filter Query filter. |
| 564 |
* @return $this |
| 565 |
* |
| 566 |
**/ |
| 567 |
public function addFilter($filter) { |
| 568 |
$this->_query []= $filter; |
| 569 |
return $this; |
| 570 |
} |
| 571 |
|
| 572 |
/** |
| 573 |
* Add a sort field. |
| 574 |
* |
| 575 |
* @param string $field Field name. |
| 576 |
* @param int $order One of DatafeedrApi::SORT_ASCENDING or DatafeedrApi::SORT_DESCENDING |
| 577 |
* @return $this |
| 578 |
* |
| 579 |
**/ |
| 580 |
public function addSort($field, $order = DatafeedrApi::SORT_ASCENDING) { |
| 581 |
if(strlen($field) && ($field[0] == '+' || $field[0] == '-')) { |
| 582 |
$this->_sort []= $field; |
| 583 |
} else if($order == DatafeedrApi::SORT_ASCENDING) { |
| 584 |
$this->_sort []= '+' . $field; |
| 585 |
} else if($order == DatafeedrApi::SORT_DESCENDING) { |
| 586 |
$this->_sort []= '-' . $field; |
| 587 |
} else { |
| 588 |
throw new DatafeedrError("Invalid sort order"); |
| 589 |
} |
| 590 |
return $this; |
| 591 |
} |
| 592 |
|
| 593 |
/** |
| 594 |
* Set which fields to retrieve. |
| 595 |
* |
| 596 |
* @param array $fields List of field names. |
| 597 |
* @return $this |
| 598 |
* |
| 599 |
**/ |
| 600 |
public function setFields($fields) { |
| 601 |
$this->_fields = $fields; |
| 602 |
return $this; |
| 603 |
} |
| 604 |
|
| 605 |
/** |
| 606 |
* Exclude duplicate results. |
| 607 |
* |
| 608 |
* @param string $filter Equality filter in form "field1 field2 | field3". |
| 609 |
* @return $this |
| 610 |
* |
| 611 |
**/ |
| 612 |
public function excludeDuplicates($filter) { |
| 613 |
if(is_array($filter)) { |
| 614 |
$filter = implode(' ', $filter); |
| 615 |
} |
| 616 |
$this->_excludeDuplicates = $filter; |
| 617 |
return $this; |
| 618 |
} |
| 619 |
|
| 620 |
/** |
| 621 |
* Set a limit. |
| 622 |
* |
| 623 |
* @param int $limit The limit. |
| 624 |
* @return $this |
| 625 |
* |
| 626 |
**/ |
| 627 |
public function setLimit($limit) { |
| 628 |
$this->_limit = $limit; |
| 629 |
return $this; |
| 630 |
} |
| 631 |
|
| 632 |
/** |
| 633 |
* Set an offset. |
| 634 |
* |
| 635 |
* @param int $offset The offset. |
| 636 |
* @return $this |
| 637 |
* |
| 638 |
**/ |
| 639 |
public function setOffset($offset) { |
| 640 |
$this->_offset = $offset; |
| 641 |
return $this; |
| 642 |
} |
| 643 |
|
| 644 |
/** |
| 645 |
* Set a price group count. |
| 646 |
* |
| 647 |
* @param int $groups Group count. |
| 648 |
* @return $this |
| 649 |
* |
| 650 |
**/ |
| 651 |
public function setPriceGroups($groups) { |
| 652 |
$this->_priceGroups = $groups; |
| 653 |
return $this; |
| 654 |
} |
| 655 |
|
| 656 |
/** |
| 657 |
* Get found networks. |
| 658 |
* |
| 659 |
* @return array |
| 660 |
* |
| 661 |
**/ |
| 662 |
public function getNetworks() { |
| 663 |
return $this->_responseItem('networks', array()); |
| 664 |
} |
| 665 |
|
| 666 |
/** |
| 667 |
* Get found merchants. |
| 668 |
* |
| 669 |
* @return array |
| 670 |
* |
| 671 |
**/ |
| 672 |
public function getMerchants() { |
| 673 |
return $this->_responseItem('merchants', array()); |
| 674 |
} |
| 675 |
|
| 676 |
/** |
| 677 |
* Get found price groups. |
| 678 |
* |
| 679 |
* @return array |
| 680 |
* |
| 681 |
**/ |
| 682 |
public function getPriceGroups() { |
| 683 |
return $this->_responseItem('price_groups', array()); |
| 684 |
} |
| 685 |
|
| 686 |
/** |
| 687 |
* Create a request object to use with the API. |
| 688 |
* |
| 689 |
* @return array |
| 690 |
* |
| 691 |
**/ |
| 692 |
public function getParams() { |
| 693 |
$request = array(); |
| 694 |
if($this->_query) { |
| 695 |
$request['query'] = $this->_query; |
| 696 |
} |
| 697 |
if($this->_sort) { |
| 698 |
$request['sort'] = $this->_sort; |
| 699 |
} |
| 700 |
if($this->_fields) { |
| 701 |
$request['fields'] = $this->_fields; |
| 702 |
} |
| 703 |
if($this->_limit) { |
| 704 |
$request['limit'] = $this->_limit; |
| 705 |
} |
| 706 |
if($this->_offset) { |
| 707 |
$request['offset'] = $this->_offset; |
| 708 |
} |
| 709 |
if($this->_priceGroups) { |
| 710 |
$request['price_groups'] = $this->_priceGroups; |
| 711 |
} |
| 712 |
if($this->_excludeDuplicates) { |
| 713 |
$request['exclude_duplicates'] = $this->_excludeDuplicates; |
| 714 |
} |
| 715 |
$request['string_ids'] = 1; |
| 716 |
return $request; |
| 717 |
} |
| 718 |
|
| 719 |
/** |
| 720 |
* Run search and return a list of products. |
| 721 |
* |
| 722 |
* @return array |
| 723 |
* |
| 724 |
**/ |
| 725 |
public function execute() { |
| 726 |
$params = $this->getParams(); |
| 727 |
if(!isset($params['query'])) { |
| 728 |
throw new DatafeedrError("Query can't be empty"); |
| 729 |
} |
| 730 |
$this->_apiCall('search', $params); |
| 731 |
return $this->_responseItem('products', array()); |
| 732 |
} |
| 733 |
|
| 734 |
} |
| 735 |
} |
| 736 |
|
| 737 |
if(!class_exists('DatafeedrMerchantSearchRequest', false)) { |
| 738 |
/** |
| 739 |
* Search request for Datafeedr Merchants. |
| 740 |
**/ |
| 741 |
class DatafeedrMerchantSearchRequest extends DatafeedrSearchRequestBase |
| 742 |
{ |
| 743 |
/** |
| 744 |
* Constructor. |
| 745 |
* |
| 746 |
* @param object $api DatafeedrApi object. |
| 747 |
* |
| 748 |
**/ |
| 749 |
public function __construct($api) { |
| 750 |
parent::__construct($api); |
| 751 |
|
| 752 |
$this->_query = array(); |
| 753 |
$this->_sort = array(); |
| 754 |
$this->_fields = array(); |
| 755 |
$this->_limit = 0; |
| 756 |
$this->_offset = 0; |
| 757 |
} |
| 758 |
|
| 759 |
/** |
| 760 |
* Add a query filter. |
| 761 |
* |
| 762 |
* @param string $filter Query filter. |
| 763 |
* @return $this |
| 764 |
* |
| 765 |
**/ |
| 766 |
public function addFilter($filter) { |
| 767 |
$this->_query []= $filter; |
| 768 |
return $this; |
| 769 |
} |
| 770 |
|
| 771 |
/** |
| 772 |
* Add a sort field. |
| 773 |
* |
| 774 |
* @param string $field Field name. |
| 775 |
* @param int $order One of DatafeedrApi::SORT_ASCENDING or DatafeedrApi::SORT_DESCENDING |
| 776 |
* @throws DatafeedrError |
| 777 |
* @return $this |
| 778 |
* |
| 779 |
**/ |
| 780 |
public function addSort($field, $order = DatafeedrApi::SORT_ASCENDING) { |
| 781 |
if(strlen($field) && ($field[0] == '+' || $field[0] == '-')) { |
| 782 |
$this->_sort []= $field; |
| 783 |
} else if($order == DatafeedrApi::SORT_ASCENDING) { |
| 784 |
$this->_sort []= '+' . $field; |
| 785 |
} else if($order == DatafeedrApi::SORT_DESCENDING) { |
| 786 |
$this->_sort []= '-' . $field; |
| 787 |
} else { |
| 788 |
throw new DatafeedrError("Invalid sort order"); |
| 789 |
} |
| 790 |
return $this; |
| 791 |
} |
| 792 |
|
| 793 |
/** |
| 794 |
* Set which fields to retrieve. |
| 795 |
* |
| 796 |
* @param array $fields List of field names. |
| 797 |
* @return $this |
| 798 |
* |
| 799 |
**/ |
| 800 |
public function setFields($fields) { |
| 801 |
$this->_fields = $fields; |
| 802 |
return $this; |
| 803 |
} |
| 804 |
|
| 805 |
/** |
| 806 |
* Set a limit. |
| 807 |
* |
| 808 |
* @param int $limit The limit. |
| 809 |
* @return $this |
| 810 |
* |
| 811 |
**/ |
| 812 |
public function setLimit($limit) { |
| 813 |
$this->_limit = $limit; |
| 814 |
return $this; |
| 815 |
} |
| 816 |
|
| 817 |
/** |
| 818 |
* Set an offset. |
| 819 |
* |
| 820 |
* @param int $offset The offset. |
| 821 |
* @return $this |
| 822 |
* |
| 823 |
**/ |
| 824 |
public function setOffset($offset) { |
| 825 |
$this->_offset = $offset; |
| 826 |
return $this; |
| 827 |
} |
| 828 |
|
| 829 |
/** |
| 830 |
* Get found networks. |
| 831 |
* |
| 832 |
* @return array |
| 833 |
* |
| 834 |
**/ |
| 835 |
public function getNetworks() { |
| 836 |
return $this->_responseItem('networks', array()); |
| 837 |
} |
| 838 |
|
| 839 |
/** |
| 840 |
* Get found merchants. |
| 841 |
* |
| 842 |
* @return array |
| 843 |
* |
| 844 |
**/ |
| 845 |
public function getMerchants() { |
| 846 |
return $this->_responseItem('merchants', array()); |
| 847 |
} |
| 848 |
|
| 849 |
/** |
| 850 |
* Run search and return a list of merchants. |
| 851 |
* |
| 852 |
* @throws DatafeedrError |
| 853 |
* @return array |
| 854 |
* |
| 855 |
**/ |
| 856 |
public function execute() { |
| 857 |
$params = $this->getParams(); |
| 858 |
if(!isset($params['query'])) { |
| 859 |
throw new DatafeedrError("Query can't be empty"); |
| 860 |
} |
| 861 |
$this->_apiCall('merchant_search', $params); |
| 862 |
return $this->_responseItem('merchants', array()); |
| 863 |
} |
| 864 |
|
| 865 |
/** |
| 866 |
* Create a request object to use with the API. |
| 867 |
* |
| 868 |
* @return array |
| 869 |
* |
| 870 |
**/ |
| 871 |
public function getParams() { |
| 872 |
$request = array(); |
| 873 |
if($this->_query) { |
| 874 |
$request['query'] = $this->_query; |
| 875 |
} |
| 876 |
if($this->_sort) { |
| 877 |
$request['sort'] = $this->_sort; |
| 878 |
} |
| 879 |
if($this->_fields) { |
| 880 |
$request['fields'] = $this->_fields; |
| 881 |
} |
| 882 |
if($this->_limit) { |
| 883 |
$request['limit'] = $this->_limit; |
| 884 |
} |
| 885 |
if($this->_offset) { |
| 886 |
$request['offset'] = $this->_offset; |
| 887 |
} |
| 888 |
return $request; |
| 889 |
} |
| 890 |
} |
| 891 |
} |
| 892 |
|
| 893 |
if(!class_exists('DatafeedrAmazonRequest', false)) { |
| 894 |
/** |
| 895 |
* Generic Amazon request. |
| 896 |
**/ |
| 897 |
class DatafeedrAmazonRequest extends DatafeedrSearchRequestBase |
| 898 |
{ |
| 899 |
protected $_found = -1; |
| 900 |
|
| 901 |
const AWS_VERSION = "2011-08-01"; |
| 902 |
|
| 903 |
/** |
| 904 |
* Constructor |
| 905 |
* |
| 906 |
* @param object $api DatafeedrApi object. |
| 907 |
* @param string $awsAccessKeyId Amazon access key. |
| 908 |
* @param string $awsSecretKey Amazon secret key. |
| 909 |
* @param string $awsAssociateTag Amazon associate tag. |
| 910 |
* @param string $locale Amazon locale (two-letter code). |
| 911 |
*/ |
| 912 |
public function __construct($api, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale="US") { |
| 913 |
parent::__construct($api); |
| 914 |
$this->_hosts = array( |
| 915 |
"CA" => "webservices.amazon.ca", |
| 916 |
"CN" => "webservices.amazon.cn", |
| 917 |
"DE" => "webservices.amazon.de", |
| 918 |
"ES" => "webservices.amazon.es", |
| 919 |
"FR" => "webservices.amazon.fr", |
| 920 |
"IT" => "webservices.amazon.it", |
| 921 |
"JP" => "webservices.amazon.co.jp", |
| 922 |
"UK" => "webservices.amazon.co.uk", |
| 923 |
"US" => "webservices.amazon.com", |
| 924 |
); |
| 925 |
|
| 926 |
$this->_params = array(); |
| 927 |
$this->_locale = strtoupper($locale); |
| 928 |
|
| 929 |
if(!isset($this->_hosts[$this->_locale])) { |
| 930 |
throw new DatafeedrError("Invalid Amazon locale"); |
| 931 |
} |
| 932 |
|
| 933 |
$this->_awsAccessKeyId = $awsAccessKeyId; |
| 934 |
$this->_awsSecretKey = $awsSecretKey; |
| 935 |
$this->_awsAssociateTag = $awsAssociateTag; |
| 936 |
} |
| 937 |
|
| 938 |
/** |
| 939 |
* Returns all parameters. |
| 940 |
* |
| 941 |
* @return array |
| 942 |
* |
| 943 |
**/ |
| 944 |
public function getParams() { |
| 945 |
return $this->_params; |
| 946 |
} |
| 947 |
|
| 948 |
protected function _amazonUrl($operation, $params, $defaults=NULL) { |
| 949 |
$params = array_filter($params); |
| 950 |
|
| 951 |
if(!is_null($defaults)) { |
| 952 |
foreach($defaults as $k => $v) { |
| 953 |
if(!isset($params[$k])) { |
| 954 |
$params[$k] = $v; |
| 955 |
} |
| 956 |
} |
| 957 |
} |
| 958 |
|
| 959 |
$params["Operation"] = $operation; |
| 960 |
$params["Service"] = "AWSECommerceService"; |
| 961 |
$params["AWSAccessKeyId"] = $this->_awsAccessKeyId; |
| 962 |
$params["AssociateTag"] = $this->_awsAssociateTag; |
| 963 |
$params["Version"] = self::AWS_VERSION; |
| 964 |
$params["Timestamp"] = gmdate("Y-m-d\\TH:i:s\\Z"); |
| 965 |
|
| 966 |
ksort($params); |
| 967 |
$query = array(); |
| 968 |
foreach($params as $k => $v) { |
| 969 |
if(is_array($v)) { |
| 970 |
$v = implode(',', $v); |
| 971 |
} |
| 972 |
$query []= $k . '=' . rawurlencode($v); |
| 973 |
} |
| 974 |
$query = implode('&', $query); |
| 975 |
$host = $this->_hosts[$this->_locale]; |
| 976 |
$path = "/onca/xml"; |
| 977 |
$subj = sprintf("GET\n%s\n%s\n%s", $host, $path, $query); |
| 978 |
$sign = rawurlencode(base64_encode(hash_hmac("sha256", $subj, $this->_awsSecretKey, TRUE))); |
| 979 |
return "http://{$host}{$path}?{$query}&Signature={$sign}"; |
| 980 |
} |
| 981 |
} |
| 982 |
} |
| 983 |
|
| 984 |
if(!class_exists('DatafeedrAmazonSearchRequest', false)) { |
| 985 |
/** |
| 986 |
* Amazon search request. |
| 987 |
**/ |
| 988 |
class DatafeedrAmazonSearchRequest extends DatafeedrAmazonRequest |
| 989 |
{ |
| 990 |
/** |
| 991 |
* Add a parameter. |
| 992 |
* |
| 993 |
* @param string $name Parameter name. |
| 994 |
* @param string $value Parameter value. |
| 995 |
* @return $this |
| 996 |
* |
| 997 |
* @see http://docs.aws.amazon.com/AWSECommerceService/latest/DG/ItemSearch.html |
| 998 |
* |
| 999 |
**/ |
| 1000 |
public function addParam($name, $value) { |
| 1001 |
$this->_params[$name] = $value; |
| 1002 |
return $this; |
| 1003 |
} |
| 1004 |
|
| 1005 |
/** |
| 1006 |
* Run search and return a list of products. |
| 1007 |
* |
| 1008 |
* @return array |
| 1009 |
* |
| 1010 |
**/ |
| 1011 |
public function execute() { |
| 1012 |
$defaults = array( |
| 1013 |
'ResponseGroup' => 'ItemAttributes,Images,OfferFull,BrowseNodes,EditorialReview,VariationSummary', |
| 1014 |
'SearchIndex' => 'All', |
| 1015 |
); |
| 1016 |
$url = $this->_amazonUrl('ItemSearch', $this->_params, $defaults); |
| 1017 |
$this->_apiCall('amazon_search', array('url' => $url)); |
| 1018 |
return $this->_responseItem('products', array()); |
| 1019 |
} |
| 1020 |
} |
| 1021 |
} |
| 1022 |
|
| 1023 |
if(!class_exists('DatafeedrAmazonLookupRequest', false)) { |
| 1024 |
/** |
| 1025 |
* Amazon lookup request. |
| 1026 |
**/ |
| 1027 |
class DatafeedrAmazonLookupRequest extends DatafeedrAmazonRequest |
| 1028 |
{ |
| 1029 |
/** |
| 1030 |
* Add a parameter. |
| 1031 |
* |
| 1032 |
* @param string $name Parameter name - one of 'ASIN', 'SKU', 'UPC', 'EAN', 'ISBN' |
| 1033 |
* @param string|array $value Parameter value or an array of values (up to 10). |
| 1034 |
* @return $this |
| 1035 |
* |
| 1036 |
* @see http://docs.aws.amazon.com/AWSECommerceService/latest/DG/ItemLookup.html |
| 1037 |
* |
| 1038 |
**/ |
| 1039 |
public function addParam($name, $value) { |
| 1040 |
$this->_params[$name] = $value; |
| 1041 |
return $this; |
| 1042 |
} |
| 1043 |
|
| 1044 |
/** |
| 1045 |
* Run search and return a list of products. |
| 1046 |
* |
| 1047 |
* @return array |
| 1048 |
* |
| 1049 |
**/ |
| 1050 |
public function execute() { |
| 1051 |
$params = array_filter($this->_params); |
| 1052 |
$types = array('ASIN', 'SKU', 'UPC', 'EAN', 'ISBN', 'asin', 'sku', 'upc', 'ean', 'isbn'); |
| 1053 |
foreach($types as $type) { |
| 1054 |
if(isset($params[$type])) { |
| 1055 |
$params['IdType'] = strtoupper($type); |
| 1056 |
$params['ItemId'] = $params[$type]; |
| 1057 |
unset($params[$type]); |
| 1058 |
} |
| 1059 |
} |
| 1060 |
|
| 1061 |
$defaults = array( |
| 1062 |
'ResponseGroup' => 'ItemAttributes,Images,OfferFull,BrowseNodes,EditorialReview,VariationSummary', |
| 1063 |
); |
| 1064 |
if(isset($params['IdType']) && $params['IdType'] != 'ASIN') { |
| 1065 |
$defaults['SearchIndex'] = 'All'; |
| 1066 |
} |
| 1067 |
$url = $this->_amazonUrl('ItemLookup', $params, $defaults); |
| 1068 |
$this->_apiCall('amazon_search', array('url' => $url)); |
| 1069 |
return $this->_responseItem('products', array()); |
| 1070 |
} |
| 1071 |
} |
| 1072 |
} |
| 1073 |
|
| 1074 |
if(!class_exists('DatafeedrError', false)) { |
| 1075 |
/** |
| 1076 |
* Generic Api error. |
| 1077 |
**/ |
| 1078 |
class DatafeedrError extends Exception |
| 1079 |
{ |
| 1080 |
} |
| 1081 |
} |
| 1082 |
|
| 1083 |
if(!class_exists('DatafeedrBadRequestError', false)) { |
| 1084 |
/** |
| 1085 |
* API error: Invalid Request. |
| 1086 |
**/ |
| 1087 |
class DatafeedrBadRequestError extends DatafeedrError |
| 1088 |
{ |
| 1089 |
} |
| 1090 |
} |
| 1091 |
|
| 1092 |
if(!class_exists('DatafeedrAuthenticationError', false)) { |
| 1093 |
/** |
| 1094 |
* API error: Authentication failed. |
| 1095 |
**/ |
| 1096 |
class DatafeedrAuthenticationError extends DatafeedrError |
| 1097 |
{ |
| 1098 |
} |
| 1099 |
} |
| 1100 |
|
| 1101 |
if(!class_exists('DatafeedrLimitExceededError', false)) { |
| 1102 |
/** |
| 1103 |
* API error: Query limit exceeded. |
| 1104 |
**/ |
| 1105 |
class DatafeedrLimitExceededError extends DatafeedrError |
| 1106 |
{ |
| 1107 |
} |
| 1108 |
} |
| 1109 |
|
| 1110 |
if(!class_exists('DatafeedrHTTPError', false)) { |
| 1111 |
/** |
| 1112 |
* API error: Unspecified HTTP error. |
| 1113 |
**/ |
| 1114 |
class DatafeedrHTTPError extends DatafeedrError |
| 1115 |
{ |
| 1116 |
} |
| 1117 |
} |
| 1118 |
|
| 1119 |
if(!class_exists('DatafeedrQueryError', false)) { |
| 1120 |
/** |
| 1121 |
* API error: Error in the search query. |
| 1122 |
**/ |
| 1123 |
class DatafeedrQueryError extends DatafeedrError |
| 1124 |
{ |
| 1125 |
} |
| 1126 |
} |
| 1127 |
|
| 1128 |
if(!class_exists('DatafeedrExternalError', false)) { |
| 1129 |
/** |
| 1130 |
* API error: External service error. |
| 1131 |
**/ |
| 1132 |
class DatafeedrExternalError extends DatafeedrError |
| 1133 |
{ |
| 1134 |
} |
| 1135 |
} |
| 1136 |
|
| 1137 |
if(!class_exists('DatafeedrServerError', false)) { |
| 1138 |
/** |
| 1139 |
* API error: Internal server error. |
| 1140 |
**/ |
| 1141 |
class DatafeedrServerError extends DatafeedrError |
| 1142 |
{ |
| 1143 |
} |
| 1144 |
} |
| 1145 |
|
| 1146 |
?> |