/plugin/PBAPI/PBAPI.php

https://bitbucket.org/chamilo/chamilo-ext-repo-photobucket-dev/ · PHP · 689 lines · 346 code · 63 blank · 280 comment · 65 complexity · 64d29b73d279f743f9ff5d98da4dc292 MD5 · raw file

  1. <?php
  2. /**
  3. * Photobucket API
  4. * Fluent interface for PHP5
  5. *
  6. * @author jhart
  7. * @package PBAPI
  8. *
  9. * @copyright Copyright (c) 2008, Photobucket, Inc.
  10. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  11. */
  12. require_once dirname(__FILE__) . '/PBAPI/Exception.php';
  13. /**
  14. * Load Exceptions
  15. */
  16. /**
  17. * Load Response Exceptions
  18. */
  19. require_once dirname(__FILE__) . '/PBAPI/Exception/Response.php';
  20. /**
  21. * PBAPI Class
  22. * Main class for Photobucket API interaction
  23. *
  24. * @package PBAPI
  25. */
  26. class PBAPI
  27. {
  28. /**
  29. * Request object holder
  30. *
  31. * @var PBAPI_Request
  32. */
  33. protected $request;
  34. /**
  35. * Response parser holder
  36. *
  37. * @var PBAPI_Response
  38. */
  39. protected $response;
  40. /**
  41. * Methods classes holder
  42. *
  43. * @var PBAPI_Methods
  44. */
  45. protected $methods;
  46. /**
  47. * current method stack
  48. *
  49. * @var array
  50. */
  51. protected $method_stack = array();
  52. /**
  53. * Current parameter set
  54. *
  55. * @var array key->value pairs
  56. */
  57. protected $params = array();
  58. /**
  59. * Current URI
  60. *
  61. * @var string
  62. */
  63. protected $uri;
  64. /**
  65. * Current username
  66. *
  67. * @var string
  68. */
  69. protected $username = '';
  70. /**
  71. * Flag to not reset after method call
  72. *
  73. * @var bool
  74. */
  75. protected $noReset = false;
  76. /**
  77. * Method validation map
  78. *
  79. * @static
  80. * @var array
  81. */
  82. static $method_validation_map;
  83. /**
  84. * Class constructor
  85. * Sets up request, consumer and methods
  86. *
  87. * @param string $consumer_key OAuth consumer key (scid)
  88. * @param string $consumer_secret OAuth consumer secret (private key)
  89. * @param string $type [optional, default=determined] Request type (one of class names in PBAPI/Request/*)
  90. * @param string $subdomain [optional, default='api'] default subdomain to use in requests
  91. * @param string $default_format [optional, default='xml'] response format to receive from api
  92. * @param array $type_params [optional, default=none] Request class parameters
  93. */
  94. public function __construct($consumer_key, $consumer_secret, $type = null, $subdomain = 'api', $default_format = 'xml', $type_params = array())
  95. {
  96. $this->_loadMethodClass('base');
  97. $this->setRequest($type, $subdomain, $default_format, $type_params);
  98. $this->setOAuthConsumer($consumer_key, $consumer_secret);
  99. }
  100. /////////////////////// Setup and Settings ///////////////////////
  101. /**
  102. * Set OAuth Token
  103. *
  104. * @param string $token oauth token
  105. * @param string $token_secret oauth secret
  106. * @param string $username [optional, default=nochange] username associated with this token
  107. * @return PBAPI $this Fluent reference to self
  108. * @throws PBAPI_Exception on missing request
  109. */
  110. public function setOAuthToken($token, $token_secret, $username = '')
  111. {
  112. if ($this->request)
  113. $this->request->setOAuthToken($token, $token_secret);
  114. else
  115. throw new PBAPI_Exception('Request missing - cannot set OAuth Token', $this);
  116. if ($username)
  117. $this->username = $username;
  118. return $this;
  119. }
  120. /**
  121. * Get OAuth Token
  122. *
  123. * @throws PBAPI_Exception on missing request
  124. */
  125. public function getOAuthToken()
  126. {
  127. if ($this->request)
  128. return $this->request->getOAuthToken();
  129. else
  130. throw new PBAPI_Exception('Request missing - cannot get OAuth Token', $this);
  131. }
  132. /**
  133. * Set OAuth Consumer info
  134. *
  135. * @param string $consumer_key OAuth consumer key (scid)
  136. * @param string $consumer_secret OAuth consumer secret (private key)
  137. * @return PBAPI $this Fluent reference to self
  138. * @throws PBAPI_Exception on missing request
  139. */
  140. public function setOAuthConsumer($consumer_key, $consumer_secret)
  141. {
  142. if ($this->request)
  143. $this->request->setOAuthConsumer($consumer_key, $consumer_secret);
  144. else
  145. throw new PBAPI_Exception('Request missing - cannot set OAuth Token', $this);
  146. return $this;
  147. }
  148. /**
  149. * Set current subdomain
  150. *
  151. * @param string $subdomain
  152. * @return PBAPI $this Fluent reference to self
  153. * @throws PBAPI_Exception on missing request
  154. */
  155. public function setSubdomain($subdomain)
  156. {
  157. if ($this->request)
  158. $this->request->setSubdomain($subdomain);
  159. else
  160. throw new PBAPI_Exception('Request missing - cannot set Subdomain', $this);
  161. return $this;
  162. }
  163. /**
  164. * Get current subdomain
  165. */
  166. public function getSubdomain()
  167. {
  168. if ($this->request)
  169. return $this->request->getSubdomain();
  170. else
  171. throw new PBAPI_Exception('Request missing - cannot get Subdomain', $this);
  172. }
  173. /**
  174. * Get oauth token username
  175. */
  176. public function getUsername()
  177. {
  178. return $this->username;
  179. }
  180. /**
  181. * Set response parser
  182. *
  183. * @param string $type [optional, default=none] type of response parser (one of PBAPI/Response/*)
  184. * @param array $params [optional, default=none] parameters to set up parser
  185. * @return PBAPI $this Fluent reference to self
  186. */
  187. public function setResponseParser($type = null, $params = array())
  188. {
  189. $class = 'PBAPI_Response_' . $type;
  190. if (! class_exists($class))
  191. require ('PBAPI/Response/' . $type . '.php');
  192. $this->response = new $class($params);
  193. if (! $this->response)
  194. throw new PBAPI_Exception('Could not get Response Parser', $this);
  195. if (! $this->request)
  196. throw new PBAPI_Exception('Request missing - cannot set OAuth Token', $this);
  197. $this->request->setDefaultFormat($this->response->getFormat());
  198. return $this;
  199. }
  200. /**
  201. * Set request method
  202. *
  203. * @param string $type [optional, default=determined] Request type (one of class names in PBAPI/Request/*)
  204. * @param string $subdomain [optional, default='api'] default subdomain to use in requests
  205. * @param string $default_format [optional, default='xml'] response format to receive from api
  206. * @param array $type_params [optional, default=none] Request class parameters
  207. * @return PBAPI $this Fluent reference to self
  208. */
  209. public function setRequest($type = null, $subdomain = 'api', $default_format = 'xml', $request_params = array())
  210. {
  211. if (! $type)
  212. $type = self :: _detectRequestStrategy();
  213. $class = 'PBAPI_Request_' . $type;
  214. if (! class_exists($class))
  215. require ('PBAPI/Request/' . $type . '.php');
  216. $this->request = new $class($subdomain, $default_format, $request_params);
  217. return $this;
  218. }
  219. /**
  220. * Attempt to detect request strategy and set the type
  221. *
  222. * @return string
  223. */
  224. protected static function _detectRequestStrategy()
  225. {
  226. if (function_exists('curl_init'))
  227. return 'curl';
  228. if (ini_get('allow_url_fopen'))
  229. return 'fopenurl';
  230. }
  231. /**
  232. * Reset current data
  233. *
  234. * @param bool $uri [optional] reset URI data
  235. * @param bool $methods [optional] reset method data (current method depth)
  236. * @param bool $params [optional] reset all parameters
  237. * @param bool $auth [optional] reset auth token
  238. * @return PBAPI $this Fluent reference to self
  239. */
  240. public function reset($uri = true, $methods = true, $params = true, $auth = false)
  241. {
  242. if ($uri)
  243. $this->uri = null;
  244. if ($methods)
  245. {
  246. $this->methods->_reset();
  247. $this->method_stack = array();
  248. }
  249. if ($params)
  250. $this->params = array();
  251. if ($auth && $this->request)
  252. $this->resetOAuthToken();
  253. return $this;
  254. }
  255. /**
  256. * Set No Reset Flag
  257. *
  258. * @param bool $set
  259. * @return PBAPI $this Fluent reference to self
  260. */
  261. public function setNoReset($set)
  262. {
  263. $this->noReset = $set;
  264. return $this;
  265. }
  266. /////////////////////// Requests and Responses ///////////////////////
  267. /**
  268. * Get current parameters
  269. *
  270. * @return array current parameter key->values
  271. */
  272. public function getParams()
  273. {
  274. return $this->params;
  275. }
  276. /**
  277. * Get parsed response (from response parser)
  278. *
  279. * @param bool $onlycontent only return 'content' of response
  280. * @return mixed
  281. * @throws PBAPI_Exception on no response parser
  282. * @throws PBAPI_Exception_Response on response exception
  283. */
  284. public function getParsedResponse($onlycontent = false)
  285. {
  286. if (! $this->response)
  287. throw new PBAPI_Exception('No response parser set up', $this);
  288. try
  289. {
  290. return $this->response->parse(trim($this->response_string), $onlycontent);
  291. }
  292. catch (PBAPI_Exception_Response $e)
  293. {
  294. //set core into exception
  295. throw new PBAPI_Exception_Response($e->getMessage(), $e->getCode(), $this);
  296. }
  297. }
  298. /**
  299. * Get raw response string
  300. *
  301. * @return string
  302. */
  303. public function getResponseString()
  304. {
  305. return $this->response_string;
  306. }
  307. /**#@+
  308. * Forward current set up request to the request method and get back the response
  309. *
  310. * @return PBAPI $this Fluent reference to self
  311. */
  312. public function get()
  313. {
  314. $this->_validateRequest('get');
  315. $this->_setResponse($this->request->get($this->uri, $this->params));
  316. if (! $this->noReset)
  317. $this->reset();
  318. return $this;
  319. }
  320. public function post()
  321. {
  322. $this->_validateRequest('post');
  323. $this->_setResponse($this->request->post($this->uri, $this->params));
  324. if (! $this->noReset)
  325. $this->reset();
  326. return $this;
  327. }
  328. public function put()
  329. {
  330. $this->_validateRequest('put');
  331. $this->_setResponse($this->request->put($this->uri, $this->params));
  332. if (! $this->noReset)
  333. $this->reset();
  334. return $this;
  335. }
  336. public function delete()
  337. {
  338. $this->_validateRequest('delete');
  339. $this->_setResponse($this->request->delete($this->uri, $this->params));
  340. if (! $this->noReset)
  341. $this->reset();
  342. return $this;
  343. }
  344. /**#@-*/
  345. /**
  346. * Load and set the current OAuth token from the last response string
  347. *
  348. * @param bool $subdomain true if you want to also set the current default call subdomain to what is in the response.
  349. * @return PBAPI $this Fluent reference to self
  350. */
  351. public function loadTokenFromResponse($subdomain = true)
  352. {
  353. $string = trim($this->response_string);
  354. $params = array();
  355. parse_str($string, $params);
  356. if (empty($params) || empty($params['oauth_token']) || empty($params['oauth_token_secret']))
  357. {
  358. throw new PBAPI_Exception('Token and Token Secret returned in response');
  359. }
  360. $username = (! empty($params['username'])) ? $params['username'] : '';
  361. $this->setOAuthToken($params['oauth_token'], $params['oauth_token_secret'], $username);
  362. if ($subdomain && ! empty($params['subdomain']))
  363. {
  364. $this->setSubdomain($params['subdomain']);
  365. }
  366. return $this;
  367. }
  368. /**
  369. * Go to Redirect URL
  370. * does actual header()
  371. *
  372. * @param string $type [login|logout|...]
  373. * @param string $extra [optional] set 'extra' parameter
  374. * @throws PBAPI_Exception on invalid redirect
  375. */
  376. public function goRedirect($type = null, $extra = null)
  377. {
  378. if (strpos($type, 'http://') !== 0)
  379. {
  380. switch ($type)
  381. {
  382. case 'login' :
  383. $this->request->redirectLogin($extra);
  384. case 'logout' :
  385. $this->request->redirectLogout($extra);
  386. default :
  387. throw new PBAPI_Exception('Invalid redirect', $this);
  388. }
  389. }
  390. else
  391. {
  392. if ($extra)
  393. {
  394. $sep = (strpos($type, '?')) ? '&' : '?';
  395. $url = $type . $sep . 'extra=' . $extra;
  396. }
  397. else
  398. {
  399. $url = $type;
  400. }
  401. header('Location: ' . $url);
  402. exit();
  403. }
  404. }
  405. /////////////////////// Inter Class 'Private' Methods ///////////////////////
  406. /**
  407. * Set a parameter
  408. *
  409. * @param string $name
  410. * @param string $value
  411. * @return PBAPI $this Fluent reference to self
  412. */
  413. public function _setParam($name, $value)
  414. {
  415. $this->params[$name] = $value;
  416. return $this;
  417. }
  418. /**
  419. * Set a list of parameters
  420. *
  421. * @param array $pairs parameters as key=>value (allowing empty)
  422. * @return PBAPI $this Fluent reference to self
  423. */
  424. public function _setParamList($pairs)
  425. {
  426. if (! $pairs)
  427. return $this;
  428. foreach ($pairs as $name => $value)
  429. {
  430. $this->_setParam($name, $value);
  431. }
  432. return $this;
  433. }
  434. /**
  435. * Set current URI
  436. *
  437. * @param string $uri uri string to set, sprintf format
  438. * @param array $replacements [optional, default=none] if uri is sprintf string, replacements from this array in array order
  439. * @return PBAPI $this Fluent reference to self
  440. */
  441. public function _setUri($uri, $replacements = null)
  442. {
  443. if ($replacements !== null && ! is_array($replacements))
  444. $replacements = array($replacements);
  445. if ($replacements !== null)
  446. {
  447. $replacements = array_map('urlencode', $replacements);
  448. $this->uri = vsprintf($uri, $replacements);
  449. }
  450. else
  451. $this->uri = $uri;
  452. return $this;
  453. }
  454. /**
  455. * Append more to the current uri
  456. *
  457. * @param string $uri uri string to set, sprintf format
  458. * @param array $replacements [optional, default=none] if uri is sprintf string, replacements from this array in array order
  459. * @return PBAPI $this Fluent reference to self
  460. */
  461. public function _appendUri($uri, $replacements = null)
  462. {
  463. if ($replacements !== null && ! is_array($replacements))
  464. $replacements = array($replacements);
  465. if ($replacements !== null)
  466. {
  467. $replacements = array_map('urlencode', $replacements);
  468. $this->uri .= vsprintf($uri, $replacements);
  469. }
  470. else
  471. $this->uri .= $uri;
  472. return $this;
  473. }
  474. /**
  475. * Load a method class
  476. *
  477. * @param string $name Method class name (one of PBAPI/Methods/*)
  478. * @return PBAPI_Methods
  479. */
  480. public function _loadMethodClass($name)
  481. {
  482. $class = 'PBAPI_Methods_' . $name;
  483. if (! class_exists($class))
  484. require_once (dirname(__FILE__) . '/PBAPI/Methods/' . $name . '.php');
  485. $classObj = new $class($this);
  486. return $this->_setMethods($classObj);
  487. }
  488. /**
  489. * Set Methods class
  490. *
  491. * @param PBAPI_Methods $class class instance
  492. * @return PBAPI $this Fluent reference to self
  493. */
  494. public function _setMethods($class)
  495. {
  496. $this->methods = $class;
  497. return $this;
  498. }
  499. /**
  500. * Get parameters currently in obj
  501. *
  502. * @return array key->value
  503. */
  504. public function _getParams()
  505. {
  506. return $this->params;
  507. }
  508. /**
  509. * Get method stack - this is the level list of the method
  510. *
  511. * @return array methods list (in call order)
  512. */
  513. public function _getMethodStack()
  514. {
  515. return $this->method_stack;
  516. }
  517. /**
  518. * Set the current response string
  519. *
  520. * @param string $string
  521. * @return PBAPI $this Fluent reference to self
  522. */
  523. protected function _setResponse($string)
  524. {
  525. $this->response_string = $string;
  526. return $this;
  527. }
  528. /**
  529. * Validate Request (as currently set)
  530. *
  531. * $dt = syck_load(file_get_contents('api-defs.yml'));
  532. * file_put_contents('methods.dat', serialize($dt));
  533. *
  534. * @param string $method HTTP method to check against
  535. * @return PBAPI $this Fluent reference to self
  536. * @throws PBAPI_Exception method or parameters don't match presets
  537. */
  538. protected function _validateRequest($method)
  539. {
  540. //get proper map
  541. $map = $this->_loadMethodValidationMap();
  542. //fixup stack
  543. $stack = $this->method_stack;
  544. if (empty($stack[1]))
  545. $stack[1] = '_default';
  546. //get method
  547. $val_methods = $map[$stack[0]][$stack[1]];
  548. if (! $val_methods || ! array_key_exists($method, $val_methods))
  549. throw new PBAPI_Exception('invalid method: ' . $method, $this);
  550. //get parameters
  551. $val_params = $val_methods[$method];
  552. if ($val_params)
  553. {
  554. //look for unknown parameters (if parameters are specified)
  555. $unknowns = array_diff_key($this->params, $val_params);
  556. if (count($unknowns))
  557. throw new PBAPI_Exception('unknown parameters: ' . implode(', ', array_keys($unknowns)), $this);
  558. //look for missing required parameters
  559. $missing = array_diff_key($val_params, $this->params);
  560. if (count($missing))
  561. {
  562. foreach ($missing as $key => $val)
  563. {
  564. if ($val != 'required')
  565. unset($missing[$key]);
  566. if ($key == 'aid' || $key == 'mid' || $key == 'uid' || $key == 'tagid') //todo somehow do this better
  567. unset($missing[$key]); //also skip stuff we're catching already
  568. }
  569. if (count($missing))
  570. throw new PBAPI_Exception('missing required parameters: ' . implode(', ', array_keys($missing)), $this);
  571. }
  572. }
  573. return $this;
  574. }
  575. /**
  576. * Load validation map from data file
  577. * Loads from ./PBAPI/data/methods.dat - a php serialize() file.
  578. * The .yml file in the same directory is the 'source' of that dat file.
  579. *
  580. * @return array validation map from data file
  581. */
  582. protected function _loadMethodValidationMap()
  583. {
  584. if (! self :: $method_validation_map)
  585. {
  586. $path = dirname(__FILE__) . '/PBAPI/data/methods.dat';
  587. self :: $method_validation_map = unserialize(file_get_contents($path));
  588. if (! self :: $method_validation_map)
  589. throw new PBAPI_Exception('Could not load method map', $this);
  590. }
  591. return self :: $method_validation_map;
  592. }
  593. /////////////////////// Magics ///////////////////////
  594. /**
  595. * Magic function to forward other calls to the Methods
  596. * This is the meat of the API, really
  597. *
  598. * @param string $name function name
  599. * @param array $args argument array
  600. * @return PBAPI $this Fluent reference to self
  601. */
  602. public function __call($name, $args)
  603. {
  604. if (empty($args))
  605. {
  606. $this->methods->$name();
  607. }
  608. else
  609. if (! empty($args[0]) && empty($args[1]))
  610. {
  611. $this->methods->$name($args[0]);
  612. }
  613. else
  614. if (! empty($args[0]) && ! empty($args[1]))
  615. {
  616. $this->methods->$name($args[0], $args[1]);
  617. }
  618. else
  619. {
  620. //not currently used, but for forward compatibility
  621. call_user_func_array(array($this->methods, $name), $args);
  622. }
  623. $this->method_stack[] = $name;
  624. return $this;
  625. }
  626. }