Flickr.php 21 KB

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