2
0

Flickr.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Service
  17. * @subpackage Flickr
  18. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. /**
  23. * @category Zend
  24. * @package Zend_Service
  25. * @subpackage Flickr
  26. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  27. * @license http://framework.zend.com/license/new-bsd New BSD License
  28. */
  29. class Zend_Service_Flickr
  30. {
  31. /**
  32. * Base URI for the REST client
  33. */
  34. const URI_BASE = 'http://www.flickr.com';
  35. /**
  36. * Your Flickr API key
  37. *
  38. * @var string
  39. */
  40. public $apiKey;
  41. /**
  42. * Reference to REST client object
  43. *
  44. * @var Zend_Rest_Client
  45. */
  46. protected $_restClient = null;
  47. /**
  48. * Performs object initializations
  49. *
  50. * # Sets up character encoding
  51. * # Saves the API key
  52. *
  53. * @param string $apiKey Your Flickr API key
  54. * @return void
  55. */
  56. public function __construct($apiKey)
  57. {
  58. iconv_set_encoding('output_encoding', 'UTF-8');
  59. iconv_set_encoding('input_encoding', 'UTF-8');
  60. iconv_set_encoding('internal_encoding', 'UTF-8');
  61. $this->apiKey = (string) $apiKey;
  62. }
  63. /**
  64. * Find Flickr photos by tag.
  65. *
  66. * Query options include:
  67. *
  68. * # per_page: how many results to return per query
  69. * # page: the starting page offset. first result will be (page - 1) * per_page + 1
  70. * # tag_mode: Either 'any' for an OR combination of tags,
  71. * or 'all' for an AND combination. Default is 'any'.
  72. * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp.
  73. * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp.
  74. * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime.
  75. * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime.
  76. *
  77. * @param string|array $query A single tag or an array of tags.
  78. * @param array $options Additional parameters to refine your query.
  79. * @return Zend_Service_Flickr_ResultSet
  80. * @throws Zend_Service_Exception
  81. */
  82. public function tagSearch($query, array $options = array())
  83. {
  84. static $method = 'flickr.photos.search';
  85. static $defaultOptions = array('per_page' => 10,
  86. 'page' => 1,
  87. 'tag_mode' => 'or',
  88. 'extras' => 'license, date_upload, date_taken, owner_name, icon_server');
  89. $options['tags'] = is_array($query) ? implode(',', $query) : $query;
  90. $options = $this->_prepareOptions($method, $options, $defaultOptions);
  91. $this->_validateTagSearch($options);
  92. // now search for photos
  93. $restClient = $this->getRestClient();
  94. $restClient->getHttpClient()->resetParameters();
  95. $response = $restClient->restGet('/services/rest/', $options);
  96. if ($response->isError()) {
  97. /**
  98. * @see Zend_Service_Exception
  99. */
  100. require_once 'Zend/Service/Exception.php';
  101. throw new Zend_Service_Exception('An error occurred sending request. Status code: '
  102. . $response->getStatus());
  103. }
  104. $dom = new DOMDocument();
  105. $dom->loadXML($response->getBody());
  106. self::_checkErrors($dom);
  107. /**
  108. * @see Zend_Service_Flickr_ResultSet
  109. */
  110. require_once 'Zend/Service/Flickr/ResultSet.php';
  111. return new Zend_Service_Flickr_ResultSet($dom, $this);
  112. }
  113. /**
  114. * Finds photos by a user's username or email.
  115. *
  116. * Additional query options include:
  117. *
  118. * # per_page: how many results to return per query
  119. * # page: the starting page offset. first result will be (page - 1) * per_page + 1
  120. * # min_upload_date: Minimum upload date to search on. Date should be a unix timestamp.
  121. * # max_upload_date: Maximum upload date to search on. Date should be a unix timestamp.
  122. * # min_taken_date: Minimum upload date to search on. Date should be a MySQL datetime.
  123. * # max_taken_date: Maximum upload date to search on. Date should be a MySQL datetime.
  124. *
  125. * @param string $query username or email
  126. * @param array $options Additional parameters to refine your query.
  127. * @return Zend_Service_Flickr_ResultSet
  128. * @throws Zend_Service_Exception
  129. */
  130. public function userSearch($query, array $options = null)
  131. {
  132. static $method = 'flickr.people.getPublicPhotos';
  133. static $defaultOptions = array('per_page' => 10,
  134. 'page' => 1,
  135. 'extras' => 'license, date_upload, date_taken, owner_name, icon_server');
  136. // can't access by username, must get ID first
  137. if (strchr($query, '@')) {
  138. // optimistically hope this is an email
  139. $options['user_id'] = $this->getIdByEmail($query);
  140. } else {
  141. // we can safely ignore this exception here
  142. $options['user_id'] = $this->getIdByUsername($query);
  143. }
  144. $options = $this->_prepareOptions($method, $options, $defaultOptions);
  145. $this->_validateUserSearch($options);
  146. // now search for photos
  147. $restClient = $this->getRestClient();
  148. $restClient->getHttpClient()->resetParameters();
  149. $response = $restClient->restGet('/services/rest/', $options);
  150. if ($response->isError()) {
  151. /**
  152. * @see Zend_Service_Exception
  153. */
  154. require_once 'Zend/Service/Exception.php';
  155. throw new Zend_Service_Exception('An error occurred sending request. Status code: '
  156. . $response->getStatus());
  157. }
  158. $dom = new DOMDocument();
  159. $dom->loadXML($response->getBody());
  160. self::_checkErrors($dom);
  161. /**
  162. * @see Zend_Service_Flickr_ResultSet
  163. */
  164. require_once 'Zend/Service/Flickr/ResultSet.php';
  165. return new Zend_Service_Flickr_ResultSet($dom, $this);
  166. }
  167. /**
  168. * Finds photos in a group's pool.
  169. *
  170. * @param string $query group id
  171. * @param array $options Additional parameters to refine your query.
  172. * @return Zend_Service_Flickr_ResultSet
  173. * @throws Zend_Service_Exception
  174. */
  175. public function groupPoolGetPhotos($query, array $options = array())
  176. {
  177. static $method = 'flickr.groups.pools.getPhotos';
  178. static $defaultOptions = array('per_page' => 10,
  179. 'page' => 1,
  180. 'extras' => 'license, date_upload, date_taken, owner_name, icon_server');
  181. if (empty($query) || !is_string($query)) {
  182. /**
  183. * @see Zend_Service_Exception
  184. */
  185. require_once 'Zend/Service/Exception.php';
  186. throw new Zend_Service_Exception('You must supply a group id');
  187. }
  188. $options['group_id'] = $query;
  189. $options = $this->_prepareOptions($method, $options, $defaultOptions);
  190. $this->_validateGroupPoolGetPhotos($options);
  191. // now search for photos
  192. $restClient = $this->getRestClient();
  193. $restClient->getHttpClient()->resetParameters();
  194. $response = $restClient->restGet('/services/rest/', $options);
  195. if ($response->isError()) {
  196. /**
  197. * @see Zend_Service_Exception
  198. */
  199. require_once 'Zend/Service/Exception.php';
  200. throw new Zend_Service_Exception('An error occurred sending request. Status code: '
  201. . $response->getStatus());
  202. }
  203. $dom = new DOMDocument();
  204. $dom->loadXML($response->getBody());
  205. self::_checkErrors($dom);
  206. /**
  207. * @see Zend_Service_Flickr_ResultSet
  208. */
  209. require_once 'Zend/Service/Flickr/ResultSet.php';
  210. return new Zend_Service_Flickr_ResultSet($dom, $this);
  211. }
  212. /**
  213. * Utility function to find Flickr User IDs for usernames.
  214. *
  215. * (You can only find a user's photo with their NSID.)
  216. *
  217. * @param string $username the username
  218. * @return string the NSID (userid)
  219. * @throws Zend_Service_Exception
  220. */
  221. public function getIdByUsername($username)
  222. {
  223. static $method = 'flickr.people.findByUsername';
  224. $options = array('api_key' => $this->apiKey, 'method' => $method, 'username' => (string) $username);
  225. if (empty($username)) {
  226. /**
  227. * @see Zend_Service_Exception
  228. */
  229. require_once 'Zend/Service/Exception.php';
  230. throw new Zend_Service_Exception('You must supply a username');
  231. }
  232. $restClient = $this->getRestClient();
  233. $restClient->getHttpClient()->resetParameters();
  234. $response = $restClient->restGet('/services/rest/', $options);
  235. if ($response->isError()) {
  236. /**
  237. * @see Zend_Service_Exception
  238. */
  239. require_once 'Zend/Service/Exception.php';
  240. throw new Zend_Service_Exception('An error occurred sending request. Status code: '
  241. . $response->getStatus());
  242. }
  243. $dom = new DOMDocument();
  244. $dom->loadXML($response->getBody());
  245. self::_checkErrors($dom);
  246. $xpath = new DOMXPath($dom);
  247. return (string) $xpath->query('//user')->item(0)->getAttribute('id');
  248. }
  249. /**
  250. * Utility function to find Flickr User IDs for emails.
  251. *
  252. * (You can only find a user's photo with their NSID.)
  253. *
  254. * @param string $email the email
  255. * @return string the NSID (userid)
  256. * @throws Zend_Service_Exception
  257. */
  258. public function getIdByEmail($email)
  259. {
  260. static $method = 'flickr.people.findByEmail';
  261. if (empty($email)) {
  262. /**
  263. * @see Zend_Service_Exception
  264. */
  265. require_once 'Zend/Service/Exception.php';
  266. throw new Zend_Service_Exception('You must supply an e-mail address');
  267. }
  268. $options = array('api_key' => $this->apiKey, 'method' => $method, 'find_email' => (string) $email);
  269. $restClient = $this->getRestClient();
  270. $restClient->getHttpClient()->resetParameters();
  271. $response = $restClient->restGet('/services/rest/', $options);
  272. if ($response->isError()) {
  273. /**
  274. * @see Zend_Service_Exception
  275. */
  276. require_once 'Zend/Service/Exception.php';
  277. throw new Zend_Service_Exception('An error occurred sending request. Status code: '
  278. . $response->getStatus());
  279. }
  280. $dom = new DOMDocument();
  281. $dom->loadXML($response->getBody());
  282. self::_checkErrors($dom);
  283. $xpath = new DOMXPath($dom);
  284. return (string) $xpath->query('//user')->item(0)->getAttribute('id');
  285. }
  286. /**
  287. * Returns Flickr photo details by for the given photo ID
  288. *
  289. * @param string $id the NSID
  290. * @return array of Zend_Service_Flickr_Image, details for the specified image
  291. * @throws Zend_Service_Exception
  292. */
  293. public function getImageDetails($id)
  294. {
  295. static $method = 'flickr.photos.getSizes';
  296. if (empty($id)) {
  297. /**
  298. * @see Zend_Service_Exception
  299. */
  300. require_once 'Zend/Service/Exception.php';
  301. throw new Zend_Service_Exception('You must supply a photo ID');
  302. }
  303. $options = array('api_key' => $this->apiKey, 'method' => $method, 'photo_id' => $id);
  304. $restClient = $this->getRestClient();
  305. $restClient->getHttpClient()->resetParameters();
  306. $response = $restClient->restGet('/services/rest/', $options);
  307. $dom = new DOMDocument();
  308. $dom->loadXML($response->getBody());
  309. $xpath = new DOMXPath($dom);
  310. self::_checkErrors($dom);
  311. $retval = array();
  312. /**
  313. * @see Zend_Service_Flickr_Image
  314. */
  315. require_once 'Zend/Service/Flickr/Image.php';
  316. foreach ($xpath->query('//size') as $size) {
  317. $label = (string) $size->getAttribute('label');
  318. $retval[$label] = new Zend_Service_Flickr_Image($size);
  319. }
  320. return $retval;
  321. }
  322. /**
  323. * Returns a reference to the REST client, instantiating it if necessary
  324. *
  325. * @return Zend_Rest_Client
  326. */
  327. public function getRestClient()
  328. {
  329. if (null === $this->_restClient) {
  330. /**
  331. * @see Zend_Rest_Client
  332. */
  333. require_once 'Zend/Rest/Client.php';
  334. $this->_restClient = new Zend_Rest_Client(self::URI_BASE);
  335. }
  336. return $this->_restClient;
  337. }
  338. /**
  339. * Validate User Search Options
  340. *
  341. * @param array $options
  342. * @return void
  343. * @throws Zend_Service_Exception
  344. */
  345. protected function _validateUserSearch(array $options)
  346. {
  347. $validOptions = array('api_key', 'method', 'user_id', 'per_page', 'page', 'extras', 'min_upload_date',
  348. 'min_taken_date', 'max_upload_date', 'max_taken_date');
  349. $this->_compareOptions($options, $validOptions);
  350. /**
  351. * @see Zend_Validate_Between
  352. */
  353. require_once 'Zend/Validate/Between.php';
  354. $between = new Zend_Validate_Between(1, 500, true);
  355. if (!$between->isValid($options['per_page'])) {
  356. /**
  357. * @see Zend_Service_Exception
  358. */
  359. require_once 'Zend/Service/Exception.php';
  360. throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option');
  361. }
  362. /**
  363. * @see Zend_Validate_Int
  364. */
  365. require_once 'Zend/Validate/Int.php';
  366. $int = new Zend_Validate_Int();
  367. if (!$int->isValid($options['page'])) {
  368. /**
  369. * @see Zend_Service_Exception
  370. */
  371. require_once 'Zend/Service/Exception.php';
  372. throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option');
  373. }
  374. // validate extras, which are delivered in csv format
  375. if ($options['extras']) {
  376. $extras = explode(',', $options['extras']);
  377. $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server');
  378. foreach($extras as $extra) {
  379. /**
  380. * @todo The following does not do anything [yet], so it is commented out.
  381. */
  382. //in_array(trim($extra), $validExtras);
  383. }
  384. }
  385. }
  386. /**
  387. * Validate Tag Search Options
  388. *
  389. * @param array $options
  390. * @return void
  391. * @throws Zend_Service_Exception
  392. */
  393. protected function _validateTagSearch(array $options)
  394. {
  395. $validOptions = array('method', 'api_key', 'user_id', 'tags', 'tag_mode', 'text', 'min_upload_date',
  396. 'max_upload_date', 'min_taken_date', 'max_taken_date', 'license', 'sort',
  397. 'privacy_filter', 'bbox', 'accuracy', 'machine_tags', 'machine_tag_mode', 'group_id',
  398. 'extras', 'per_page', 'page');
  399. $this->_compareOptions($options, $validOptions);
  400. /**
  401. * @see Zend_Validate_Between
  402. */
  403. require_once 'Zend/Validate/Between.php';
  404. $between = new Zend_Validate_Between(1, 500, true);
  405. if (!$between->isValid($options['per_page'])) {
  406. /**
  407. * @see Zend_Service_Exception
  408. */
  409. require_once 'Zend/Service/Exception.php';
  410. throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option');
  411. }
  412. /**
  413. * @see Zend_Validate_Int
  414. */
  415. require_once 'Zend/Validate/Int.php';
  416. $int = new Zend_Validate_Int();
  417. if (!$int->isValid($options['page'])) {
  418. /**
  419. * @see Zend_Service_Exception
  420. */
  421. require_once 'Zend/Service/Exception.php';
  422. throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option');
  423. }
  424. // validate extras, which are delivered in csv format
  425. if ($options['extras']) {
  426. $extras = explode(',', $options['extras']);
  427. $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server');
  428. foreach($extras as $extra) {
  429. /**
  430. * @todo The following does not do anything [yet], so it is commented out.
  431. */
  432. //in_array(trim($extra), $validExtras);
  433. }
  434. }
  435. }
  436. /**
  437. * Validate Group Search Options
  438. *
  439. * @param array $options
  440. * @throws Zend_Service_Exception
  441. * @return void
  442. */
  443. protected function _validateGroupPoolGetPhotos(array $options)
  444. {
  445. $validOptions = array('api_key', 'tags', 'method', 'group_id', 'per_page', 'page', 'extras', 'user_id');
  446. $this->_compareOptions($options, $validOptions);
  447. /**
  448. * @see Zend_Validate_Between
  449. */
  450. require_once 'Zend/Validate/Between.php';
  451. $between = new Zend_Validate_Between(1, 500, true);
  452. if (!$between->isValid($options['per_page'])) {
  453. /**
  454. * @see Zend_Service_Exception
  455. */
  456. require_once 'Zend/Service/Exception.php';
  457. throw new Zend_Service_Exception($options['per_page'] . ' is not valid for the "per_page" option');
  458. }
  459. /**
  460. * @see Zend_Validate_Int
  461. */
  462. require_once 'Zend/Validate/Int.php';
  463. $int = new Zend_Validate_Int();
  464. if (!$int->isValid($options['page'])) {
  465. /**
  466. * @see Zend_Service_Exception
  467. */
  468. require_once 'Zend/Service/Exception.php';
  469. throw new Zend_Service_Exception($options['page'] . ' is not valid for the "page" option');
  470. }
  471. // validate extras, which are delivered in csv format
  472. if (isset($options['extras'])) {
  473. $extras = explode(',', $options['extras']);
  474. $validExtras = array('license', 'date_upload', 'date_taken', 'owner_name', 'icon_server');
  475. foreach($extras as $extra) {
  476. /**
  477. * @todo The following does not do anything [yet], so it is commented out.
  478. */
  479. //in_array(trim($extra), $validExtras);
  480. }
  481. }
  482. }
  483. /**
  484. * Throws an exception if and only if the response status indicates a failure
  485. *
  486. * @param DOMDocument $dom
  487. * @return void
  488. * @throws Zend_Service_Exception
  489. */
  490. protected static function _checkErrors(DOMDocument $dom)
  491. {
  492. if ($dom->documentElement->getAttribute('stat') === 'fail') {
  493. $xpath = new DOMXPath($dom);
  494. $err = $xpath->query('//err')->item(0);
  495. /**
  496. * @see Zend_Service_Exception
  497. */
  498. require_once 'Zend/Service/Exception.php';
  499. throw new Zend_Service_Exception('Search failed due to error: ' . $err->getAttribute('msg')
  500. . ' (error #' . $err->getAttribute('code') . ')');
  501. }
  502. }
  503. /**
  504. * Prepare options for the request
  505. *
  506. * @param string $method Flickr Method to call
  507. * @param array $options User Options
  508. * @param array $defaultOptions Default Options
  509. * @return array Merged array of user and default/required options
  510. */
  511. protected function _prepareOptions($method, array $options, array $defaultOptions)
  512. {
  513. $options['method'] = (string) $method;
  514. $options['api_key'] = $this->apiKey;
  515. return array_merge($defaultOptions, $options);
  516. }
  517. /**
  518. * Throws an exception if and only if any user options are invalid
  519. *
  520. * @param array $options User options
  521. * @param array $validOptions Valid options
  522. * @return void
  523. * @throws Zend_Service_Exception
  524. */
  525. protected function _compareOptions(array $options, array $validOptions)
  526. {
  527. $difference = array_diff(array_keys($options), $validOptions);
  528. if ($difference) {
  529. /**
  530. * @see Zend_Service_Exception
  531. */
  532. require_once 'Zend/Service/Exception.php';
  533. throw new Zend_Service_Exception('The following parameters are invalid: ' . implode(',', $difference));
  534. }
  535. }
  536. }