PluginProbe
Datafeedr API / 1.1.2
Datafeedr API v1.1.2
1.4.2 1.0.125 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.3 1.0.30 1.0.31 1.0.32 1.0.33 All 181 releases
← All changes | libraries/datafeedr.php +1544 -811 1.0.21.1.2 View file →
@@ -1,941 +1,1674 @@
1 1 <?php
2 2
3 3 /**
4 - * Datafeedr Api Client Library.
4 + * Datafeedr API PHP Library
5 5 *
6 - * @version 0.1b.6544
7 - * @copyright Datafeedr 2007 ~ 2013 - All Rights Reserved
6 + * @version 3.0.0
8 7 *
9 - * @mainpage
8 + * Copyright (c) 2007 ~ 2017, Datafeedr - All Rights Reserved
10 9 *
11 - * Example of use:
10 + * Permission to use, copy, modify, and/or distribute this software for any
11 + * purpose with or without fee is hereby granted, provided that the above
12 + * copyright notice and this permission notice appear in all copies.
12 13 *
13 - * @code
14 - * $api = new DatafeedrApi('<access id>', '<secret key>');
14 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 + */
22 +
23 +/**
24 + * Class DatafeedrApi
15 25 *
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 -**/
26 + * This is the core Datafeedr API class.
27 + */
28 +class DatafeedrApi {
35 29
36 -if(!class_exists('DatafeedrApi', false)) {
30 + protected $_accessId;
31 + protected $_secretKey;
37 32
38 -/**
39 - * Datafeedr API core class.
40 - **/
41 -class DatafeedrApi
42 -{
43 - protected $_accessId;
44 - protected $_secretKey;
33 + protected $_hasZlib;
34 + protected $_host;
35 + protected $_retry;
36 + protected $_retryTimeout;
37 + protected $_returnObjects;
38 + protected $_timeout;
39 + protected $_transport;
40 + protected $_https;
41 + protected $_url;
42 + protected $_userAgent;
45 43
46 - protected $_transport;
47 - protected $_timeout;
48 - protected $_url;
49 - protected $_returnObjects;
44 + protected $_status;
45 + protected $_errors;
50 46
51 - protected $_status;
47 + const SORT_DESCENDING = - 1;
48 + const SORT_ASCENDING = + 1;
52 49
53 - const SORT_DESCENDING = -1;
54 - const SORT_ASCENDING = +1;
50 + const DEFAULT_HOST = 'api.datafeedr.com';
55 51
56 - const DEFAULT_URL = 'http://api.datafeedr.com';
57 - const DEFAULT_TIMEOUT = 30;
52 + const REQUEST_COMPRESSION_THRESHOLD = 1024;
58 53
59 - const VERSION = '0.1b.6544';
54 + const VERSION = '3.0.0';
60 55
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;
56 + /**
57 + * DatafeedrApi constructor.
58 + *
59 + * @param string $accessId Datafeedr API Access ID.
60 + * @param string $secretKey Datafeedr API Secret Key.
61 + * @param array $options Options.
62 + *
63 + * Possble options:
64 + *
65 + * - host: API host name. Default: 'api.datafeedr.com'
66 + * - https: TRUE if using https. Default: FALSE.
67 + * - transport: HTTP transport name or function. Default: 'wordpress'
68 + * - timeout: HTTP connection timeout, in seconds. Default: 0
69 + * - returnObjects: True to return Objects. False to return associative arrays Default: false
70 + * - retry: How many times to repeat a request on a temporary failure. Default: 0 (do not repeat)
71 + * - retryTimeout: Timeout between retry requests, in seconds. Default: 5
72 + *
73 + * The `transport` option tells how HTTP requests should be made.
74 + * It can be either a string that describes one of built-in transports ("curl", "file" or "wordpress"),
75 + * or a callable object that should accept a URL, an array of headers and a string of post data and
76 + * should return an array [int http response status, string response body].
77 + *
78 + *
79 + * @throws DatafeedrError Throws error if any option is invalid.
80 + * @since 1.0.0
81 + *
82 + */
83 + public function __construct( $accessId, $secretKey, $options = null ) {
79 84
80 - $this->_errors = array(
81 - 1 => 'DatafeedrBadRequestError',
82 - 2 => 'DatafeedrAuthenticationError',
83 - 3 => 'DatafeedrLimitExceededError',
84 - 4 => 'DatafeedrQueryError',
85 - 7 => 'DatafeedrExternalError',
86 - 9 => 'DatafeedrError',
87 - );
85 + $this->_accessId = $accessId;
86 + $this->_secretKey = $secretKey;
88 87
89 - $this->_url = self::DEFAULT_URL;
90 - $this->_timeout = $timeout ? $timeout : self::DEFAULT_TIMEOUT;
91 - $this->_returnObjects = $returnObjects;
88 + $this->_errors = array(
89 + 1 => 'DatafeedrBadRequestError',
90 + 2 => 'DatafeedrAuthenticationError',
91 + 3 => 'DatafeedrLimitExceededError',
92 + 4 => 'DatafeedrQueryError',
93 + 7 => 'DatafeedrExternalError',
94 + 9 => 'DatafeedrServerError',
95 + );
92 96
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 - }
97 + $this->_parseOptions( $options );
98 + }
109 99
110 - /**
111 - * Return status information.
112 - *
113 - * @return array
114 - *
115 - **/
116 - public function getStatus() {
117 - $this->apiCall('status');
118 - return $this->_status;
119 - }
100 + /**
101 + * Returns API status information.
102 + *
103 + * @return array An array of API Status information.
104 + * @since 1.0.0
105 + *
106 + */
107 + public function getStatus() {
108 + $this->apiCall( 'status' );
120 109
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 - }
110 + return $this->_status;
111 + }
132 112
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 - }
113 + /**
114 + * Return status information from the last request.
115 + *
116 + * If no API request has been made, return NULL
117 + *
118 + * @return array|null Status information or NULL.
119 + * @since 1.0.0
120 + *
121 + */
122 + public function lastStatus() {
123 + return $this->_status;
124 + }
154 125
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 - }
126 + /**
127 + * Return the list of networks.
128 + *
129 + * @param integer|array $networkId Optional. Network ID or an array of network IDs.
130 + * @param boolean $includeEmpty Optional. If FALSE, omit networks with 0 products.
131 + * @param array $fields Optional. An array of fields to retrieve.
132 + *
133 + * @return array An array of Networks.
134 + * @since 1.0.0
135 + *
136 + */
137 + public function getNetworks( $networkId = null, $includeEmpty = false, $fields = null ) {
176 138
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 - }
139 + $request = array();
196 140
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 - }
141 + if ( $networkId ) {
142 + $request['_ids'] = $this->_intarray( $networkId );
143 + }
212 144
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 - }
145 + $request['skip_empty'] = intval( ! $includeEmpty );
231 146
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 - }
147 + if ( $fields ) {
148 + $request['fields'] = $fields;
149 + }
249 150
250 - /**
251 - * Create a new DatafeedrSearchRequest object.
252 - *
253 - * @return DatafeedrSearchRequest
254 - *
255 - **/
256 - public function searchRequest() {
257 - return new DatafeedrSearchRequest($this);
258 - }
151 + $response = $this->apiCall( 'networks', $request );
259 152
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 - }
153 + return $this->_get( $response, 'networks' );
154 + }
269 155
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 - }
156 + /**
157 + * Return the list of merchants.
158 + *
159 + * @param integer|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. An array of fields to retrieve.
162 + *
163 + * @return array An array of merchants.
164 + * @since 1.0.0
165 + *
166 + */
167 + public function getMerchants( $networkId = null, $includeEmpty = false, $fields = null ) {
279 168
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();
169 + $request = array();
291 170
292 - $request['aid'] = $this->_accessId;
293 - $request['timestamp'] = gmdate('Y-m-d H:i:s');
171 + if ( $networkId ) {
172 + $request['source_ids'] = $this->_intarray( $networkId );
173 + }
294 174
295 - $message = $request['aid'] .$action . $request['timestamp'];
296 - $request['signature'] = hash_hmac('sha256', $message, $this->_secretKey, FALSE);
175 + $request['skip_empty'] = intval( ! $includeEmpty );
297 176
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 - }
177 + if ( $fields ) {
178 + $request['fields'] = $fields;
179 + }
311 180
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 - }
181 + $response = $this->apiCall( 'merchants', $request );
318 182
319 - if($status != 200) {
320 - throw new DatafeedrHTTPError("Status $status");
321 - }
183 + return $this->_get( $response, 'merchants' );
184 + }
322 185
323 - $this->_status = $this->_get($response, 'status');
324 - return $response;
325 - }
186 + /**
187 + * Return a list of merchants by their IDs.
188 + *
189 + * @param integer|array $merchantId Merchant ID or array of Merchant IDs.
190 + * @param boolean $includeEmpty Optional. If FALSE, omit merchants with 0 products.
191 + * @param array $fields Optional. An array of fields to retrieve.
192 + *
193 + * @return array An array of merchants.
194 + * @since 1.0.0
195 + *
196 + */
197 + public function getMerchantsById( $merchantId, $includeEmpty = false, $fields = null ) {
326 198
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 - }
199 + $request = array();
200 + $request['_ids'] = $this->_intarray( $merchantId );
201 + $request['skip_empty'] = intval( ! $includeEmpty );
336 202
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 - }
203 + if ( $fields ) {
204 + $request['fields'] = $fields;
205 + }
344 206
207 + $response = $this->apiCall( 'merchants', $request );
345 208
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);
209 + return $this->_get( $response, 'merchants' );
210 + }
363 211
364 - $response = curl_exec($ch);
365 - $status = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
366 - $errno = curl_errno($ch);
367 - $errmsg = curl_error($ch);
212 + /**
213 + * Return a list of searchable fields.
214 + *
215 + * @param integer|array $networkId Optional. Network ID or array of network IDs.
216 + *
217 + * @return array An array of searchable fields.
218 + * @todo - Does this return a list of all fields for a specific network or only indexed/searchable fields? Update docs accordingly.
219 + *
220 + * @since 1.0.0
221 + *
222 + */
223 + public function getFields( $networkId = null ) {
368 224
369 - curl_close($ch);
225 + $request = array();
370 226
371 - if($errno) {
372 - throw new DatafeedrHTTPError($errmsg, $errno);
373 - }
227 + if ( $networkId ) {
228 + $request['source_ids'] = $this->_intarray( $networkId );
229 + }
374 230
375 - return array($status, $response);
376 - }
231 + $response = $this->apiCall( 'fields', $request );
377 232
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);
233 + return $this->_get( $response, 'fields' );
234 + }
397 235
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 - }
236 + /**
237 + * Return a list of products by their IDs.
238 + *
239 + * @param integer|array $productId Product ID or an array of products IDs.
240 + * @param array $fields Optional. An array of fields to retrieve.
241 + *
242 + * @return array An array of Products.
243 + * @since 1.0.0
244 + *
245 + */
246 + public function getProducts( $productId, $fields = null ) {
407 247
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 = '';
248 + $request = array();
249 + $request['_ids'] = $this->_intarray( $productId );
250 + $request['string_ids'] = 1;
421 251
422 - $fp = fsockopen($parts['host'], 80, $errno, $errmsg, $this->_timeout);
423 - if(!$fp) {
424 - throw new DatafeedrHTTPError($errmsg, $errno);
425 - }
252 + if ( $fields ) {
253 + $request['fields'] = $fields;
254 + }
426 255
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);
256 + $response = $this->apiCall( 'get', $request );
430 257
431 - $buf = '';
432 - while(!feof($fp)) {
433 - $buf .= fgets($fp, 1024);
434 - }
435 - fclose($fp);
258 + return $this->_get( $response, 'products' );
259 + }
436 260
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 - }
261 + /**
262 + * Return a list of Zanox merchant IDs ("zmids").
263 + *
264 + * @param integer|array $merchantId Merchant ID or an array of merchant IDs.
265 + * @param integer $adspaceId Zanox Adspace ID.
266 + * @param string $connectId Zanox Connection ID.
267 + *
268 + * @return array An array of arrays (adspace_id, merchant_id, program_id, zmid).
269 + * @since 1.0.0
270 + *
271 + */
272 + public function getZanoxMerchantIds( $merchantId, $adspaceId, $connectId ) {
273 +
274 + $request = array();
275 +
276 + $request['merchant_ids'] = $this->_intarray( $merchantId );
277 + $request['adspace_id'] = $adspaceId;
278 + $request['connect_id'] = $connectId;
279 +
280 + $response = $this->apiCall( 'zanox_merchant_ids', $request );
281 +
282 + return $this->_get( $response, 'zanox_merchant_ids' );
283 + }
284 +
285 + /**
286 + * Return a list of PerformanceHorizon campaign references ("camrefs").
287 + *
288 + * @param integer|array $merchantId Merchant ID or an array of merchant IDs.
289 + * @param string $applicationKey PerformanceHorizon application_key.
290 + * @param string $userApiKey PerformanceHorizon user_api_key.
291 + * @param string $publisherId PerformanceHorizon publisher_id.
292 + *
293 + * @return array An array of arrays (campaign_id, camref, merchant_id).
294 + * @since 2.0.0
295 + *
296 + */
297 +
298 + public function getPerformanceHorizonCamrefs( $merchantId, $applicationKey, $userApiKey, $publisherId ) {
299 +
300 + $request = array();
301 +
302 + $request['merchant_ids'] = $this->_intarray( $merchantId );
303 + $request['application_key'] = $applicationKey;
304 + $request['user_api_key'] = $userApiKey;
305 + $request['publisher_id'] = $publisherId;
306 +
307 + $response = $this->apiCall( 'performancehorizon_camrefs', $request );
308 +
309 + return $this->_get( $response, 'performancehorizon_camrefs' );
310 + }
311 +
312 + /**
313 + * Return a list of Effiliation affiliate ids.
314 + *
315 + * @param integer|array $merchantId Merchant ID or an array of merchant IDs.
316 + * @param string $apiKey Effiliation api_key.
317 + *
318 + * @return array An array of arrays (affiliate_id, merchant_id).
319 + * @since 2.0.2
320 + *
321 + */
322 +
323 + public function getEffiliationAffiliateIds( $merchantId, $apiKey ) {
324 +
325 + $request = array();
326 +
327 + $request['merchant_ids'] = $this->_intarray( $merchantId );
328 + $request['api_key'] = $apiKey;
329 +
330 + $response = $this->apiCall( 'effiliation_affiliate_ids', $request );
331 +
332 + return $this->_get( $response, 'effiliation_affiliate_ids' );
333 + }
334 +
335 + /**
336 + * Create a new DatafeedrSearchRequest object.
337 + *
338 + * @return DatafeedrSearchRequest
339 + * @since 1.0.0
340 + *
341 + */
342 + public function searchRequest() {
343 + return new DatafeedrSearchRequest( $this );
344 + }
345 +
346 + /**
347 + * Create a new DatafeedrMerchantSearchRequest object.
348 + *
349 + * @return DatafeedrMerchantSearchRequest
350 + * @since 1.0.0
351 + *
352 + */
353 + public function merchantSearchRequest() {
354 + return new DatafeedrMerchantSearchRequest( $this );
355 + }
356 +
357 + /**
358 + * Create a new DatafeedrAmazonSearchRequest object.
359 + *
360 + * @param string $awsAccessKeyId Amazon Access Key.
361 + * @param string $awsSecretKey Amazon Secret Key.
362 + * @param string $awsAssociateTag Amazon Associates tag.
363 + * @param string $locale The country locale code.
364 + *
365 + * @return DatafeedrAmazonSearchRequest
366 + * @since 1.0.0
367 + *
368 + */
369 + public function amazonSearchRequest( $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale = 'US' ) {
370 + return new DatafeedrAmazonSearchRequest( $this, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale );
371 + }
372 +
373 + /**
374 + * Create a new DatafeedrAmazonLookupRequest object.
375 + *
376 + * @param string $awsAccessKeyId Amazon Access Key.
377 + * @param string $awsSecretKey Amazon Secret Key.
378 + * @param string $awsAssociateTag Amazon Associates tag.
379 + * @param string $locale The country locale code.
380 + *
381 + * @return DatafeedrAmazonLookupRequest
382 + * @since 1.0.0
383 + *
384 + */
385 + public function amazonLookupRequest( $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale = 'US' ) {
386 + return new DatafeedrAmazonLookupRequest( $this, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale );
387 + }
388 +
389 + /**
390 + * Perform the raw API call.
391 + *
392 + * @param string $action API Action. (Examples: status, merchants, networks, search, etc...)
393 + * @param array $request Optional. Request data.
394 + *
395 + * @return array Returns $response array.
396 + * @throws DatafeedrHTTPError Throws error if request status is not 200.
397 + *
398 + * @since 1.0.0
399 + *
400 + */
401 + public function apiCall( $action, $request = null ) {
402 +
403 + if ( ! $request ) {
404 + $request = array();
405 + }
406 +
407 + $request['aid'] = $this->_accessId;
408 + $request['timestamp'] = gmdate( 'Y-m-d H:i:s' );
409 +
410 + if ( $this->_https ) {
411 + $request['akey'] = $this->_secretKey;
412 + } else {
413 + $message = $request['aid'] . $action . $request['timestamp'];
414 + $request['signature'] = hash_hmac( 'sha256', $message, $this->_secretKey, false );
415 + }
416 +
417 + $postdata = json_encode( $request );
418 + $url = $this->_url . '/' . $action;
419 + $headers = array(
420 + 'Host: ' . $this->_host,
421 + 'Content-Type: application/json',
422 + 'Accept: application/json',
423 + 'Connection: close',
424 + 'User-Agent: ' . $this->_userAgent
425 + );
426 +
427 + if ( $this->_hasZlib && strlen( $postdata ) >= self::REQUEST_COMPRESSION_THRESHOLD ) {
428 + $postdata = gzcompress( $postdata );
429 + $headers [] = 'Content-Encoding: deflate';
430 + }
431 +
432 + $headers [] = 'Content-Length: ' . strlen( $postdata );
433 +
434 + list( $status, $response ) = $this->_performRequest( $url, $headers, $postdata );
435 +
436 + if ( strlen( $response ) ) {
437 + $response = json_decode( $response, ! $this->_returnObjects );
438 + }
439 +
440 + $error = $this->_get( $response, 'error' );
441 + if ( $error ) {
442 + $type = $this->_get( $response, 'type' );
443 + $cls = isset( $this->_errors[ $type ] ) ? $this->_errors[ $type ] : 'DatafeedrError';
444 + throw new $cls( $this->_get( $response, 'message' ), $error );
445 + }
446 +
447 + if ( 200 != $status ) {
448 + throw new DatafeedrHTTPError( "Status $status" );
449 + }
450 +
451 + $this->_status = $this->_get( $response, 'status' );
452 +
453 + return $response;
454 + }
455 +
456 + /**
457 + * Returns the default set of options.
458 + *
459 + * @since 2.0.0
460 + *
461 + * @returns array $options.
462 + *
463 + */
464 + protected function _defaultOptions() {
465 + return array(
466 + 'host' => self::DEFAULT_HOST,
467 + 'https' => false,
468 + 'transport' => 'wordpress_or_curl',
469 + 'timeout' => 30,
470 + 'returnObjects' => false,
471 + 'retry' => 0,
472 + 'retryTimeout' => 5
473 + );
474 + }
475 +
476 + /**
477 + * Parse constructor options.
478 + *
479 + * @param array $options .
480 + *
481 + * @throws DatafeedrError Throws error if any option is invalid.
482 + * @since 2.0.0
483 + *
484 + */
485 + protected function _parseOptions( $options ) {
486 + $opts = $this->_defaultOptions();
487 +
488 + if ( ! is_null( $options ) ) {
489 + if ( ! is_array( $options ) ) {
490 + throw new DatafeedrError( "Options must be an array" );
491 + }
492 +
493 + foreach ( $options as $key => $value ) {
494 + if ( isset( $opts[ $key ] ) ) {
495 + $opts[ $key ] = $value;
496 + }
497 + }
498 + }
499 +
500 + $pt = $opts['https'] ? 'https' : 'http';
501 + $tr = $opts['transport'];
502 +
503 + $this->_url = $pt . '://' . $opts['host'];
504 + $this->_https = $opts['https'];
505 + $this->_host = $opts['host'];
506 + $this->_timeout = intval( $opts['timeout'] );
507 + $this->_returnObjects = intval( $opts['returnObjects'] );
508 + $this->_retry = intval( $opts['retry'] );
509 + $this->_retryTimeout = intval( $opts['retryTimeout'] );
510 +
511 + switch ( $tr ) {
512 + case 'curl':
513 + $this->_transport = array( $this, '_transportCurl' );
514 + break;
515 + case 'file':
516 + $this->_transport = array( $this, '_transportFile' );
517 + break;
518 + case 'wordpress':
519 + if ( ! function_exists( 'wp_remote_post' ) ) {
520 + throw new DatafeedrError( "Wordpress transport requires wp_remote_post" );
521 + }
522 + $this->_transport = array( $this, '_transportWordpress' );
523 + break;
524 + case 'wordpress_or_curl':
525 + if ( ! function_exists( 'wp_remote_post' ) ) {
526 + $this->_transport = array( $this, '_transportCurl' );
527 + $tr = 'curl';
528 + } else {
529 + $this->_transport = array( $this, '_transportWordpress' );
530 + $tr = 'wordpress';
531 + }
532 + break;
533 +
534 + default:
535 + if ( ! is_callable( $tr ) ) {
536 + throw new DatafeedrError( "Transport must be a function" );
537 + }
538 + $this->_transport = $tr;
539 + $tr = 'custom';
540 + }
541 +
542 + $this->_hasZlib = function_exists( 'gzcompress' );
543 + $this->_userAgent = sprintf( 'datafeedr.php.%s/%s/zlib=%s', self::VERSION, $tr,
544 + $this->_hasZlib ? 'yes' : 'no' );
545 + }
546 +
547 + /**
548 + * Perform an HTTP request.
549 + *
550 + * @param string $url
551 + * @param array $headers
552 + * @param string $postdata
553 + *
554 + * @return array An array of (status, responseBody)
555 + * @throws DatafeedrConnectionError
556 + *
557 + * @since 2.0.0
558 + *
559 + */
560 + protected function _performRequest( $url, $headers, $postdata ) {
561 + $retry = $this->_retry;
562 +
563 + while ( true ) {
564 + try {
565 + return call_user_func( $this->_transport, $url, $headers, $postdata );
566 + } catch ( DatafeedrConnectionError $err ) {
567 + if ( $retry <= 0 ) {
568 + throw $err;
569 + }
570 + sleep( $this->_retryTimeout );
571 + $retry --;
572 + }
573 + }
574 +
575 + return array();
576 + }
577 +
578 + /**
579 + * Convert an ID or an array of IDs to a simple array of IDs.
580 + *
581 + * @param integer|string|array $id_or_ids An ID or an array of IDs.
582 + *
583 + * @return array An array of IDs.
584 + * @since 1.0.0
585 + *
586 + */
587 + protected function _intarray( $id_or_ids ) {
588 +
589 + if ( is_numeric( $id_or_ids ) ) {
590 + return array( $id_or_ids );
591 + }
592 +
593 + if ( is_array( $id_or_ids ) ) {
594 + return array_values( $id_or_ids );
595 + }
596 +
597 + return array();
598 + }
599 +
600 + /**
601 + * Returns a specific value from an array or object for a given key or property. If key or
602 + * property does not exist, returns $default.
603 + *
604 + * @param array|object $obj An array or object to extract value from.
605 + * @param string $prop The array key or object property to get the value for.
606 + * @param null $default Optional. The value to return if $obj or $prop does not exist.
607 + *
608 + * @return mixed|null The returned value.
609 + * @since 1.0.0
610 + *
611 + */
612 + protected function _get( $obj, $prop, $default = null ) {
613 +
614 + if ( is_array( $obj ) && isset( $obj[ $prop ] ) ) {
615 + return $obj[ $prop ];
616 + }
617 +
618 + if ( is_object( $obj ) && isset( $obj->$prop ) ) {
619 + return $obj->$prop;
620 + }
621 +
622 + return $default;
623 + }
624 +
625 + /**
626 + * Perform an HTTP POST request using the CURL library.
627 + *
628 + * @param string $url Request url.
629 + * @param array $headers Array of headers.
630 + * @param string $postdata Post data.
631 + *
632 + * @return array (int http status, string response body)
633 + * @throws DatafeedrConnectionError Throws error if curl_errno() returns an error.
634 + *
635 + * @since 1.0.0
636 + *
637 + */
638 + protected function _transportCurl( $url, $headers, $postdata ) {
639 +
640 + $ch = curl_init();
641 + curl_setopt( $ch, CURLOPT_URL, $url );
642 + curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $this->_timeout );
643 + curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
644 + curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
645 + curl_setopt( $ch, CURLOPT_POST, 1 );
646 + curl_setopt( $ch, CURLOPT_POSTFIELDS, $postdata );
647 + curl_setopt( $ch, CURLOPT_ENCODING, '' );
648 + $response = curl_exec( $ch );
649 + $status = intval( curl_getinfo( $ch, CURLINFO_HTTP_CODE ) );
650 + $errno = curl_errno( $ch );
651 + $errmsg = curl_error( $ch );
652 +
653 + curl_close( $ch );
654 +
655 + if ( $errno ) {
656 + throw new DatafeedrConnectionError( $errmsg, $errno );
657 + }
658 +
659 + return array( $status, $response );
660 + }
661 +
662 + /**
663 + * Perform a HTTP post request using file functions.
664 + *
665 + * @param string $url Request url.
666 + * @param array $headers Array of headers.
667 + * @param string $postdata Post data.
668 + *
669 + * @return array (int http status, string response body)
670 + * @throws DatafeedrConnectionError Throws error if $response is false.
671 + *
672 + * @since 1.0.0
673 + *
674 + */
675 + protected function _transportFile( $url, $headers, $postdata ) {
676 +
677 + $options = array(
678 + 'http' => array(
679 + 'method' => 'POST',
680 + 'content' => $postdata,
681 + 'header' => implode( "\r\n", $headers ),
682 + 'ignore_errors' => true,
683 + 'timeout' => $this->_timeout,
684 + )
685 + );
686 + $context = stream_context_create( $options );
687 + $response = file_get_contents( $url, false, $context );
688 +
689 + $status = 200;
690 + if ( isset( $http_response_header ) && isset( $http_response_header[0] ) ) {
691 + if ( preg_match( '/HTTP.+?(\d\d\d)/', $http_response_header[0], $match ) ) {
692 + $status = intval( $match[1] );
693 + }
694 + } else if ( $response === false ) {
695 + throw new DatafeedrConnectionError( "Invalid response" );
696 + }
697 +
698 + return array( $status, $response );
699 + }
700 +
701 + /**
702 + * Perform a HTTP post request using Wordpress functions.
703 + *
704 + * @param string $url Request url.
705 + * @param array $headers Array of headers.
706 + * @param string $postdata Post data.
707 + *
708 + * @return array (int http status, string response body)
709 + * @throws DatafeedrHTTPError Throws error if $response is WP_Error.
710 + *
711 + * @throws DatafeedrConnectionError Throws error if $response is WP_Error and the error code is 'http_request_failed'.
712 + * @since 1.0.0
713 + *
714 + */
715 + protected function _transportWordpress( $url, $headers, $postdata ) {
716 +
717 + $ha = array();
718 + foreach ( $headers as $h ) {
719 + $h = explode( ':', $h, 2 );
720 + $ha[ strtolower( $h[0] ) ] = $h[1];
721 + }
722 +
723 + $args = array(
724 + 'method' => 'POST',
725 + 'headers' => $ha,
726 + 'body' => $postdata,
727 + 'httpversion' => '1.1',
728 + 'timeout' => $this->_timeout,
729 + 'blocking' => true,
730 + 'compress' => false,
731 + 'decompress' => true,
732 + 'user-agent' => $ha['user-agent']
733 + );
734 +
735 + $res = wp_remote_post( $url, $args );
736 +
737 + if ( is_wp_error( $res ) ) {
738 + $code = $res->get_error_code();
739 + $message = $res->get_error_message();
740 +
741 + if ( $code === 'http_request_failed' ) {
742 + throw new DatafeedrConnectionError( $message );
743 + } else {
744 + throw new DatafeedrHTTPError( $message );
745 + }
746 + }
747 +
748 + return array( $res['response']['code'], $res['body'] );
749 + }
448 750 }
751 +
752 +/**
753 + * Generic Datafeedr API search request.
754 + */
755 +class DatafeedrSearchRequestBase {
756 +
757 + /**
758 + * The DatafeedrAPI object.
759 + *
760 + * @since 1.0.0
761 + * @var DatafeedrAPI $_api
762 + */
763 + protected $_api;
764 +
765 + /**
766 + * An array containing the full response of the last API request.
767 + *
768 + * @since 1.0.0
769 + * @var array $_lastResponse Response array.
770 + */
771 + protected $_lastResponse;
772 +
773 + /**
774 + * DatafeedrSearchRequestBase constructor.
775 + *
776 + * @param DatafeedrApi $api
777 + *
778 + * @since 1.0.0
779 + *
780 + */
781 + public function __construct( $api ) {
782 + $this->_api = $api;
783 + }
784 +
785 + /**
786 + * Get the number of found products.
787 + *
788 + * @return integer
789 + * @since 1.0.0
790 + *
791 + */
792 + public function getFoundCount() {
793 + return $this->_responseItem( 'found_count', 0 );
794 + }
795 +
796 + /**
797 + * Get the number of products that can be retrieved from the server.
798 + *
799 + * @return integer
800 + * @since 1.0.0
801 + *
802 + */
803 + public function getResultCount() {
804 + return $this->_responseItem( 'result_count', 0 );
805 + }
806 +
807 + /**
808 + * Returns the full response from the last search.
809 + *
810 + * Possible items in array include the following:
811 + *
812 + * Array (
813 + * 'found_count' => integer,
814 + * 'length' => integer,
815 + * 'merchants' => array,
816 + * 'networks' => array,
817 + * 'price_groups' => array,
818 + * 'products' => array,
819 + * 'result_count' => integer,
820 + * 'status' => array,
821 + * 'time' => integer,
822 + * 'version' => string,
823 + * )
824 + *
825 + * @return array An array of the full response.
826 + * @since 1.0.0
827 + *
828 + */
829 + public function getResponse() {
830 + return $this->_lastResponse;
831 + }
832 +
833 + /**
834 + * Returns a specific item or property from the response data.
835 + *
836 + * @param string $prop The item or property to get from the response array or object.
837 + * @param mixed $default Return if $prop is not found in the array or object.
838 + *
839 + * @return mixed Specific item or property from response data.
840 + * @throws DatafeedrError Throws error if $this->_lastResponse is NULL.
841 + *
842 + * @since 1.0.0
843 + *
844 + */
845 + protected function _responseItem( $prop, $default ) {
846 +
847 + if ( is_null( $this->_lastResponse ) ) {
848 + throw new DatafeedrError( "Reading from an empty request" );
849 + }
850 +
851 + if ( is_object( $this->_lastResponse ) && isset( $this->_lastResponse->$prop ) ) {
852 + return $this->_lastResponse->$prop;
853 + }
854 +
855 + if ( is_array( $this->_lastResponse ) && isset( $this->_lastResponse[ $prop ] ) ) {
856 + return $this->_lastResponse[ $prop ];
857 + }
858 +
859 + return $default;
860 + }
861 +
862 + /**
863 + * Sets the $_lastResponse property to the the full response from the last API request.
864 + *
865 + * @param string $action The request action (ex. status, search, merchants, networks, etc...)
866 + * @param array $request The current API request.
867 + *
868 + * @since 1.0.0
869 + *
870 + */
871 + function _apiCall( $action, $request = null ) {
872 + $this->_lastResponse = $this->_api->apiCall( $action, $request );
873 + }
449 874 }
450 875
451 -if(!class_exists('DatafeedrSearchRequest', false)) {
452 -
453 876 /**
877 + * Class DatafeedrSearchRequest
878 + *
454 879 * Search request for Datafeedr API.
455 -**/
456 -class DatafeedrSearchRequest
457 -{
458 - protected $_api;
880 + */
881 +class DatafeedrSearchRequest extends DatafeedrSearchRequestBase {
459 882
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 - }
883 + protected $_query;
884 + protected $_sort;
885 + protected $_fields;
886 + protected $_limit;
887 + protected $_offset;
888 + protected $_priceGroups;
889 + protected $_excludeDuplicates;
890 + protected $_merchantLimit;
477 891
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 - }
892 + /**
893 + * DatafeedrSearchRequest constructor.
894 + *
895 + * @param DatafeedrApi $api
896 + *
897 + * @since 1.0.0
898 + *
899 + */
900 + public function __construct( $api ) {
489 901
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 - }
902 + parent::__construct( $api );
510 903
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 - }
904 + $this->_query = array();
905 + $this->_sort = array();
906 + $this->_fields = array();
907 + $this->_limit = 0;
908 + $this->_offset = 0;
909 + $this->_priceGroups = 0;
910 + $this->_excludeDuplicates = '';
911 + $this->_merchantLimit = 0;
912 + }
522 913
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 - }
914 + /**
915 + * Add a query filter.
916 + *
917 + * @param string $filter Query filter.
918 + *
919 + * @return DatafeedrSearchRequest Returns $this.
920 + * @since 1.0.0
921 + *
922 + */
923 + public function addFilter( $filter ) {
924 + $this->_query[] = $filter;
536 925
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 - }
926 + return $this;
927 + }
548 928
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 - }
929 + /**
930 + * Adds a sort field.
931 + *
932 + * @param string $field Field name.
933 + * @param integer $order One of DatafeedrApi::SORT_ASCENDING or DatafeedrApi::SORT_DESCENDING
934 + *
935 + * @return DatafeedrSearchRequest Returns $this.
936 + * @throws DatafeedrError Throws error if sort order is invalid.
937 + *
938 + * @since 1.0.0
939 + *
940 + */
941 + public function addSort( $field, $order = DatafeedrApi::SORT_ASCENDING ) {
560 942
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 - }
943 + if ( strlen( $field ) && ( $field[0] == '+' || $field[0] == '-' ) ) {
944 + $this->_sort [] = $field;
945 + } else if ( $order == DatafeedrApi::SORT_ASCENDING ) {
946 + $this->_sort [] = '+' . $field;
947 + } else if ( $order == DatafeedrApi::SORT_DESCENDING ) {
948 + $this->_sort [] = '-' . $field;
949 + } else {
950 + throw new DatafeedrError( "Invalid sort order" );
951 + }
572 952
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 - }
953 + return $this;
954 + }
582 955
583 - /**
584 - * Get found networks.
585 - *
586 - * @return array
587 - *
588 - **/
589 - public function getNetworks() {
590 - return $this->_responseItem('networks', array());
591 - }
956 + /**
957 + * Set which fields to retrieve.
958 + *
959 + * @param array $fields An array of fields to return for each requested item.
960 + *
961 + * Example:
962 + *
963 + * Array (
964 + * 'name',
965 + * 'price',
966 + * 'description',
967 + * 'url',
968 + * )
969 + *
970 + * @return DatafeedrSearchRequest Returns $this.
971 + * @since 1.0.0
972 + *
973 + */
974 + public function setFields( $fields ) {
975 + $this->_fields = $fields;
592 976
593 - /**
594 - * Get found merchants.
595 - *
596 - * @return array
597 - *
598 - **/
599 - public function getMerchants() {
600 - return $this->_responseItem('merchants', array());
601 - }
977 + return $this;
978 + }
602 979
603 - /**
604 - * Get found price groups.
605 - *
606 - * @return array
607 - *
608 - **/
609 - public function getPriceGroups() {
610 - return $this->_responseItem('price_groups', array());
611 - }
980 + /**
981 + * Exclude duplicate filter.
982 + *
983 + * @param string $filter Equality filter in form "field1 field2 | field3".
984 + *
985 + * @return DatafeedrSearchRequest Returns $this.
986 + * @since 1.0.0
987 + *
988 + */
989 + public function excludeDuplicates( $filter ) {
612 990
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 - }
991 + if ( is_array( $filter ) ) {
992 + $filter = implode( ' ', $filter );
993 + }
645 994
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 - }
995 + $this->_excludeDuplicates = $filter;
660 996
661 - /**
662 - * Return the response from the last search.
663 - *
664 - * @return array
665 - *
666 - **/
667 - public function getResponse() {
668 - return $this->_lastResponse;
669 - }
997 + return $this;
998 + }
670 999
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 - }
1000 + /**
1001 + * Set a limit of number of records to return.
1002 + *
1003 + * @param integer $limit The maximum number of records to return.
1004 + *
1005 + * @return DatafeedrSearchRequest Returns $this.
1006 + * @since 1.0.0
1007 + *
1008 + */
1009 + public function setLimit( $limit ) {
1010 + $this->_limit = $limit;
1011 +
1012 + return $this;
1013 + }
1014 +
1015 + /**
1016 + * Set an offset for the search.
1017 + *
1018 + * @param integer $offset The offset.
1019 + *
1020 + * @return DatafeedrSearchRequest Returns $this.
1021 + * @since 1.0.0
1022 + *
1023 + */
1024 + public function setOffset( $offset ) {
1025 + $this->_offset = $offset;
1026 +
1027 + return $this;
1028 + }
1029 +
1030 + /**
1031 + * Set a limit of results by merchant.
1032 + *
1033 + * @param integer $limit The limit.
1034 + *
1035 + * @return DatafeedrSearchRequest Returns $this.
1036 + * @since 2.0.3
1037 + *
1038 + */
1039 + public function setMerchantLimit( $limit ) {
1040 + $this->_merchantLimit = $limit;
1041 +
1042 + return $this;
1043 + }
1044 +
1045 + /**
1046 + * Set a price group count.
1047 + *
1048 + * This should be used in conjunction with the $this->getPriceGroups() method.
1049 + *
1050 + * Setting $groups to 3 will organize the products into 3 price groups like this:
1051 + *
1052 + * Array (
1053 + * [0] => Array (
1054 + * [product_count] => 321
1055 + * [max] => 9633332
1056 + * [min] => 0
1057 + * )
1058 + * [1] => Array (
1059 + * [product_count] => 116
1060 + * [max] => 19266665
1061 + * [min] => 9633333
1062 + * )
1063 + * [2] => Array (
1064 + * [product_count] => 43
1065 + * [max] => 28900000
1066 + * [min] => 19266666
1067 + * )
1068 + * )
1069 + *
1070 + * @param integer $groups Number of price groups to create.
1071 + *
1072 + * @return DatafeedrSearchRequest Returns $this.
1073 + * @since 1.0.0
1074 + *
1075 + */
1076 + public function setPriceGroups( $groups ) {
1077 + $this->_priceGroups = $groups;
1078 +
1079 + return $this;
1080 + }
1081 +
1082 + /**
1083 + * Get networks found in this request.
1084 + *
1085 + * @return array
1086 + * @since 1.0.0
1087 + *
1088 + */
1089 + public function getNetworks() {
1090 + return $this->_responseItem( 'networks', array() );
1091 + }
1092 +
1093 + /**
1094 + * Get merchants found in this request.
1095 + *
1096 + * @return array
1097 + * @since 1.0.0
1098 + *
1099 + */
1100 + public function getMerchants() {
1101 + return $this->_responseItem( 'merchants', array() );
1102 + }
1103 +
1104 + /**
1105 + * Get price groups found in this request.
1106 + *
1107 + * This must be used in conjunction with $this->setPriceGroups();
1108 + *
1109 + * Returns an array like this:
1110 + *
1111 + * Array (
1112 + * [0] => Array (
1113 + * [product_count] => 321
1114 + * [max] => 9633332
1115 + * [min] => 0
1116 + * )
1117 + * [1] => Array (
1118 + * [product_count] => 116
1119 + * [max] => 19266665
1120 + * [min] => 9633333
1121 + * )
1122 + * [2] => Array (
1123 + * [product_count] => 43
1124 + * [max] => 28900000
1125 + * [min] => 19266666
1126 + * )
1127 + * )
1128 + *
1129 + * @return array An array of price groups and the product count, min and max prices in each group.
1130 + * @since 1.0.0
1131 + *
1132 + */
1133 + public function getPriceGroups() {
1134 + return $this->_responseItem( 'price_groups', array() );
1135 + }
1136 +
1137 + /**
1138 + * Create a request array to use for querying the API.
1139 + *
1140 + * @return array The $request array().
1141 + * @since 1.0.0
1142 + *
1143 + */
1144 + public function getParams() {
1145 +
1146 + $request = array();
1147 +
1148 + if ( $this->_query ) {
1149 + $request['query'] = $this->_query;
1150 + }
1151 +
1152 + if ( $this->_sort ) {
1153 + $request['sort'] = $this->_sort;
1154 + }
1155 +
1156 + if ( $this->_fields ) {
1157 + $request['fields'] = $this->_fields;
1158 + }
1159 +
1160 + if ( $this->_limit ) {
1161 + $request['limit'] = $this->_limit;
1162 + }
1163 +
1164 + if ( $this->_offset ) {
1165 + $request['offset'] = $this->_offset;
1166 + }
1167 +
1168 + if ( $this->_priceGroups ) {
1169 + $request['price_groups'] = $this->_priceGroups;
1170 + }
1171 +
1172 + if ( $this->_merchantLimit ) {
1173 + $request['merchant_limit'] = $this->_merchantLimit;
1174 + }
1175 +
1176 + if ( $this->_excludeDuplicates ) {
1177 + $request['exclude_duplicates'] = $this->_excludeDuplicates;
1178 + }
1179 +
1180 + $request['string_ids'] = 1;
1181 +
1182 + return $request;
1183 + }
1184 +
1185 + /**
1186 + * Run search and return a list of products.
1187 + *
1188 + * @return array An array of products.
1189 + * @throws DatafeedrError Throw error if query is empty.
1190 + *
1191 + * @since 1.0.0
1192 + *
1193 + */
1194 + public function execute() {
1195 +
1196 + $params = $this->getParams();
1197 +
1198 + if ( ! isset( $params['query'] ) ) {
1199 + throw new DatafeedrError( "Query can't be empty" );
1200 + }
1201 +
1202 + $this->_apiCall( 'search', $params );
1203 +
1204 + return $this->_responseItem( 'products', array() );
1205 + }
683 1206 }
1207 +
1208 +/**
1209 + * Search request for Datafeedr Merchants.
1210 + */
1211 +class DatafeedrMerchantSearchRequest extends DatafeedrSearchRequestBase {
1212 +
1213 + protected $_query;
1214 + protected $_sort;
1215 + protected $_fields;
1216 + protected $_limit;
1217 + protected $_offset;
1218 +
1219 + /**
1220 + * DatafeedrMerchantSearchRequest constructor.
1221 + *
1222 + * @param DatafeedrApi $api
1223 + *
1224 + * @since 1.0.0
1225 + *
1226 + */
1227 + public function __construct( $api ) {
1228 +
1229 + parent::__construct( $api );
1230 +
1231 + $this->_query = array();
1232 + $this->_sort = array();
1233 + $this->_fields = array();
1234 + $this->_limit = 0;
1235 + $this->_offset = 0;
1236 + }
1237 +
1238 + /**
1239 + * Add a query filter.
1240 + *
1241 + * @param string $filter Query filter.
1242 + *
1243 + * @return DatafeedrMerchantSearchRequest Returns $this.
1244 + * @since 1.0.0
1245 + *
1246 + */
1247 + public function addFilter( $filter ) {
1248 + $this->_query [] = $filter;
1249 +
1250 + return $this;
1251 + }
1252 +
1253 + /**
1254 + * Add a sort field.
1255 + *
1256 + * @param string $field Field name.
1257 + * @param integer $order One of DatafeedrApi::SORT_ASCENDING or DatafeedrApi::SORT_DESCENDING
1258 + *
1259 + * @return DatafeedrMerchantSearchRequest Returns $this.
1260 + * @throws DatafeedrError Throw error if invalid sort order.
1261 + *
1262 + * @since 1.0.0
1263 + *
1264 + */
1265 + public function addSort( $field, $order = DatafeedrApi::SORT_ASCENDING ) {
1266 +
1267 + if ( strlen( $field ) && ( $field[0] == '+' || $field[0] == '-' ) ) {
1268 + $this->_sort [] = $field;
1269 + } else if ( $order == DatafeedrApi::SORT_ASCENDING ) {
1270 + $this->_sort [] = '+' . $field;
1271 + } else if ( $order == DatafeedrApi::SORT_DESCENDING ) {
1272 + $this->_sort [] = '-' . $field;
1273 + } else {
1274 + throw new DatafeedrError( "Invalid sort order" );
1275 + }
1276 +
1277 + return $this;
1278 + }
1279 +
1280 + /**
1281 + * Set which fields to retrieve.
1282 + *
1283 + * @param array $fields An array of field names.
1284 + *
1285 + * @return DatafeedrMerchantSearchRequest Returns $this.
1286 + * @since 1.0.0
1287 + *
1288 + */
1289 + public function setFields( $fields ) {
1290 + $this->_fields = $fields;
1291 +
1292 + return $this;
1293 + }
1294 +
1295 + /**
1296 + * Set a limit.
1297 + *
1298 + * @param integer $limit Number of items to return.
1299 + *
1300 + * @return DatafeedrMerchantSearchRequest Returns $this.
1301 + * @since 1.0.0
1302 + *
1303 + */
1304 + public function setLimit( $limit ) {
1305 + $this->_limit = $limit;
1306 +
1307 + return $this;
1308 + }
1309 +
1310 + /**
1311 + * Set an offset.
1312 + *
1313 + * @param integer $offset The offset.
1314 + *
1315 + * @return DatafeedrMerchantSearchRequest Returns $this.
1316 + * @since 1.0.0
1317 + *
1318 + */
1319 + public function setOffset( $offset ) {
1320 + $this->_offset = $offset;
1321 +
1322 + return $this;
1323 + }
1324 +
1325 + /**
1326 + * Get networks found in this request.
1327 + *
1328 + * @return array
1329 + * @since 1.0.0
1330 + *
1331 + */
1332 + public function getNetworks() {
1333 + return $this->_responseItem( 'networks', array() );
1334 + }
1335 +
1336 + /**
1337 + * Get merchants found in this request.
1338 + *
1339 + * @return array
1340 + * @since 1.0.0
1341 + *
1342 + */
1343 + public function getMerchants() {
1344 + return $this->_responseItem( 'merchants', array() );
1345 + }
1346 +
1347 + /**
1348 + * Run search and return an array of merchants.
1349 + *
1350 + * @return array Array of merchants
1351 + * @throws DatafeedrError Throws error if query is empty.
1352 + *
1353 + * @since 1.0.0
1354 + *
1355 + */
1356 + public function execute() {
1357 +
1358 + $params = $this->getParams();
1359 +
1360 + if ( ! isset( $params['query'] ) ) {
1361 + throw new DatafeedrError( "Query can't be empty" );
1362 + }
1363 +
1364 + $this->_apiCall( 'merchant_search', $params );
1365 +
1366 + return $this->_responseItem( 'merchants', array() );
1367 + }
1368 +
1369 + /**
1370 + * Create a request array to use for querying the API.
1371 + *
1372 + * @return array The $request array().
1373 + * @since 1.0.0
1374 + *
1375 + */
1376 + public function getParams() {
1377 +
1378 + $request = array();
1379 +
1380 + if ( $this->_query ) {
1381 + $request['query'] = $this->_query;
1382 + }
1383 +
1384 + if ( $this->_sort ) {
1385 + $request['sort'] = $this->_sort;
1386 + }
1387 +
1388 + if ( $this->_fields ) {
1389 + $request['fields'] = $this->_fields;
1390 + }
1391 +
1392 + if ( $this->_limit ) {
1393 + $request['limit'] = $this->_limit;
1394 + }
1395 +
1396 + if ( $this->_offset ) {
1397 + $request['offset'] = $this->_offset;
1398 + }
1399 +
1400 + return $request;
1401 + }
684 1402 }
685 1403
686 -if(!class_exists('DatafeedrAmazonRequest', false)) {
1404 +/**
1405 + * Generic Amazon request.
1406 + */
1407 +class DatafeedrAmazonRequest extends DatafeedrSearchRequestBase {
687 1408
688 -class DatafeedrAmazonRequest
689 -{
690 - protected $_found = -1;
691 - protected $_api;
1409 + protected $_found = - 1;
692 1410
693 - const AWS_VERSION = "2011-08-01";
1411 + protected $_hosts;
1412 + protected $_params;
1413 + protected $_locale;
1414 + protected $_awsAccessKeyId;
1415 + protected $_awsSecretKey;
1416 + protected $_awsAssociateTag;
694 1417
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 - );
1418 + const AWS_VERSION = "2011-08-01";
713 1419
714 - $this->_api = $api;
715 - $this->_params = array();
716 - $this->_locale = strtoupper($locale);
1420 + /**
1421 + * DatafeedrAmazonRequest constructor
1422 + *
1423 + * @param DatafeedrApi $api
1424 + * @param string $awsAccessKeyId Amazon Access Key.
1425 + * @param string $awsSecretKey Amazon Secret Key.
1426 + * @param string $awsAssociateTag Amazon Associate tag.
1427 + * @param string $locale Optional. Amazon locale (two-letter code).
1428 + *
1429 + * @throws DatafeedrError Throws error if Amazon Locale is invalid.
1430 + * @since 1.0.0
1431 + *
1432 + */
1433 + public function __construct( $api, $awsAccessKeyId, $awsSecretKey, $awsAssociateTag, $locale = 'US' ) {
717 1434
718 - if(!isset($this->_hosts[$this->_locale])) {
719 - throw new DatafeedrError("Invalid Amazon locale");
720 - }
1435 + parent::__construct( $api );
721 1436
722 - $this->_awsAccessKeyId = $awsAccessKeyId;
723 - $this->_awsSecretKey = $awsSecretKey;
724 - $this->_awsAssociateTag = $awsAssociateTag;
725 - }
1437 + $this->_hosts = array(
1438 + 'AU' => 'webservices.amazon.com.au',
1439 + 'BR' => 'webservices.amazon.com.br',
1440 + 'CA' => 'webservices.amazon.ca',
1441 + 'FR' => 'webservices.amazon.fr',
1442 + 'DE' => 'webservices.amazon.de',
1443 + 'IN' => 'webservices.amazon.in',
1444 + 'IT' => 'webservices.amazon.it',
1445 + 'JP' => 'webservices.amazon.co.jp',
1446 + 'MX' => 'webservices.amazon.com.mx',
1447 + 'NL' => 'webservices.amazon.nl',
1448 + 'SG' => 'webservices.amazon.sg',
1449 + 'SA' => 'webservices.amazon.sa',
1450 + 'ES' => 'webservices.amazon.es',
1451 + 'SE' => 'webservices.amazon.se',
1452 + 'TR' => 'webservices.amazon.com.tr',
1453 + 'AE' => 'webservices.amazon.ae',
1454 + 'UK' => 'webservices.amazon.co.uk',
1455 + 'US' => 'webservices.amazon.com',
1456 + );
726 1457
727 - /**
728 - * Returns all parameters.
729 - *
730 - * @return array
731 - *
732 - **/
733 - public function getParams() {
734 - return $this->_params;
735 - }
1458 + $this->_params = array();
1459 + $this->_locale = strtoupper( $locale );
736 1460
737 - protected function _amazonUrl($operation, $params, $defaults=NULL) {
738 - $params = array_filter($params);
1461 + if ( ! isset( $this->_hosts[ $this->_locale ] ) ) {
1462 + throw new DatafeedrError( 'Invalid Amazon locale' );
1463 + }
739 1464
740 - if(!is_null($defaults)) {
741 - foreach($defaults as $k => $v)
742 - if(!isset($params[$k]))
743 - $params[$k] = $v;
744 - }
1465 + $this->_awsAccessKeyId = $awsAccessKeyId;
1466 + $this->_awsSecretKey = $awsSecretKey;
1467 + $this->_awsAssociateTag = $awsAssociateTag;
1468 + }
745 1469
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");
1470 + /**
1471 + * Returns all parameters.
1472 + *
1473 + * @return array
1474 + * @since 1.0.0
1475 + *
1476 + */
1477 + public function getParams() {
1478 + return $this->_params;
1479 + }
752 1480
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 - }
1481 + /**
1482 + * Prepare am API request for Amazon.
1483 + *
1484 + * @param string $operation
1485 + * @param array $params
1486 + *
1487 + * @return array
1488 + * @since 3.0.0
1489 + *
1490 + */
1491 + protected function _amazonRequest( $operation, $params ) {
1492 + return array(
1493 + 'amz_access' => $this->_awsAccessKeyId,
1494 + 'amz_key' => $this->_awsSecretKey,
1495 + 'amz_tag' => $this->_awsAssociateTag,
1496 + 'locale' => $this->_locale,
1497 + 'operation' => $operation,
1498 + 'params' => $params,
1499 + );
1500 + }
767 1501 }
768 -}
769 1502
770 -if(!class_exists('DatafeedrAmazonSearchRequest', false)) {
1503 +/**
1504 + * Class DatafeedrAmazonSearchRequest
1505 + *
1506 + * Amazon search request class.
1507 + */
1508 +class DatafeedrAmazonSearchRequest extends DatafeedrAmazonRequest {
771 1509
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 - }
1510 + /**
1511 + * Add a parameter.
1512 + *
1513 + * @param string $name Parameter name.
1514 + * @param string $value Parameter value.
1515 + *
1516 + * @return DatafeedrAmazonSearchRequest Returns $this.
1517 + *
1518 + * @since 1.0.0
1519 + *
1520 + * @see https://webservices.amazon.com/paapi5/documentation/search-items.html#ItemLookup-rp
1521 + */
1522 + public function addParam( $name, $value ) {
1523 + $this->_params[ $name ] = $value;
788 1524
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 - }
1525 + return $this;
1526 + }
805 1527
806 - /**
807 - * Get a number of found products.
808 - *
809 - * @return int
810 - *
811 - **/
812 - public function getFoundCount() {
813 - return $this->_found;
814 - }
1528 + /**
1529 + * Run search and return an array of products.
1530 + *
1531 + * IMPORTANT - The Amazon API returns a MAXIMUM of 10 products per API request and a maximum of
1532 + * 50 products per search query.
1533 + *
1534 + * @return array An array of products.
1535 + * @throws DatafeedrError
1536 + * @since 1.0.0
1537 + *
1538 + */
1539 + public function execute() {
1540 +
1541 + $params = array_filter( $this->_params );
1542 + $req = $this->_amazonRequest( 'SearchItems', $params );
1543 + $this->_apiCall( 'amazon_find', $req );
1544 +
1545 + return $this->_responseItem( 'products', array() );
1546 + }
815 1547 }
816 -}
817 1548
818 -if(!class_exists('DatafeedrAmazonLookupRequest', false)) {
1549 +/**
1550 + * Class DatafeedrAmazonLookupRequest
1551 + *
1552 + * Amazon lookup request.
1553 + */
1554 +class DatafeedrAmazonLookupRequest extends DatafeedrAmazonRequest {
819 1555
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 - }
1556 + /**
1557 + * Add a parameter.
1558 + *
1559 + * @param string $name Parameter name - one of 'ASIN', 'SKU', 'UPC', 'EAN', 'ISBN'
1560 + * @param string|array $value Parameter value or an array of values (up to 10).
1561 + *
1562 + * @return DatafeedrAmazonLookupRequest Returns $this.
1563 + *
1564 + * @since 1.0.0
1565 + *
1566 + * @see https://webservices.amazon.com/paapi5/documentation/get-items.html#ItemLookup-rp
1567 + */
1568 + public function addParam( $name, $value ) {
1569 + $this->_params[ $name ] = $value;
836 1570
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 - }
1571 + return $this;
1572 + }
853 1573
854 - $defaults = array(
855 - 'ResponseGroup' => 'ItemAttributes,Images,OfferFull,BrowseNodes,EditorialReview,VariationSummary',
856 - );
857 - if(isset($params['IdType']) && $params['IdType'] != 'ASIN')
858 - $defaults['SearchIndex'] = 'All';
1574 + /**
1575 + * Run search and return an array of products.
1576 + *
1577 + * @return array An array of products.
1578 + * @since 1.0.0
1579 + *
1580 + */
1581 + public function execute() {
859 1582
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 - }
1583 + $params = array_filter( $this->_params );
1584 + $types = array( 'ASIN', 'SKU', 'UPC', 'EAN', 'ISBN', 'asin', 'sku', 'upc', 'ean', 'isbn' );
865 1585
866 - /**
867 - * Get a number of found products.
868 - *
869 - * @return int
870 - *
871 - **/
872 - public function getFoundCount() {
873 - return $this->_found;
874 - }
1586 + foreach ( $types as $type ) {
1587 + if ( isset( $params[ $type ] ) ) {
1588 + $params['ItemIdType'] = strtoupper( $type );
1589 + $params['ItemIds'] = $params[ $type ];
1590 + if ( is_string( $params['ItemIds'] ) ) {
1591 + $params['ItemIds'] = explode( ',', $params['ItemIds'] );
1592 + }
1593 + unset( $params[ $type ] );
1594 + }
1595 + }
1596 +
1597 + $req = $this->_amazonRequest( 'GetItems', $params );
1598 + $this->_apiCall( 'amazon_find', $req );
1599 +
1600 + return $this->_responseItem( 'products', array() );
1601 + }
875 1602 }
876 -}
877 1603
878 -if(!class_exists('DatafeedrError', false)) {
879 1604 /**
1605 + * Class DatafeedrError.
1606 + *
880 1607 * Generic Api error.
881 -**/
882 -class DatafeedrError extends Exception
883 -{
1608 + */
1609 +class DatafeedrError extends Exception {
884 1610 }
885 -}
886 1611
887 -if(!class_exists('DatafeedrBadRequestError', false)) {
888 1612 /**
1613 + * Class DatafeedrBadRequestError.
1614 + *
889 1615 * API error: Invalid Request.
890 -**/
891 -class DatafeedrBadRequestError extends DatafeedrError
892 -{
1616 + */
1617 +class DatafeedrBadRequestError extends DatafeedrError {
893 1618 }
894 -}
895 1619
896 -if(!class_exists('DatafeedrAuthenticationError', false)) {
897 1620 /**
1621 + * Class DatafeedrAuthenticationError.
1622 + *
898 1623 * API error: Authentication failed.
899 -**/
900 -class DatafeedrAuthenticationError extends DatafeedrError
901 -{
1624 + */
1625 +class DatafeedrAuthenticationError extends DatafeedrError {
902 1626 }
903 -}
904 1627
905 -if(!class_exists('DatafeedrLimitExceededError', false)) {
906 1628 /**
1629 + * Class DatafeedrLimitExceededError.
1630 + *
907 1631 * API error: Query limit exceeded.
908 -**/
909 -class DatafeedrLimitExceededError extends DatafeedrError
910 -{
1632 + */
1633 +class DatafeedrLimitExceededError extends DatafeedrError {
911 1634 }
912 -}
913 1635
914 1636 /**
1637 + * Class DatafeedrHTTPError.
1638 + *
915 1639 * API error: Unspecified HTTP error.
916 -**/
917 -if(!class_exists('DatafeedrHTTPError', false)) {
918 -class DatafeedrHTTPError extends DatafeedrError
919 -{
1640 + */
1641 +class DatafeedrHTTPError extends DatafeedrError {
920 1642 }
1643 +
1644 +/**
1645 + * Class DatafeedrConnectionError.
1646 + *
1647 + * API error: Connection error.
1648 + */
1649 +class DatafeedrConnectionError extends DatafeedrError {
921 1650 }
922 1651
923 -if(!class_exists('DatafeedrQueryError', false)) {
924 1652 /**
1653 + * Class DatafeedrQueryError.
1654 + *
925 1655 * API error: Error in the search query.
926 -**/
927 -class DatafeedrQueryError extends DatafeedrError
928 -{
1656 + */
1657 +class DatafeedrQueryError extends DatafeedrError {
929 1658 }
930 -}
931 1659
932 -if(!class_exists('DatafeedrExternalError', false)) {
933 1660 /**
1661 + * Class DatafeedrExternalError.
1662 + *
934 1663 * API error: External service error.
935 -**/
936 -class DatafeedrExternalError extends DatafeedrError
937 -{
1664 + */
1665 +class DatafeedrExternalError extends DatafeedrError {
938 1666 }
939 -}
940 1667
941 -?>
1668 +/**
1669 + * Class DatafeedrServerError.
1670 + *
1671 + * API error: Internal server error.
1672 + */
1673 +class DatafeedrServerError extends DatafeedrError {
1674 +}