8 * This source file is subject to the new BSD license that is bundled
9 * with this package in the file LICENSE.txt.
10 * It is also available through the world-wide-web at this URL:
11 * http://framework.zend.com/license/new-bsd
12 * If you did not receive a copy of the license and are unable to
13 * obtain it through the world-wide-web, please send an email
14 * to license@zend.com so we can send you a copy immediately.
17 * @package Zend_Service
19 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
20 * @license http://framework.zend.com/license/new-bsd New BSD License
27 * @package Zend_Service
29 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
30 * @license http://framework.zend.com/license/new-bsd New BSD License
32 class Zend_Service_Flickr
35 * Base URI for the REST client
37 const URI_BASE
= 'http://www.flickr.com';
47 * Reference to REST client object
49 * @var Zend_Rest_Client
51 protected $_restClient = null;
55 * Performs object initializations
57 * # Sets up character encoding
60 * @param string $apiKey Your Flickr API key
63 public function __construct($apiKey)
65 iconv_set_encoding('output_encoding', 'UTF-8');
66 iconv_set_encoding('input_encoding', 'UTF-8');
67 iconv_set_encoding('internal_encoding', 'UTF-8');
69 $this->apiKey
= (string) $apiKey;
74 * Find Flickr photos by tag.
76 * Query options include:
78 * # per_page: how many results to return per query
79 * # page: the starting page offset. first result will be (page - 1) * per_page + 1
80 * # tag_mode: Either 'any' for an OR combination of tags,
81 * or 'all' for an AND combination. Default is 'any'.
82 * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp.
83 * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp.
84 * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime.
85 * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime.
87 * @param string|array $query A single tag or an array of tags.
88 * @param array $options Additional parameters to refine your query.
89 * @return Zend_Service_Flickr_ResultSet
90 * @throws Zend_Service_Exception
92 public function tagSearch($query, array $options = array())
94 static $method = 'flickr.photos.search';
95 static $defaultOptions = array('per_page' => 10,
98 'extras' => 'license, date_upload, date_taken, owner_name, icon_server');
100 $options['tags'] = is_array($query) ?
implode(',', $query) : $query;
102 $options = $this->_prepareOptions($method, $options, $defaultOptions);
104 $this->_validateTagSearch($options);
106 // now search for photos
107 $restClient = $this->getRestClient();
108 $restClient->getHttpClient()->resetParameters();
109 $response = $restClient->restGet('/services/rest/', $options);
111 if ($response->isError()) {
113 * @see Zend_Service_Exception
115 require_once 'Zend/Service/Exception.php';
116 throw new Zend_Service_Exception('An error occurred sending request. Status code: '
117 . $response->getStatus());
120 $dom = new DOMDocument();
121 $dom->loadXML($response->getBody());
123 self
::_checkErrors($dom);
126 * @see Zend_Service_Flickr_ResultSet
128 require_once 'Zend/Service/Flickr/ResultSet.php';
129 return new Zend_Service_Flickr_ResultSet($dom, $this);
134 * Finds photos by a user's username or email.
136 * Additional query options include:
138 * # per_page: how many results to return per query
139 * # page: the starting page offset. first result will be (page - 1) * per_page + 1
140 * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp.
141 * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp.
142 * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime.
143 * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime.
145 * @param string $query username or email
146 * @param array $options Additional parameters to refine your query.
147 * @return Zend_Service_Flickr_ResultSet
148 * @throws Zend_Service_Exception
150 public function userSearch($query, array $options = null)
152 static $method = 'flickr.people.getPublicPhotos';
153 static $defaultOptions = array('per_page' => 10,
155 'extras' => 'license, date_upload, date_taken, owner_name, icon_server');
158 // can't access by username, must get ID first
159 if (strchr($query, '@')) {
160 // optimistically hope this is an email
161 $options['user_id'] = $this->getIdByEmail($query);
163 // we can safely ignore this exception here
164 $options['user_id'] = $this->getIdByUsername($query);
167 $options = $this->_prepareOptions($method, $options, $defaultOptions);
168 $this->_validateUserSearch($options);
170 // now search for photos
171 $restClient = $this->getRestClient();
172 $restClient->getHttpClient()->resetParameters();
173 $response = $restClient->restGet('/services/rest/', $options);
175 if ($response->isError()) {
177 * @see Zend_Service_Exception
179 require_once 'Zend/Service/Exception.php';
180 throw new Zend_Service_Exception('An error occurred sending request. Status code: '
181 . $response->getStatus());
184 $dom = new DOMDocument();
185 $dom->loadXML($response->getBody());
187 self
::_checkErrors($dom);
190 * @see Zend_Service_Flickr_ResultSet
192 require_once 'Zend/Service/Flickr/ResultSet.php';
193 return new Zend_Service_Flickr_ResultSet($dom, $this);
197 * Finds photos in a group's pool.
199 * @param string $query group id
200 * @param array $options Additional parameters to refine your query.
201 * @return Zend_Service_Flickr_ResultSet
202 * @throws Zend_Service_Exception
204 public function groupPoolGetPhotos($query, array $options = array())
206 static $method = 'flickr.groups.pools.getPhotos';
207 static $defaultOptions = array('per_page' => 10,
209 'extras' => 'license, date_upload, date_taken, owner_name, icon_server');
211 if (empty($query) ||
!is_string($query)) {
213 * @see Zend_Service_Exception
215 require_once 'Zend/Service/Exception.php';
216 throw new Zend_Service_Exception('You must supply a group id');
219 $options['group_id'] = $query;
221 $options = $this->_prepareOptions($method, $options, $defaultOptions);
223 $this->_validateGroupPoolGetPhotos($options);
225 // now search for photos
226 $restClient = $this->getRestClient();
227 $restClient->getHttpClient()->resetParameters();
228 $response = $restClient->restGet('/services/rest/', $options);
230 if ($response->isError()) {
232 * @see Zend_Service_Exception
234 require_once 'Zend/Service/Exception.php';
235 throw new Zend_Service_Exception('An error occurred sending request. Status code: '
236 . $response->getStatus());
239 $dom = new DOMDocument();
240 $dom->loadXML($response->getBody());
242 self
::_checkErrors($dom);
245 * @see Zend_Service_Flickr_ResultSet
247 require_once 'Zend/Service/Flickr/ResultSet.php';
248 return new Zend_Service_Flickr_ResultSet($dom, $this);
254 * Utility function to find Flickr User IDs for usernames.
256 * (You can only find a user's photo with their NSID.)
258 * @param string $username the username
259 * @return string the NSID (userid)
260 * @throws Zend_Service_Exception
262 public function getIdByUsername($username)
264 static $method = 'flickr.people.findByUsername';
266 $options = array('api_key' => $this->apiKey
, 'method' => $method, 'username' => (string) $username);
268 if (empty($username)) {
270 * @see Zend_Service_Exception
272 require_once 'Zend/Service/Exception.php';
273 throw new Zend_Service_Exception('You must supply a username');
276 $restClient = $this->getRestClient();
277 $restClient->getHttpClient()->resetParameters();
278 $response = $restClient->restGet('/services/rest/', $options);
280 if ($response->isError()) {
282 * @see Zend_Service_Exception
284 require_once 'Zend/Service/Exception.php';
285 throw new Zend_Service_Exception('An error occurred sending request. Status code: '
286 . $response->getStatus());
289 $dom = new DOMDocument();
290 $dom->loadXML($response->getBody());
291 self
::_checkErrors($dom);
292 $xpath = new DOMXPath($dom);
293 return (string) $xpath->query('//user')->item(0)->getAttribute('id');
298 * Utility function to find Flickr User IDs for emails.
300 * (You can only find a user's photo with their NSID.)
302 * @param string $email the email
303 * @return string the NSID (userid)
304 * @throws Zend_Service_Exception
306 public function getIdByEmail($email)
308 static $method = 'flickr.people.findByEmail';
312 * @see Zend_Service_Exception
314 require_once 'Zend/Service/Exception.php';
315 throw new Zend_Service_Exception('You must supply an e-mail address');
318 $options = array('api_key' => $this->apiKey
, 'method' => $method, 'find_email' => (string) $email);
320 $restClient = $this->getRestClient();
321 $restClient->getHttpClient()->resetParameters();
322 $response = $restClient->restGet('/services/rest/', $options);
324 if ($response->isError()) {
326 * @see Zend_Service_Exception
328 require_once 'Zend/Service/Exception.php';
329 throw new Zend_Service_Exception('An error occurred sending request. Status code: '
330 . $response->getStatus());
333 $dom = new DOMDocument();
334 $dom->loadXML($response->getBody());
335 self
::_checkErrors($dom);
336 $xpath = new DOMXPath($dom);
337 return (string) $xpath->query('//user')->item(0)->getAttribute('id');
342 * Returns Flickr photo details by for the given photo ID
344 * @param string $id the NSID
345 * @return array of Zend_Service_Flickr_Image, details for the specified image
346 * @throws Zend_Service_Exception
348 public function getImageDetails($id)
350 static $method = 'flickr.photos.getSizes';
354 * @see Zend_Service_Exception
356 require_once 'Zend/Service/Exception.php';
357 throw new Zend_Service_Exception('You must supply a photo ID');
360 $options = array('api_key' => $this->apiKey
, 'method' => $method, 'photo_id' => $id);
362 $restClient = $this->getRestClient();
363 $restClient->getHttpClient()->resetParameters();
364 $response = $restClient->restGet('/services/rest/', $options);
366 $dom = new DOMDocument();
367 $dom->loadXML($response->getBody());
368 $xpath = new DOMXPath($dom);
369 self
::_checkErrors($dom);
372 * @see Zend_Service_Flickr_Image
374 require_once 'Zend/Service/Flickr/Image.php';
375 foreach ($xpath->query('//size') as $size) {
376 $label = (string) $size->getAttribute('label');
377 $retval[$label] = new Zend_Service_Flickr_Image($size);
385 * Returns a reference to the REST client, instantiating it if necessary
387 * @return Zend_Rest_Client
389 public function getRestClient()
391 if (null === $this->_restClient
) {
393 * @see Zend_Rest_Client
395 require_once 'Zend/Rest/Client.php';
396 $this->_restClient
= new Zend_Rest_Client(self
::URI_BASE
);
399 return $this->_restClient
;
404 * Validate User Search Options
406 * @param array $options
408 * @throws Zend_Service_Exception
410 protected function _validateUserSearch(array $options)
412 $validOptions = array('api_key', 'method', 'user_id', 'per_page', 'page', 'extras', 'min_upload_date',
413 'min_taken_date', 'max_upload_date', 'max_taken_date', 'safe_search');
415 $this->_compareOptions($options, $validOptions);
418 * @see Zend_Validate_Between
420 require_once 'Zend/Validate/Between.php';
421 $between = new Zend_Validate_Between(1, 500, true);
422 if (!$between->isValid($options['per_page'])) {
424 * @see Zend_Service_Exception
426 require_once 'Zend/Service/Exception.php';
427 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option');
431 * @see Zend_Validate_Int
433 require_once 'Zend/Validate/Int.php';
434 $int = new Zend_Validate_Int();
435 if (!$int->isValid($options['page'])) {
437 * @see Zend_Service_Exception
439 require_once 'Zend/Service/Exception.php';
440 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option');
443 // validate extras, which are delivered in csv format
444 if ($options['extras']) {
445 $extras = explode(',', $options['extras']);
446 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server');
447 foreach($extras as $extra) {
449 * @todo The following does not do anything [yet], so it is commented out.
451 //in_array(trim($extra), $validExtras);
458 * Validate Tag Search Options
460 * @param array $options
462 * @throws Zend_Service_Exception
464 protected function _validateTagSearch(array $options)
466 $validOptions = array('method', 'api_key', 'user_id', 'tags', 'tag_mode', 'text', 'min_upload_date',
467 'max_upload_date', 'min_taken_date', 'max_taken_date', 'license', 'sort',
468 'privacy_filter', 'bbox', 'accuracy', 'safe_search', 'content_type', 'machine_tags',
469 'machine_tag_mode', 'group_id', 'contacts', 'woe_id', 'place_id', 'media', 'has_geo',
470 'geo_context', 'lat', 'lon', 'radius', 'radius_units', 'is_commons', 'is_gallery',
471 'extras', 'per_page', 'page');
473 $this->_compareOptions($options, $validOptions);
476 * @see Zend_Validate_Between
478 require_once 'Zend/Validate/Between.php';
479 $between = new Zend_Validate_Between(1, 500, true);
480 if (!$between->isValid($options['per_page'])) {
482 * @see Zend_Service_Exception
484 require_once 'Zend/Service/Exception.php';
485 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option');
489 * @see Zend_Validate_Int
491 require_once 'Zend/Validate/Int.php';
492 $int = new Zend_Validate_Int();
493 if (!$int->isValid($options['page'])) {
495 * @see Zend_Service_Exception
497 require_once 'Zend/Service/Exception.php';
498 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option');
501 // validate extras, which are delivered in csv format
502 if ($options['extras']) {
503 $extras = explode(',', $options['extras']);
504 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server');
505 foreach($extras as $extra) {
507 * @todo The following does not do anything [yet], so it is commented out.
509 //in_array(trim($extra), $validExtras);
517 * Validate Group Search Options
519 * @param array $options
520 * @throws Zend_Service_Exception
523 protected function _validateGroupPoolGetPhotos(array $options)
525 $validOptions = array('api_key', 'tags', 'method', 'group_id', 'per_page', 'page', 'extras', 'user_id');
527 $this->_compareOptions($options, $validOptions);
530 * @see Zend_Validate_Between
532 require_once 'Zend/Validate/Between.php';
533 $between = new Zend_Validate_Between(1, 500, true);
534 if (!$between->isValid($options['per_page'])) {
536 * @see Zend_Service_Exception
538 require_once 'Zend/Service/Exception.php';
539 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option');
543 * @see Zend_Validate_Int
545 require_once 'Zend/Validate/Int.php';
546 $int = new Zend_Validate_Int();
548 if (!$int->isValid($options['page'])) {
550 * @see Zend_Service_Exception
552 require_once 'Zend/Service/Exception.php';
553 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option');
556 // validate extras, which are delivered in csv format
557 if (isset($options['extras'])) {
558 $extras = explode(',', $options['extras']);
559 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server');
560 foreach($extras as $extra) {
562 * @todo The following does not do anything [yet], so it is commented out.
564 //in_array(trim($extra), $validExtras);
571 * Throws an exception if and only if the response status indicates a failure
573 * @param DOMDocument $dom
575 * @throws Zend_Service_Exception
577 protected static function _checkErrors(DOMDocument
$dom)
579 if ($dom->documentElement
->getAttribute('stat') === 'fail') {
580 $xpath = new DOMXPath($dom);
581 $err = $xpath->query('//err')->item(0);
583 * @see Zend_Service_Exception
585 require_once 'Zend/Service/Exception.php';
586 throw new Zend_Service_Exception('Search failed due to error: ' . $err->getAttribute('msg')
587 . ' (error #' . $err->getAttribute('code') . ')');
593 * Prepare options for the request
595 * @param string $method Flickr Method to call
596 * @param array $options User Options
597 * @param array $defaultOptions Default Options
598 * @return array Merged array of user and default/required options
600 protected function _prepareOptions($method, array $options, array $defaultOptions)
602 $options['method'] = (string) $method;
603 $options['api_key'] = $this->apiKey
;
605 return array_merge($defaultOptions, $options);
610 * Throws an exception if and only if any user options are invalid
612 * @param array $options User options
613 * @param array $validOptions Valid options
615 * @throws Zend_Service_Exception
617 protected function _compareOptions(array $options, array $validOptions)
619 $difference = array_diff(array_keys($options), $validOptions);
622 * @see Zend_Service_Exception
624 require_once 'Zend/Service/Exception.php';
625 throw new Zend_Service_Exception('The following parameters are invalid: ' . implode(',', $difference));