PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/App/REST.php

http://github.com/atk4/atk4
PHP | 196 lines | 123 code | 30 blank | 43 comment | 17 complexity | 9dce4e19e63a4230a53207dcec89c949 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * REST Server implementation for Agile Toolkit.
  4. *
  5. * This class takes advantage of the tight integration for Agile Toolkit
  6. * to enhance and make it super simple to create an awesome API for
  7. * your existing application.
  8. */
  9. // @codingStandardsIgnoreStart because REST is acronym
  10. class App_REST extends App_CLI
  11. {
  12. // @codingStandardsIgnoreEnd
  13. public $doc_page = 'app/rest';
  14. public $page;
  15. public $endpoint;
  16. public $version;
  17. /**
  18. * Initialization.
  19. */
  20. public function init()
  21. {
  22. parent::init();
  23. try {
  24. // Extra 24-hour protection
  25. parent::init();
  26. $this->getLogger();
  27. $this->pm = $this->add($this->pagemanager_class, $this->pagemanager_options);
  28. /** @type Controller_PageManager $this->pm */
  29. $this->pm->parseRequestedURL();
  30. // It's recommended that you use versioning inside your API,
  31. // for example http://api.example.com/v1/user
  32. //
  33. // This way version is accessible anywhere from $this->app->version
  34. list($this->version,) = explode('_', $this->page, 2);
  35. // Add-ons may define additional endpoints for your API, but
  36. // you must activate them explicitly.
  37. $this->pathfinder->base_location->defineContents(array('endpoint' => 'endpoint'));
  38. } catch (Exception $e) {
  39. $this->caughtException($e);
  40. }
  41. }
  42. /**
  43. * Output will be properly fromatted.
  44. *
  45. * @param mixed $data
  46. */
  47. public function encodeOutput($data)
  48. {
  49. // TODO - use HTTP_ACCEPT here ?
  50. //var_dump($_SERVER['HTTP_ACCEPT']);
  51. if ($_GET['format'] == 'xml') {
  52. throw $this->exception('only JSON format is supported', null, 406);
  53. }
  54. if ($_GET['format'] == 'json_pretty') {
  55. header('Content-type: application/json');
  56. echo json_encode($data, JSON_PRETTY_PRINT);
  57. return;
  58. }
  59. if ($_GET['format'] == 'html') {
  60. echo '<pre>';
  61. echo json_encode($data, JSON_PRETTY_PRINT);
  62. return;
  63. }
  64. header('Content-type: application/json');
  65. if ($data === null) {
  66. $data = array();
  67. }
  68. echo json_encode($data);
  69. }
  70. /**
  71. * Main.
  72. */
  73. public function main()
  74. {
  75. $this->execute();
  76. $this->hook('saveDelayedModels');
  77. }
  78. /**
  79. * Execute.
  80. */
  81. public function execute()
  82. {
  83. try {
  84. try {
  85. $file = $this->app->locatePath('endpoint', str_replace('_', '/', $this->page).'.php');
  86. include_once $file;
  87. $this->pm->base_path = '/';
  88. } catch (Exception $e) {
  89. http_response_code(500);
  90. if ($e instanceof Exception_Pathfinder) {
  91. $error = array(
  92. 'error' => 'No such endpoint',
  93. 'type' => 'API_Error',
  94. );
  95. } else {
  96. $error = array(
  97. 'error' => 'Problem with endpoint',
  98. 'type' => 'API_Error',
  99. );
  100. }
  101. $this->caughtException($e);
  102. $this->encodeOutput($error);
  103. return;
  104. }
  105. try {
  106. $class = 'endpoint_'.$this->page;
  107. $this->endpoint = $this->add($class);
  108. $this->endpoint->app = $this;
  109. $this->endpoint->api = $this->endpoint->app; // compatibility with ATK 4.2 and lower
  110. $method = strtolower($_SERVER['REQUEST_METHOD']);
  111. $raw_post = file_get_contents('php://input');
  112. if ($raw_post && $raw_post[0] == '{') {
  113. $args = json_decode($raw_post, true);
  114. } elseif ($method == 'put') {
  115. parse_str($raw_post, $args);
  116. } else {
  117. $args = $_POST;
  118. }
  119. if ($_GET['method']) {
  120. $method .= '_'.$_GET['method'];
  121. }
  122. if (!$this->endpoint->hasMethod($method)) {
  123. throw $this->exception('Method does not exist for this endpoint', null, 404)
  124. ->addMoreInfo('method', $method)
  125. ->addMoreInfo('endpoint', $this->endpoint)
  126. ;
  127. }
  128. $this->logRequest($method, $args);
  129. // Perform the desired action
  130. $this->encodeOutput($this->endpoint->$method($args));
  131. $this->logSuccess();
  132. } catch (Exception $e) {
  133. $this->caughtException($e);
  134. http_response_code($e->getCode() ?: 500);
  135. $error = array(
  136. 'error' => $e->getMessage(),
  137. 'type' => get_class($e),
  138. 'more_info' => $e instanceof BaseException ? $e->more_info : null,
  139. );
  140. array_walk_recursive($error, function (&$item) {
  141. if (is_object($item)) {
  142. $item = (string) $item;
  143. }
  144. });
  145. $this->encodeOutput($error);
  146. }
  147. } catch (Exception $e) {
  148. $this->caughtException($e);
  149. }
  150. }
  151. /**
  152. * Override to extend the logging.
  153. *
  154. * @param string $method
  155. * @param array $args
  156. */
  157. public function logRequest($method, $args)
  158. {
  159. }
  160. /**
  161. * Overwrite to extend the logging.
  162. */
  163. public function logSuccess()
  164. {
  165. }
  166. }