File indexing completed on 2024-12-22 05:37:06
0001 <?php 0002 0003 /** 0004 * Zend Framework 0005 * 0006 * LICENSE 0007 * 0008 * This source file is subject to the new BSD license that is bundled 0009 * with this package in the file LICENSE.txt. 0010 * It is also available through the world-wide-web at this URL: 0011 * http://framework.zend.com/license/new-bsd 0012 * If you did not receive a copy of the license and are unable to 0013 * obtain it through the world-wide-web, please send an email 0014 * to license@zend.com so we can send you a copy immediately. 0015 * 0016 * @category Zend 0017 * @package Zend_Service 0018 * @subpackage Flickr 0019 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0020 * @license http://framework.zend.com/license/new-bsd New BSD License 0021 * @version $Id$ 0022 */ 0023 0024 /** @see Zend_Xml_Security */ 0025 // require_once 'Zend/Xml/Security.php'; 0026 0027 /** 0028 * @category Zend 0029 * @package Zend_Service 0030 * @subpackage Flickr 0031 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0032 * @license http://framework.zend.com/license/new-bsd New BSD License 0033 */ 0034 class Zend_Service_Flickr 0035 { 0036 /** 0037 * Base URI for the REST client 0038 */ 0039 const URI_BASE = 'https://www.flickr.com'; 0040 0041 /** 0042 * Your Flickr API key 0043 * 0044 * @var string 0045 */ 0046 public $apiKey; 0047 0048 /** 0049 * Reference to REST client object 0050 * 0051 * @var Zend_Rest_Client 0052 */ 0053 protected $_restClient = null; 0054 0055 0056 /** 0057 * Performs object initializations 0058 * 0059 * # Sets up character encoding 0060 * # Saves the API key 0061 * 0062 * @param string $apiKey Your Flickr API key 0063 * @return void 0064 */ 0065 public function __construct($apiKey) 0066 { 0067 $this->apiKey = (string) $apiKey; 0068 } 0069 0070 0071 /** 0072 * Find Flickr photos by tag. 0073 * 0074 * Query options include: 0075 * 0076 * # per_page: how many results to return per query 0077 * # page: the starting page offset. first result will be (page - 1) * per_page + 1 0078 * # tag_mode: Either 'any' for an OR combination of tags, 0079 * or 'all' for an AND combination. Default is 'any'. 0080 * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp. 0081 * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp. 0082 * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime. 0083 * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime. 0084 * 0085 * @param string|array $query A single tag or an array of tags. 0086 * @param array $options Additional parameters to refine your query. 0087 * @return Zend_Service_Flickr_ResultSet 0088 * @throws Zend_Service_Exception 0089 */ 0090 public function tagSearch($query, array $options = array()) 0091 { 0092 static $method = 'flickr.photos.search'; 0093 static $defaultOptions = array('per_page' => 10, 0094 'page' => 1, 0095 'tag_mode' => 'or', 0096 'extras' => 'license, date_upload, date_taken, owner_name, icon_server'); 0097 0098 $options['tags'] = is_array($query) ? implode(',', $query) : $query; 0099 0100 $options = $this->_prepareOptions($method, $options, $defaultOptions); 0101 0102 $this->_validateTagSearch($options); 0103 0104 // now search for photos 0105 $restClient = $this->getRestClient(); 0106 $restClient->getHttpClient()->resetParameters(); 0107 $response = $restClient->restGet('/services/rest/', $options); 0108 0109 if ($response->isError()) { 0110 /** 0111 * @see Zend_Service_Exception 0112 */ 0113 // require_once 'Zend/Service/Exception.php'; 0114 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' 0115 . $response->getStatus()); 0116 } 0117 0118 $dom = new DOMDocument(); 0119 $dom = Zend_Xml_Security::scan($response->getBody(), $dom); 0120 self::_checkErrors($dom); 0121 0122 /** 0123 * @see Zend_Service_Flickr_ResultSet 0124 */ 0125 // require_once 'Zend/Service/Flickr/ResultSet.php'; 0126 return new Zend_Service_Flickr_ResultSet($dom, $this); 0127 } 0128 0129 0130 /** 0131 * Finds photos by a user's username or email. 0132 * 0133 * Additional query options include: 0134 * 0135 * # per_page: how many results to return per query 0136 * # page: the starting page offset. first result will be (page - 1) * per_page + 1 0137 * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp. 0138 * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp. 0139 * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime. 0140 * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime. 0141 * 0142 * @param string $query username or email 0143 * @param array $options Additional parameters to refine your query. 0144 * @return Zend_Service_Flickr_ResultSet 0145 * @throws Zend_Service_Exception 0146 */ 0147 public function userSearch($query, array $options = null) 0148 { 0149 static $method = 'flickr.people.getPublicPhotos'; 0150 static $defaultOptions = array('per_page' => 10, 0151 'page' => 1, 0152 'extras' => 'license, date_upload, date_taken, owner_name, icon_server'); 0153 0154 0155 // can't access by username, must get ID first 0156 if (strchr($query, '@')) { 0157 // optimistically hope this is an email 0158 $options['user_id'] = $this->getIdByEmail($query); 0159 } else { 0160 // we can safely ignore this exception here 0161 $options['user_id'] = $this->getIdByUsername($query); 0162 } 0163 0164 $options = $this->_prepareOptions($method, $options, $defaultOptions); 0165 $this->_validateUserSearch($options); 0166 0167 // now search for photos 0168 $restClient = $this->getRestClient(); 0169 $restClient->getHttpClient()->resetParameters(); 0170 $response = $restClient->restGet('/services/rest/', $options); 0171 0172 if ($response->isError()) { 0173 /** 0174 * @see Zend_Service_Exception 0175 */ 0176 // require_once 'Zend/Service/Exception.php'; 0177 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' 0178 . $response->getStatus()); 0179 } 0180 0181 $dom = new DOMDocument(); 0182 $dom = Zend_Xml_Security::scan($response->getBody(), $dom); 0183 self::_checkErrors($dom); 0184 0185 /** 0186 * @see Zend_Service_Flickr_ResultSet 0187 */ 0188 // require_once 'Zend/Service/Flickr/ResultSet.php'; 0189 return new Zend_Service_Flickr_ResultSet($dom, $this); 0190 } 0191 0192 /** 0193 * Finds photos in a group's pool. 0194 * 0195 * @param string $query group id 0196 * @param array $options Additional parameters to refine your query. 0197 * @return Zend_Service_Flickr_ResultSet 0198 * @throws Zend_Service_Exception 0199 */ 0200 public function groupPoolGetPhotos($query, array $options = array()) 0201 { 0202 static $method = 'flickr.groups.pools.getPhotos'; 0203 static $defaultOptions = array('per_page' => 10, 0204 'page' => 1, 0205 'extras' => 'license, date_upload, date_taken, owner_name, icon_server'); 0206 0207 if (empty($query) || !is_string($query)) { 0208 /** 0209 * @see Zend_Service_Exception 0210 */ 0211 // require_once 'Zend/Service/Exception.php'; 0212 throw new Zend_Service_Exception('You must supply a group id'); 0213 } 0214 0215 $options['group_id'] = $query; 0216 0217 $options = $this->_prepareOptions($method, $options, $defaultOptions); 0218 0219 $this->_validateGroupPoolGetPhotos($options); 0220 0221 // now search for photos 0222 $restClient = $this->getRestClient(); 0223 $restClient->getHttpClient()->resetParameters(); 0224 $response = $restClient->restGet('/services/rest/', $options); 0225 0226 if ($response->isError()) { 0227 /** 0228 * @see Zend_Service_Exception 0229 */ 0230 // require_once 'Zend/Service/Exception.php'; 0231 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' 0232 . $response->getStatus()); 0233 } 0234 0235 $dom = new DOMDocument(); 0236 $dom = Zend_Xml_Security::scan($response->getBody(), $dom); 0237 self::_checkErrors($dom); 0238 0239 /** 0240 * @see Zend_Service_Flickr_ResultSet 0241 */ 0242 // require_once 'Zend/Service/Flickr/ResultSet.php'; 0243 return new Zend_Service_Flickr_ResultSet($dom, $this); 0244 } 0245 0246 0247 0248 /** 0249 * Utility function to find Flickr User IDs for usernames. 0250 * 0251 * (You can only find a user's photo with their NSID.) 0252 * 0253 * @param string $username the username 0254 * @return string the NSID (userid) 0255 * @throws Zend_Service_Exception 0256 */ 0257 public function getIdByUsername($username) 0258 { 0259 static $method = 'flickr.people.findByUsername'; 0260 0261 $options = array('api_key' => $this->apiKey, 'method' => $method, 'username' => (string) $username); 0262 0263 if (empty($username)) { 0264 /** 0265 * @see Zend_Service_Exception 0266 */ 0267 // require_once 'Zend/Service/Exception.php'; 0268 throw new Zend_Service_Exception('You must supply a username'); 0269 } 0270 0271 $restClient = $this->getRestClient(); 0272 $restClient->getHttpClient()->resetParameters(); 0273 $response = $restClient->restGet('/services/rest/', $options); 0274 0275 if ($response->isError()) { 0276 /** 0277 * @see Zend_Service_Exception 0278 */ 0279 // require_once 'Zend/Service/Exception.php'; 0280 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' 0281 . $response->getStatus()); 0282 } 0283 0284 $dom = new DOMDocument(); 0285 $dom = Zend_Xml_Security::scan($response->getBody(), $dom); 0286 self::_checkErrors($dom); 0287 $xpath = new DOMXPath($dom); 0288 return (string) $xpath->query('//user')->item(0)->getAttribute('id'); 0289 } 0290 0291 0292 /** 0293 * Utility function to find Flickr User IDs for emails. 0294 * 0295 * (You can only find a user's photo with their NSID.) 0296 * 0297 * @param string $email the email 0298 * @return string the NSID (userid) 0299 * @throws Zend_Service_Exception 0300 */ 0301 public function getIdByEmail($email) 0302 { 0303 static $method = 'flickr.people.findByEmail'; 0304 0305 if (empty($email)) { 0306 /** 0307 * @see Zend_Service_Exception 0308 */ 0309 // require_once 'Zend/Service/Exception.php'; 0310 throw new Zend_Service_Exception('You must supply an e-mail address'); 0311 } 0312 0313 $options = array('api_key' => $this->apiKey, 'method' => $method, 'find_email' => (string) $email); 0314 0315 $restClient = $this->getRestClient(); 0316 $restClient->getHttpClient()->resetParameters(); 0317 $response = $restClient->restGet('/services/rest/', $options); 0318 0319 if ($response->isError()) { 0320 /** 0321 * @see Zend_Service_Exception 0322 */ 0323 // require_once 'Zend/Service/Exception.php'; 0324 throw new Zend_Service_Exception('An error occurred sending request. Status code: ' 0325 . $response->getStatus()); 0326 } 0327 0328 $dom = new DOMDocument(); 0329 $dom = Zend_Xml_Security::scan($response->getBody(), $dom); 0330 self::_checkErrors($dom); 0331 $xpath = new DOMXPath($dom); 0332 return (string) $xpath->query('//user')->item(0)->getAttribute('id'); 0333 } 0334 0335 0336 /** 0337 * Returns Flickr photo details by for the given photo ID 0338 * 0339 * @param string $id the NSID 0340 * @return array of Zend_Service_Flickr_Image, details for the specified image 0341 * @throws Zend_Service_Exception 0342 */ 0343 public function getImageDetails($id) 0344 { 0345 static $method = 'flickr.photos.getSizes'; 0346 0347 if (empty($id)) { 0348 /** 0349 * @see Zend_Service_Exception 0350 */ 0351 // require_once 'Zend/Service/Exception.php'; 0352 throw new Zend_Service_Exception('You must supply a photo ID'); 0353 } 0354 0355 $options = array('api_key' => $this->apiKey, 'method' => $method, 'photo_id' => $id); 0356 0357 $restClient = $this->getRestClient(); 0358 $restClient->getHttpClient()->resetParameters(); 0359 $response = $restClient->restGet('/services/rest/', $options); 0360 0361 $dom = new DOMDocument(); 0362 $dom = Zend_Xml_Security::scan($response->getBody(), $dom); 0363 $xpath = new DOMXPath($dom); 0364 self::_checkErrors($dom); 0365 $retval = array(); 0366 /** 0367 * @see Zend_Service_Flickr_Image 0368 */ 0369 // require_once 'Zend/Service/Flickr/Image.php'; 0370 foreach ($xpath->query('//size') as $size) { 0371 $label = (string) $size->getAttribute('label'); 0372 $retval[$label] = new Zend_Service_Flickr_Image($size); 0373 } 0374 0375 return $retval; 0376 } 0377 0378 0379 /** 0380 * Returns a reference to the REST client, instantiating it if necessary 0381 * 0382 * @return Zend_Rest_Client 0383 */ 0384 public function getRestClient() 0385 { 0386 if (null === $this->_restClient) { 0387 /** 0388 * @see Zend_Rest_Client 0389 */ 0390 // require_once 'Zend/Rest/Client.php'; 0391 $this->_restClient = new Zend_Rest_Client(self::URI_BASE); 0392 } 0393 0394 return $this->_restClient; 0395 } 0396 0397 0398 /** 0399 * Validate User Search Options 0400 * 0401 * @param array $options 0402 * @return void 0403 * @throws Zend_Service_Exception 0404 */ 0405 protected function _validateUserSearch(array $options) 0406 { 0407 $validOptions = array('api_key', 'method', 'user_id', 'per_page', 'page', 'extras', 'min_upload_date', 0408 'min_taken_date', 'max_upload_date', 'max_taken_date', 'safe_search'); 0409 0410 $this->_compareOptions($options, $validOptions); 0411 0412 /** 0413 * @see Zend_Validate_Between 0414 */ 0415 // require_once 'Zend/Validate/Between.php'; 0416 $between = new Zend_Validate_Between(1, 500, true); 0417 if (!$between->isValid($options['per_page'])) { 0418 /** 0419 * @see Zend_Service_Exception 0420 */ 0421 // require_once 'Zend/Service/Exception.php'; 0422 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option'); 0423 } 0424 0425 /** 0426 * @see Zend_Validate_Int 0427 */ 0428 // require_once 'Zend/Validate/Int.php'; 0429 $int = new Zend_Validate_Int(); 0430 if (!$int->isValid($options['page'])) { 0431 /** 0432 * @see Zend_Service_Exception 0433 */ 0434 // require_once 'Zend/Service/Exception.php'; 0435 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option'); 0436 } 0437 0438 // validate extras, which are delivered in csv format 0439 if ($options['extras']) { 0440 $extras = explode(',', $options['extras']); 0441 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server'); 0442 foreach($extras as $extra) { 0443 /** 0444 * @todo The following does not do anything [yet], so it is commented out. 0445 */ 0446 //in_array(trim($extra), $validExtras); 0447 } 0448 } 0449 } 0450 0451 0452 /** 0453 * Validate Tag Search Options 0454 * 0455 * @param array $options 0456 * @return void 0457 * @throws Zend_Service_Exception 0458 */ 0459 protected function _validateTagSearch(array $options) 0460 { 0461 $validOptions = array('method', 'api_key', 'user_id', 'tags', 'tag_mode', 'text', 'min_upload_date', 0462 'max_upload_date', 'min_taken_date', 'max_taken_date', 'license', 'sort', 0463 'privacy_filter', 'bbox', 'accuracy', 'safe_search', 'content_type', 'machine_tags', 0464 'machine_tag_mode', 'group_id', 'contacts', 'woe_id', 'place_id', 'media', 'has_geo', 0465 'geo_context', 'lat', 'lon', 'radius', 'radius_units', 'is_commons', 'is_gallery', 0466 'extras', 'per_page', 'page'); 0467 0468 $this->_compareOptions($options, $validOptions); 0469 0470 /** 0471 * @see Zend_Validate_Between 0472 */ 0473 // require_once 'Zend/Validate/Between.php'; 0474 $between = new Zend_Validate_Between(1, 500, true); 0475 if (!$between->isValid($options['per_page'])) { 0476 /** 0477 * @see Zend_Service_Exception 0478 */ 0479 // require_once 'Zend/Service/Exception.php'; 0480 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option'); 0481 } 0482 0483 /** 0484 * @see Zend_Validate_Int 0485 */ 0486 // require_once 'Zend/Validate/Int.php'; 0487 $int = new Zend_Validate_Int(); 0488 if (!$int->isValid($options['page'])) { 0489 /** 0490 * @see Zend_Service_Exception 0491 */ 0492 // require_once 'Zend/Service/Exception.php'; 0493 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option'); 0494 } 0495 0496 // validate extras, which are delivered in csv format 0497 if ($options['extras']) { 0498 $extras = explode(',', $options['extras']); 0499 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server'); 0500 foreach($extras as $extra) { 0501 /** 0502 * @todo The following does not do anything [yet], so it is commented out. 0503 */ 0504 //in_array(trim($extra), $validExtras); 0505 } 0506 } 0507 0508 } 0509 0510 0511 /** 0512 * Validate Group Search Options 0513 * 0514 * @param array $options 0515 * @throws Zend_Service_Exception 0516 * @return void 0517 */ 0518 protected function _validateGroupPoolGetPhotos(array $options) 0519 { 0520 $validOptions = array('api_key', 'tags', 'method', 'group_id', 'per_page', 'page', 'extras', 'user_id'); 0521 0522 $this->_compareOptions($options, $validOptions); 0523 0524 /** 0525 * @see Zend_Validate_Between 0526 */ 0527 // require_once 'Zend/Validate/Between.php'; 0528 $between = new Zend_Validate_Between(1, 500, true); 0529 if (!$between->isValid($options['per_page'])) { 0530 /** 0531 * @see Zend_Service_Exception 0532 */ 0533 // require_once 'Zend/Service/Exception.php'; 0534 throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option'); 0535 } 0536 0537 /** 0538 * @see Zend_Validate_Int 0539 */ 0540 // require_once 'Zend/Validate/Int.php'; 0541 $int = new Zend_Validate_Int(); 0542 0543 if (!$int->isValid($options['page'])) { 0544 /** 0545 * @see Zend_Service_Exception 0546 */ 0547 // require_once 'Zend/Service/Exception.php'; 0548 throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option'); 0549 } 0550 0551 // validate extras, which are delivered in csv format 0552 if (isset($options['extras'])) { 0553 $extras = explode(',', $options['extras']); 0554 $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server'); 0555 foreach($extras as $extra) { 0556 /** 0557 * @todo The following does not do anything [yet], so it is commented out. 0558 */ 0559 //in_array(trim($extra), $validExtras); 0560 } 0561 } 0562 } 0563 0564 0565 /** 0566 * Throws an exception if and only if the response status indicates a failure 0567 * 0568 * @param DOMDocument $dom 0569 * @return void 0570 * @throws Zend_Service_Exception 0571 */ 0572 protected static function _checkErrors(DOMDocument $dom) 0573 { 0574 if ($dom->documentElement->getAttribute('stat') === 'fail') { 0575 $xpath = new DOMXPath($dom); 0576 $err = $xpath->query('//err')->item(0); 0577 /** 0578 * @see Zend_Service_Exception 0579 */ 0580 // require_once 'Zend/Service/Exception.php'; 0581 throw new Zend_Service_Exception('Search failed due to error: ' . $err->getAttribute('msg') 0582 . ' (error #' . $err->getAttribute('code') . ')'); 0583 } 0584 } 0585 0586 0587 /** 0588 * Prepare options for the request 0589 * 0590 * @param string $method Flickr Method to call 0591 * @param array $options User Options 0592 * @param array $defaultOptions Default Options 0593 * @return array Merged array of user and default/required options 0594 */ 0595 protected function _prepareOptions($method, array $options, array $defaultOptions) 0596 { 0597 $options['method'] = (string) $method; 0598 $options['api_key'] = $this->apiKey; 0599 0600 return array_merge($defaultOptions, $options); 0601 } 0602 0603 0604 /** 0605 * Throws an exception if and only if any user options are invalid 0606 * 0607 * @param array $options User options 0608 * @param array $validOptions Valid options 0609 * @return void 0610 * @throws Zend_Service_Exception 0611 */ 0612 protected function _compareOptions(array $options, array $validOptions) 0613 { 0614 $difference = array_diff(array_keys($options), $validOptions); 0615 if ($difference) { 0616 /** 0617 * @see Zend_Service_Exception 0618 */ 0619 // require_once 'Zend/Service/Exception.php'; 0620 throw new Zend_Service_Exception('The following parameters are invalid: ' . implode(',', $difference)); 0621 } 0622 } 0623 } 0624