| 1 |
<?php |
| 2 |
|
| 3 |
/** |
| 4 |
* Datafeedr Api Client Library. |
| 5 |
* |
| 6 |
* @version 0.1b.6544 |
| 7 |
* @copyright Datafeedr 2007 ~ 2013 - 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.6544'; |
| 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 => 'DatafeedrError', |
| 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 |
$this->_transport = $transport; |
| 107 |
} |
| 108 |
} |
| 109 |
|
| 110 |
/** |
| 111 |
* Return status information. |
| 112 |
* |
| 113 |
* @return array |
| 114 |
* |
| 115 |
**/ |
| 116 |
public function getStatus() { |
| 117 |
$this->apiCall('status'); |
| 118 |
return $this->_status; |
| 119 |
} |
| 120 |
|
| 121 |
/** |
| 122 |
* Return status information from the last request. |
| 123 |
* |
| 124 |
* If no Api request have been made, return NULL |
| 125 |
* |
| 126 |
* @return array|null |
| 127 |
* |
| 128 |
**/ |
| 129 |
public function lastStatus() { |
| 130 |
return $this->_status; |
| 131 |
} |
| 132 |
|
| 133 |
/** |
| 134 |
* Return the list of networks. |
| 135 |
* |
| 136 |
* @param int|array $networkId (optional) Network id or an array of network ids |
| 137 |
* @param bool $includeEmpty (optional) If FALSE, omit networks with 0 products |
| 138 |
* @param array $fields (optional) list of fields to retrieve |
| 139 |
* @return array |
| 140 |
* |
| 141 |
**/ |
| 142 |
public function getNetworks($networkId = NULL, $includeEmpty = FALSE, $fields = NULL) { |
| 143 |
$request = array(); |
| 144 |
if($networkId) { |
| 145 |
$request['_ids'] = $this->_intarray($networkId); |
| 146 |
} |
| 147 |
$request['skip_empty'] = intval(!$includeEmpty); |
| 148 |
if($fields) { |
| 149 |
$request['fields'] = $fields; |
| 150 |
} |
| 151 |
$response = $this->apiCall('networks', $request); |
| 152 |
return $this->_get($response, 'networks'); |
| 153 |
} |
| 154 |
|
| 155 |
/** |
| 156 |
* Return the list of merchants. |
| 157 |
* |
| 158 |
* @param int|array $networkId (optional) Network id or array of network ids |
| 159 |
* @param bool $includeEmpty (optional) If FALSE, omit merchants with 0 products |
| 160 |
* @param array $fields (optional) list of fields to retrieve |
| 161 |
* @return array |
| 162 |
* |
| 163 |
**/ |
| 164 |
public function getMerchants($networkId = NULL, $includeEmpty = FALSE, $fields = NULL) { |
| 165 |
$request = array(); |
| 166 |
if($networkId) { |
| 167 |
$request['source_ids'] = $this->_intarray($networkId); |
| 168 |
} |
| 169 |
$request['skip_empty'] = intval(!$includeEmpty); |
| 170 |
if($fields) { |
| 171 |
$request['fields'] = $fields; |
| 172 |
} |
| 173 |
$response = $this->apiCall('merchants', $request); |
| 174 |
return $this->_get($response, 'merchants'); |
| 175 |
} |
| 176 |
|
| 177 |
/** |
| 178 |
* Return the list of merchants by their ids. |
| 179 |
* |
| 180 |
* @param int|array $merchantId Merchant id or array of network ids |
| 181 |
* @param bool $includeEmpty (optional) If FALSE, omit merchants with 0 products |
| 182 |
* @param array $fields (optional) list of fields to retrieve |
| 183 |
* @return array |
| 184 |
* |
| 185 |
**/ |
| 186 |
public function getMerchantsById($merchantId, $includeEmpty = FALSE, $fields = NULL) { |
| 187 |
$request = array(); |
| 188 |
$request['_ids'] = $this->_intarray($merchantId); |
| 189 |
$request['skip_empty'] = intval(!$includeEmpty); |
| 190 |
if($fields) { |
| 191 |
$request['fields'] = $fields; |
| 192 |
} |
| 193 |
$response = $this->apiCall('merchants', $request); |
| 194 |
return $this->_get($response, 'merchants'); |
| 195 |
} |
| 196 |
|
| 197 |
/** |
| 198 |
* Return the list of searchable fields. |
| 199 |
* |
| 200 |
* @param int|array $networkId (optional) Network id or array of network ids |
| 201 |
* @return array |
| 202 |
* |
| 203 |
**/ |
| 204 |
public function getFields($networkId = NULL) { |
| 205 |
$request = array(); |
| 206 |
if($networkId) { |
| 207 |
$request['source_ids'] = $this->_intarray($networkId); |
| 208 |
} |
| 209 |
$response = $this->apiCall('fields', $request); |
| 210 |
return $this->_get($response, 'fields'); |
| 211 |
} |
| 212 |
|
| 213 |
/** |
| 214 |
* Return the list of products by their ids. |
| 215 |
* |
| 216 |
* @param int|array $productId Product id or an array of products ids. |
| 217 |
* @param array $fields (optional) list of fields to retrieve. |
| 218 |
* @return array |
| 219 |
* |
| 220 |
**/ |
| 221 |
public function getProducts($productId, $fields = NULL) { |
| 222 |
$request = array(); |
| 223 |
$request['_ids'] = $this->_intarray($productId); |
| 224 |
$request['string_ids'] = 1; |
| 225 |
if($fields) { |
| 226 |
$request['fields'] = $fields; |
| 227 |
} |
| 228 |
$response = $this->apiCall('get', $request); |
| 229 |
return $this->_get($response, 'products'); |
| 230 |
} |
| 231 |
|
| 232 |
/** |
| 233 |
* Return the list of Zanox merchant ids ("zmids"). |
| 234 |
* |
| 235 |
* @param int|array $merchantId Merchant id or an array of merchant ids. |
| 236 |
* @param int $adspaceId Zanox adspace Id. |
| 237 |
* @param string $connectId Zanox connection Id. |
| 238 |
* @return array |
| 239 |
* |
| 240 |
**/ |
| 241 |
public function getZanoxMerchantIds($merchantId, $adspaceId, $connectId) { |
| 242 |
$request = array(); |
| 243 |
$request['merchant_ids'] = $this->_intarray($merchantId); |
| 244 |
$request['adspace_id'] = $adspaceId; |
| 245 |
$request['connect_id'] = $connectId; |
| 246 |
$response = $this->apiCall('zanox_merchant_ids', $request); |
| 247 |
return $this->_get($response, 'zanox_merchant_ids'); |
| 248 |
} |
| 249 |
|
| 250 |
/** |
| 251 |
* Create a new DatafeedrSearchRequest object. |
| 252 |
* |
| 253 |
* @return DatafeedrSearchRequest |
| 254 |
* |
| 255 |
**/ |
| 256 |
public function searchRequest() { |
| 257 |
return new DatafeedrSearchRequest($this); |
| 258 |
} |
| 259 |
|
| 260 |
/** |
| 261 |
* Create a new DatafeedrAmazonSearchRequest object. |
| 262 |
* |
| 263 |
* @return DatafeedrAmazonSearchRequest |
| 264 |
* |
| 265 |
**/ |
| 266 |
public function amazonSearchRequest($awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale="US") { |
| 267 |
return new DatafeedrAmazonSearchRequest($this, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale); |
| 268 |
} |
| 269 |
|
| 270 |
/** |
| 271 |
* Create a new DatafeedrAmazonLookupRequest object. |
| 272 |
* |
| 273 |
* @return DatafeedrAmazonLookupRequest |
| 274 |
* |
| 275 |
**/ |
| 276 |
public function amazonLookupRequest($awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale="US") { |
| 277 |
return new DatafeedrAmazonLookupRequest($this, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale); |
| 278 |
} |
| 279 |
|
| 280 |
/** |
| 281 |
* Perform the raw Api call. |
| 282 |
* |
| 283 |
* @param string $action Api action. |
| 284 |
* @param array $request (optional) Request data. |
| 285 |
* @return array |
| 286 |
* |
| 287 |
**/ |
| 288 |
public function apiCall($action, $request = NULL) { |
| 289 |
if(!$request) |
| 290 |
$request = array(); |
| 291 |
|
| 292 |
$request['aid'] = $this->_accessId; |
| 293 |
$request['timestamp'] = gmdate('Y-m-d H:i:s'); |
| 294 |
|
| 295 |
$message = $request['aid'] .$action . $request['timestamp']; |
| 296 |
$request['signature'] = hash_hmac('sha256', $message, $this->_secretKey, FALSE); |
| 297 |
|
| 298 |
$postdata = json_encode($request); |
| 299 |
$url = $this->_url . '/' . $action; |
| 300 |
$headers = array( |
| 301 |
'Content-Type: application/json', |
| 302 |
'Accept: application/json', |
| 303 |
'Content-Length: '. strlen($postdata), |
| 304 |
'Connection: close', |
| 305 |
'User-Agent: datafeedr.php.' . self::VERSION |
| 306 |
); |
| 307 |
list($status, $response) = call_user_func($this->_transport, $url, $headers, $postdata); |
| 308 |
if(strlen($response)) { |
| 309 |
$response = json_decode($response, !$this->_returnObjects); |
| 310 |
} |
| 311 |
|
| 312 |
$error = $this->_get($response, 'error'); |
| 313 |
if($error) { |
| 314 |
$type = $this->_get($response, 'type'); |
| 315 |
$cls = isset($this->_errors[$type]) ? $this->_errors[$type] : 'DatafeedrError'; |
| 316 |
throw new $cls($this->_get($response, 'message'), $error); |
| 317 |
} |
| 318 |
|
| 319 |
if($status != 200) { |
| 320 |
throw new DatafeedrHTTPError("Status $status"); |
| 321 |
} |
| 322 |
|
| 323 |
$this->_status = $this->_get($response, 'status'); |
| 324 |
return $response; |
| 325 |
} |
| 326 |
|
| 327 |
protected function _intarray($id_or_ids) { |
| 328 |
if(is_numeric($id_or_ids)) { |
| 329 |
return array(intval($id_or_ids)); |
| 330 |
} |
| 331 |
if(is_array($id_or_ids)) { |
| 332 |
return array_map('intval', $id_or_ids); |
| 333 |
} |
| 334 |
return array(); |
| 335 |
} |
| 336 |
|
| 337 |
protected function _get($obj, $prop, $default=NULL) { |
| 338 |
if(is_array($obj) && isset($obj[$prop])) |
| 339 |
return $obj[$prop]; |
| 340 |
if(is_object($obj) && isset($obj->$prop)) |
| 341 |
return $obj->$prop; |
| 342 |
return $default; |
| 343 |
} |
| 344 |
|
| 345 |
|
| 346 |
/** |
| 347 |
* Perform a HTTP post request by the means of the curl library. |
| 348 |
* |
| 349 |
* @param string $url Request url. |
| 350 |
* @param array $headers Array of headers. |
| 351 |
* @param string $postdata Post data. |
| 352 |
* @return array (int http status, string response body) |
| 353 |
* |
| 354 |
**/ |
| 355 |
protected function _transportCurl($url, $headers, $postdata) { |
| 356 |
$ch = curl_init(); |
| 357 |
curl_setopt($ch, CURLOPT_URL, $url); |
| 358 |
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_timeout); |
| 359 |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
| 360 |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
| 361 |
curl_setopt($ch, CURLOPT_POST, 1); |
| 362 |
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); |
| 363 |
|
| 364 |
$response = curl_exec($ch); |
| 365 |
$status = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE)); |
| 366 |
$errno = curl_errno($ch); |
| 367 |
$errmsg = curl_error($ch); |
| 368 |
|
| 369 |
curl_close($ch); |
| 370 |
|
| 371 |
if($errno) { |
| 372 |
throw new DatafeedrHTTPError($errmsg, $errno); |
| 373 |
} |
| 374 |
|
| 375 |
return array($status, $response); |
| 376 |
} |
| 377 |
|
| 378 |
/** |
| 379 |
* Perform a HTTP post request using file functions. |
| 380 |
* |
| 381 |
* @param string $url Request url. |
| 382 |
* @param array $headers Array of headers. |
| 383 |
* @param string $postdata Post data. |
| 384 |
* @return array (int http status, string response body) |
| 385 |
* |
| 386 |
**/ |
| 387 |
protected function _transportFile($url, $headers, $postdata) { |
| 388 |
$options = array('http' => array( |
| 389 |
'method' => 'POST', |
| 390 |
'content' => $postdata, |
| 391 |
'header' => implode("\r\n", $headers), |
| 392 |
'ignore_errors' => TRUE, |
| 393 |
'timeout' => $this->_timeout, |
| 394 |
)); |
| 395 |
$context = stream_context_create($options); |
| 396 |
$response = file_get_contents($url, false, $context); |
| 397 |
|
| 398 |
$status = 200; |
| 399 |
if(isset($http_response_header) && isset($http_response_header[0])) { |
| 400 |
if(preg_match('/HTTP.+?(\d\d\d)/', $http_response_header[0], $match)) |
| 401 |
$status = intval($match[1]); |
| 402 |
} else if($response === false) { |
| 403 |
throw new DatafeedrHTTPError("HTTP error: invalid response"); |
| 404 |
} |
| 405 |
return array($status, $response); |
| 406 |
} |
| 407 |
|
| 408 |
/** |
| 409 |
* Perform a HTTP post request using sockets. |
| 410 |
* |
| 411 |
* @param string $url Request url. |
| 412 |
* @param array $headers Array of headers. |
| 413 |
* @param string $postdata Post data. |
| 414 |
* @return array (int http status, string response body) |
| 415 |
* |
| 416 |
**/ |
| 417 |
protected function _transportSocket($url, $headers, $postdata) { |
| 418 |
$parts = parse_url($url); |
| 419 |
$errno = 0; |
| 420 |
$errmsg = ''; |
| 421 |
|
| 422 |
$fp = fsockopen($parts['host'], 80, $errno, $errmsg, $this->_timeout); |
| 423 |
if(!$fp) { |
| 424 |
throw new DatafeedrHTTPError($errmsg, $errno); |
| 425 |
} |
| 426 |
|
| 427 |
fwrite($fp, "POST " . $parts['path'] . " HTTP/1.1\r\n"); |
| 428 |
fwrite($fp, implode("\r\n", $headers) . "\r\n\r\n"); |
| 429 |
fwrite($fp, $postdata); |
| 430 |
|
| 431 |
$buf = ''; |
| 432 |
while(!feof($fp)) { |
| 433 |
$buf .= fgets($fp, 1024); |
| 434 |
} |
| 435 |
fclose($fp); |
| 436 |
|
| 437 |
$buf = explode("\r\n\r\n", $buf, 2); |
| 438 |
if(count($buf) != 2) { |
| 439 |
throw new DatafeedrHTTPError("Invalid response"); |
| 440 |
} |
| 441 |
if(preg_match('/HTTP.+?(\d\d\d)/', $buf[0], $match)) { |
| 442 |
$status = intval($match[1]); |
| 443 |
} else { |
| 444 |
throw new DatafeedrHTTPError("Invalid status"); |
| 445 |
} |
| 446 |
return array($status, $buf[1]); |
| 447 |
} |
| 448 |
} |
| 449 |
} |
| 450 |
|
| 451 |
if(!class_exists('DatafeedrSearchRequest', false)) { |
| 452 |
|
| 453 |
/** |
| 454 |
* Search request for Datafeedr API. |
| 455 |
**/ |
| 456 |
class DatafeedrSearchRequest |
| 457 |
{ |
| 458 |
protected $_api; |
| 459 |
|
| 460 |
/** |
| 461 |
* Constructor. |
| 462 |
* |
| 463 |
* @param object $api DatafeedrApi object. |
| 464 |
* |
| 465 |
**/ |
| 466 |
public function __construct($api) { |
| 467 |
$this->_api = $api; |
| 468 |
$this->_query = array(); |
| 469 |
$this->_sort = array(); |
| 470 |
$this->_fields = array(); |
| 471 |
$this->_limit = 0; |
| 472 |
$this->_offset = 0; |
| 473 |
$this->_priceGroups = 0; |
| 474 |
$this->_excludeDuplicates = ""; |
| 475 |
$this->_lastResponse = NULL; |
| 476 |
} |
| 477 |
|
| 478 |
/** |
| 479 |
* Add a query filter. |
| 480 |
* |
| 481 |
* @param string $filter Query filter. |
| 482 |
* @return $this |
| 483 |
* |
| 484 |
**/ |
| 485 |
public function addFilter($filter) { |
| 486 |
$this->_query []= $filter; |
| 487 |
return $this; |
| 488 |
} |
| 489 |
|
| 490 |
/** |
| 491 |
* Add a sort field. |
| 492 |
* |
| 493 |
* @param string $field Field name. |
| 494 |
* @param int $order One of DatafeedrApi::SORT_ASCENDING or DatafeedrApi::SORT_DESCENDING |
| 495 |
* @return $this |
| 496 |
* |
| 497 |
**/ |
| 498 |
public function addSort($field, $order = DatafeedrApi::SORT_ASCENDING) { |
| 499 |
if(strlen($field) && ($field[0] == '+' || $field[0] == '-')) { |
| 500 |
$this->_sort []= $field; |
| 501 |
} else if($order == DatafeedrApi::SORT_ASCENDING) { |
| 502 |
$this->_sort []= '+' . $field; |
| 503 |
} else if($order == DatafeedrApi::SORT_DESCENDING) { |
| 504 |
$this->_sort []= '-' . $field; |
| 505 |
} else { |
| 506 |
throw new DatafeedrError("Invalid sort order"); |
| 507 |
} |
| 508 |
return $this; |
| 509 |
} |
| 510 |
|
| 511 |
/** |
| 512 |
* Set a list of fields to retrieve. |
| 513 |
* |
| 514 |
* @param array $fields List of field names. |
| 515 |
* @return $this |
| 516 |
* |
| 517 |
**/ |
| 518 |
public function setFields($fields) { |
| 519 |
$this->_fields = $fields; |
| 520 |
return $this; |
| 521 |
} |
| 522 |
|
| 523 |
/** |
| 524 |
* Exclude duplicate results. |
| 525 |
* |
| 526 |
* @param string $filter Equality filter in form "field1 field2 | field3". |
| 527 |
* @return $this |
| 528 |
* |
| 529 |
**/ |
| 530 |
public function excludeDuplicates($filter) { |
| 531 |
if(is_array($filter)) |
| 532 |
$filter = implode(' ', $filter); |
| 533 |
$this->_excludeDuplicates = $filter; |
| 534 |
return $this; |
| 535 |
} |
| 536 |
|
| 537 |
/** |
| 538 |
* Set a limit. |
| 539 |
* |
| 540 |
* @param int $limit The limit. |
| 541 |
* @return $this |
| 542 |
* |
| 543 |
**/ |
| 544 |
public function setLimit($limit) { |
| 545 |
$this->_limit = $limit; |
| 546 |
return $this; |
| 547 |
} |
| 548 |
|
| 549 |
/** |
| 550 |
* Set an offset. |
| 551 |
* |
| 552 |
* @param int $offset The offset. |
| 553 |
* @return $this |
| 554 |
* |
| 555 |
**/ |
| 556 |
public function setOffset($offset) { |
| 557 |
$this->_offset = $offset; |
| 558 |
return $this; |
| 559 |
} |
| 560 |
|
| 561 |
/** |
| 562 |
* Set a price group count. |
| 563 |
* |
| 564 |
* @param int $groups Group count. |
| 565 |
* @return $this |
| 566 |
* |
| 567 |
**/ |
| 568 |
public function setPriceGroups($groups) { |
| 569 |
$this->_priceGroups = $groups; |
| 570 |
return $this; |
| 571 |
} |
| 572 |
|
| 573 |
/** |
| 574 |
* Get a number of found products. |
| 575 |
* |
| 576 |
* @return int |
| 577 |
* |
| 578 |
**/ |
| 579 |
public function getFoundCount() { |
| 580 |
return $this->_responseItem('total_found', 0); |
| 581 |
} |
| 582 |
|
| 583 |
/** |
| 584 |
* Get found networks. |
| 585 |
* |
| 586 |
* @return array |
| 587 |
* |
| 588 |
**/ |
| 589 |
public function getNetworks() { |
| 590 |
return $this->_responseItem('networks', array()); |
| 591 |
} |
| 592 |
|
| 593 |
/** |
| 594 |
* Get found merchants. |
| 595 |
* |
| 596 |
* @return array |
| 597 |
* |
| 598 |
**/ |
| 599 |
public function getMerchants() { |
| 600 |
return $this->_responseItem('merchants', array()); |
| 601 |
} |
| 602 |
|
| 603 |
/** |
| 604 |
* Get found price groups. |
| 605 |
* |
| 606 |
* @return array |
| 607 |
* |
| 608 |
**/ |
| 609 |
public function getPriceGroups() { |
| 610 |
return $this->_responseItem('price_groups', array()); |
| 611 |
} |
| 612 |
|
| 613 |
/** |
| 614 |
* Create a request object to use with the API. |
| 615 |
* |
| 616 |
* @return array |
| 617 |
* |
| 618 |
**/ |
| 619 |
public function getParams() { |
| 620 |
$request = array(); |
| 621 |
if($this->_query) { |
| 622 |
$request['query'] = $this->_query; |
| 623 |
} |
| 624 |
if($this->_sort) { |
| 625 |
$request['sort'] = $this->_sort; |
| 626 |
} |
| 627 |
if($this->_fields) { |
| 628 |
$request['fields'] = $this->_fields; |
| 629 |
} |
| 630 |
if($this->_limit) { |
| 631 |
$request['limit'] = $this->_limit; |
| 632 |
} |
| 633 |
if($this->_offset) { |
| 634 |
$request['offset'] = $this->_offset; |
| 635 |
} |
| 636 |
if($this->_priceGroups) { |
| 637 |
$request['price_groups'] = $this->_priceGroups; |
| 638 |
} |
| 639 |
if($this->_excludeDuplicates) { |
| 640 |
$request['exclude_duplicates'] = $this->_excludeDuplicates; |
| 641 |
} |
| 642 |
$request['string_ids'] = 1; |
| 643 |
return $request; |
| 644 |
} |
| 645 |
|
| 646 |
/** |
| 647 |
* Run search and return a list of products. |
| 648 |
* |
| 649 |
* @return array |
| 650 |
* |
| 651 |
**/ |
| 652 |
public function execute() { |
| 653 |
$params = $this->getParams(); |
| 654 |
if(!isset($params['query'])) { |
| 655 |
throw new DatafeedrError("Query can't be empty"); |
| 656 |
} |
| 657 |
$this->_lastResponse = $this->_api->apiCall('search', $params); |
| 658 |
return $this->_responseItem('products', array()); |
| 659 |
} |
| 660 |
|
| 661 |
/** |
| 662 |
* Return the response from the last search. |
| 663 |
* |
| 664 |
* @return array |
| 665 |
* |
| 666 |
**/ |
| 667 |
public function getResponse() { |
| 668 |
return $this->_lastResponse; |
| 669 |
} |
| 670 |
|
| 671 |
protected function _responseItem($prop, $default) { |
| 672 |
if(is_null($this->_lastResponse)) { |
| 673 |
throw new DatafeedrError("Reading from an empty request"); |
| 674 |
} |
| 675 |
if(is_object($this->_lastResponse) && isset($this->_lastResponse->$prop)) { |
| 676 |
return $this->_lastResponse->$prop; |
| 677 |
} |
| 678 |
if(is_array($this->_lastResponse) && isset($this->_lastResponse[$prop])) { |
| 679 |
return $this->_lastResponse[$prop]; |
| 680 |
} |
| 681 |
return $default; |
| 682 |
} |
| 683 |
} |
| 684 |
} |
| 685 |
|
| 686 |
if(!class_exists('DatafeedrAmazonRequest', false)) { |
| 687 |
|
| 688 |
class DatafeedrAmazonRequest |
| 689 |
{ |
| 690 |
protected $_found = -1; |
| 691 |
protected $_api; |
| 692 |
|
| 693 |
const AWS_VERSION = "2011-08-01"; |
| 694 |
|
| 695 |
/** |
| 696 |
* Constructor. |
| 697 |
* |
| 698 |
* @param object $api DatafeedrApi object. |
| 699 |
* |
| 700 |
**/ |
| 701 |
public function __construct($api, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale="US") { |
| 702 |
$this->_hosts = array( |
| 703 |
"CA" => "webservices.amazon.ca", |
| 704 |
"CN" => "webservices.amazon.cn", |
| 705 |
"DE" => "webservices.amazon.de", |
| 706 |
"ES" => "webservices.amazon.es", |
| 707 |
"FR" => "webservices.amazon.fr", |
| 708 |
"IT" => "webservices.amazon.it", |
| 709 |
"JP" => "webservices.amazon.co.jp", |
| 710 |
"UK" => "webservices.amazon.co.uk", |
| 711 |
"US" => "webservices.amazon.com", |
| 712 |
); |
| 713 |
|
| 714 |
$this->_api = $api; |
| 715 |
$this->_params = array(); |
| 716 |
$this->_locale = strtoupper($locale); |
| 717 |
|
| 718 |
if(!isset($this->_hosts[$this->_locale])) { |
| 719 |
throw new DatafeedrError("Invalid Amazon locale"); |
| 720 |
} |
| 721 |
|
| 722 |
$this->_awsAccessKeyId = $awsAccessKeyId; |
| 723 |
$this->_awsSecretKey = $awsSecretKey; |
| 724 |
$this->_awsAssociateTag = $awsAssociateTag; |
| 725 |
} |
| 726 |
|
| 727 |
/** |
| 728 |
* Returns all parameters. |
| 729 |
* |
| 730 |
* @return array |
| 731 |
* |
| 732 |
**/ |
| 733 |
public function getParams() { |
| 734 |
return $this->_params; |
| 735 |
} |
| 736 |
|
| 737 |
protected function _amazonUrl($operation, $params, $defaults=NULL) { |
| 738 |
$params = array_filter($params); |
| 739 |
|
| 740 |
if(!is_null($defaults)) { |
| 741 |
foreach($defaults as $k => $v) |
| 742 |
if(!isset($params[$k])) |
| 743 |
$params[$k] = $v; |
| 744 |
} |
| 745 |
|
| 746 |
$params["Operation"] = $operation; |
| 747 |
$params["Service"] = "AWSECommerceService"; |
| 748 |
$params["AWSAccessKeyId"] = $this->_awsAccessKeyId; |
| 749 |
$params["AssociateTag"] = $this->_awsAssociateTag; |
| 750 |
$params["Version"] = self::AWS_VERSION; |
| 751 |
$params["Timestamp"] = gmdate("Y-m-d\\TH:i:s\\Z"); |
| 752 |
|
| 753 |
ksort($params); |
| 754 |
$query = array(); |
| 755 |
foreach($params as $k => $v) { |
| 756 |
if(is_array($v)) |
| 757 |
$v = implode(',', $v); |
| 758 |
$query []= $k . '=' . rawurlencode($v); |
| 759 |
} |
| 760 |
$query = implode('&', $query); |
| 761 |
$host = $this->_hosts[$this->_locale]; |
| 762 |
$path = "/onca/xml"; |
| 763 |
$subj = sprintf("GET\n%s\n%s\n%s", $host, $path, $query); |
| 764 |
$sign = rawurlencode(base64_encode(hash_hmac("sha256", $subj, $this->_awsSecretKey, TRUE))); |
| 765 |
return "http://{$host}{$path}?{$query}&Signature={$sign}"; |
| 766 |
} |
| 767 |
} |
| 768 |
} |
| 769 |
|
| 770 |
if(!class_exists('DatafeedrAmazonSearchRequest', false)) { |
| 771 |
|
| 772 |
class DatafeedrAmazonSearchRequest extends DatafeedrAmazonRequest |
| 773 |
{ |
| 774 |
/** |
| 775 |
* Add a parameter. |
| 776 |
* |
| 777 |
* @param string $name Parameter name. |
| 778 |
* @param string $value Parameter value. |
| 779 |
* @return $this |
| 780 |
* |
| 781 |
* @see http://docs.aws.amazon.com/AWSECommerceService/latest/DG/ItemSearch.html |
| 782 |
* |
| 783 |
**/ |
| 784 |
public function addParam($name, $value) { |
| 785 |
$this->_params[$name] = $value; |
| 786 |
return $this; |
| 787 |
} |
| 788 |
|
| 789 |
/** |
| 790 |
* Run search and return a list of products. |
| 791 |
* |
| 792 |
* @return array |
| 793 |
* |
| 794 |
**/ |
| 795 |
public function execute() { |
| 796 |
$defaults = array( |
| 797 |
'ResponseGroup' => 'ItemAttributes,Images,OfferFull,BrowseNodes,EditorialReview,VariationSummary', |
| 798 |
'SearchIndex' => 'All', |
| 799 |
); |
| 800 |
$url = $this->_amazonUrl('ItemSearch', $this->_params, $defaults); |
| 801 |
$response = $this->_api->apiCall('amazon_search', array('url' => $url)); |
| 802 |
$this->_found = $response['total_found']; |
| 803 |
return $response['products']; |
| 804 |
} |
| 805 |
|
| 806 |
/** |
| 807 |
* Get a number of found products. |
| 808 |
* |
| 809 |
* @return int |
| 810 |
* |
| 811 |
**/ |
| 812 |
public function getFoundCount() { |
| 813 |
return $this->_found; |
| 814 |
} |
| 815 |
} |
| 816 |
} |
| 817 |
|
| 818 |
if(!class_exists('DatafeedrAmazonLookupRequest', false)) { |
| 819 |
|
| 820 |
class DatafeedrAmazonLookupRequest extends DatafeedrAmazonRequest |
| 821 |
{ |
| 822 |
/** |
| 823 |
* Add a parameter. |
| 824 |
* |
| 825 |
* @param string $name Parameter name - one of 'ASIN', 'SKU', 'UPC', 'EAN', 'ISBN' |
| 826 |
* @param string|array $value Parameter value or an array of values (up to 10). |
| 827 |
* @return $this |
| 828 |
* |
| 829 |
* @see http://docs.aws.amazon.com/AWSECommerceService/latest/DG/ItemLookup.html |
| 830 |
* |
| 831 |
**/ |
| 832 |
public function addParam($name, $value) { |
| 833 |
$this->_params[$name] = $value; |
| 834 |
return $this; |
| 835 |
} |
| 836 |
|
| 837 |
/** |
| 838 |
* Run search and return a list of products. |
| 839 |
* |
| 840 |
* @return array |
| 841 |
* |
| 842 |
**/ |
| 843 |
public function execute() { |
| 844 |
$params = array_filter($this->_params); |
| 845 |
$types = array('ASIN', 'SKU', 'UPC', 'EAN', 'ISBN', 'asin', 'sku', 'upc', 'ean', 'isbn'); |
| 846 |
foreach($types as $type) { |
| 847 |
if(isset($params[$type])) { |
| 848 |
$params['IdType'] = strtoupper($type); |
| 849 |
$params['ItemId'] = $params[$type]; |
| 850 |
unset($params[$type]); |
| 851 |
} |
| 852 |
} |
| 853 |
|
| 854 |
$defaults = array( |
| 855 |
'ResponseGroup' => 'ItemAttributes,Images,OfferFull,BrowseNodes,EditorialReview,VariationSummary', |
| 856 |
); |
| 857 |
if(isset($params['IdType']) && $params['IdType'] != 'ASIN') |
| 858 |
$defaults['SearchIndex'] = 'All'; |
| 859 |
|
| 860 |
$url = $this->_amazonUrl('ItemLookup', $params, $defaults); |
| 861 |
$response = $this->_api->apiCall('amazon_search', array('url' => $url)); |
| 862 |
$this->_found = $response['total_found']; |
| 863 |
return $response['products']; |
| 864 |
} |
| 865 |
|
| 866 |
/** |
| 867 |
* Get a number of found products. |
| 868 |
* |
| 869 |
* @return int |
| 870 |
* |
| 871 |
**/ |
| 872 |
public function getFoundCount() { |
| 873 |
return $this->_found; |
| 874 |
} |
| 875 |
} |
| 876 |
} |
| 877 |
|
| 878 |
if(!class_exists('DatafeedrError', false)) { |
| 879 |
/** |
| 880 |
* Generic Api error. |
| 881 |
**/ |
| 882 |
class DatafeedrError extends Exception |
| 883 |
{ |
| 884 |
} |
| 885 |
} |
| 886 |
|
| 887 |
if(!class_exists('DatafeedrBadRequestError', false)) { |
| 888 |
/** |
| 889 |
* API error: Invalid Request. |
| 890 |
**/ |
| 891 |
class DatafeedrBadRequestError extends DatafeedrError |
| 892 |
{ |
| 893 |
} |
| 894 |
} |
| 895 |
|
| 896 |
if(!class_exists('DatafeedrAuthenticationError', false)) { |
| 897 |
/** |
| 898 |
* API error: Authentication failed. |
| 899 |
**/ |
| 900 |
class DatafeedrAuthenticationError extends DatafeedrError |
| 901 |
{ |
| 902 |
} |
| 903 |
} |
| 904 |
|
| 905 |
if(!class_exists('DatafeedrLimitExceededError', false)) { |
| 906 |
/** |
| 907 |
* API error: Query limit exceeded. |
| 908 |
**/ |
| 909 |
class DatafeedrLimitExceededError extends DatafeedrError |
| 910 |
{ |
| 911 |
} |
| 912 |
} |
| 913 |
|
| 914 |
/** |
| 915 |
* API error: Unspecified HTTP error. |
| 916 |
**/ |
| 917 |
if(!class_exists('DatafeedrHTTPError', false)) { |
| 918 |
class DatafeedrHTTPError extends DatafeedrError |
| 919 |
{ |
| 920 |
} |
| 921 |
} |
| 922 |
|
| 923 |
if(!class_exists('DatafeedrQueryError', false)) { |
| 924 |
/** |
| 925 |
* API error: Error in the search query. |
| 926 |
**/ |
| 927 |
class DatafeedrQueryError extends DatafeedrError |
| 928 |
{ |
| 929 |
} |
| 930 |
} |
| 931 |
|
| 932 |
if(!class_exists('DatafeedrExternalError', false)) { |
| 933 |
/** |
| 934 |
* API error: External service error. |
| 935 |
**/ |
| 936 |
class DatafeedrExternalError extends DatafeedrError |
| 937 |
{ |
| 938 |
} |
| 939 |
} |
| 940 |
|
| 941 |
?> |