REST.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. <?php
  2. /**
  3. * Abstract Controller class for REST controller mapping.
  4. * Supports GET, PUT, POST, and DELETE.
  5. *
  6. * @package KO7\REST
  7. *
  8. * @copyright (c) since 2016 Koseven Team
  9. * @license https://koseven.ga/LICENSE
  10. */
  11. abstract class KO7_Controller_REST extends Controller {
  12. /**
  13. * REST types
  14. *
  15. * @var array
  16. */
  17. protected $_action_map = [
  18. HTTP_Request::GET => 'index',
  19. HTTP_Request::PUT => 'update',
  20. HTTP_Request::POST => 'create',
  21. HTTP_Request::DELETE => 'delete'
  22. ];
  23. /**
  24. * Automatically executed before the controller action.
  25. * Evaluate Request (method, action, parameter, format)
  26. */
  27. public function before() : void
  28. {
  29. // Parent call
  30. parent::before();
  31. // Determine the request action from the request method, if the action/method is not allowed throw error
  32. // We need to do this because this module does not support all HTTP_Requests
  33. if ( ! isset($this->_action_map[$this->request->method()]))
  34. {
  35. $this->response
  36. ->status(405)
  37. ->headers('Allow', implode(', ', array_keys($this->_action_map)));
  38. }
  39. else
  40. {
  41. $this->request->action($this->_action_map[$this->request->method()]);
  42. }
  43. }
  44. /**
  45. * Automatically executed after the controller action.
  46. *
  47. * - Adds cache and content header(s).
  48. * - Formats body with given formatting method
  49. * - Adds attachment header if necessary
  50. *
  51. * @throws REST_Exception
  52. * @throws HTTP_Exception
  53. */
  54. public function after() : void
  55. {
  56. // Parent call
  57. parent::after();
  58. // Parse Parameter
  59. $params = $this->_parse_params();
  60. // Some clients cannot handle HTTP responses different than 200
  61. if (isset($params['suppressResponseCodes']) && (bool)$params['suppressResponseCodes'] === true)
  62. {
  63. $body = $this->response->body();
  64. $body['responseCode'] = $this->response->status();
  65. $this->response->body($body);
  66. $this->response->status(200);
  67. }
  68. // Try initializing the formatter
  69. try
  70. {
  71. $formatter = REST_Format::factory($this->request, $this->response);
  72. }
  73. catch (REST_Exception $e)
  74. {
  75. throw HTTP_Exception::factory(500, $e->getMessage(), NULL, $e);
  76. }
  77. // No cache / must-revalidate cache if method is not GET
  78. if ($this->request->method() !== HTTP_Request::GET)
  79. {
  80. $this->response->headers('cache-control', 'no-cache, no-store, max-age=0, must-revalidate');
  81. }
  82. // Format body
  83. $this->response->body($formatter->format());
  84. // Support attachment header
  85. if (isset($params['attachment']))
  86. {
  87. try
  88. {
  89. $this->response->send_file(TRUE, $params['attachment'].'.'.($this->request->param('format') ?: REST_Format::$default_format));
  90. }
  91. catch (KO7_Exception $e)
  92. {
  93. throw new REST_Exception($e->getMessage(), NULL, $e->getCode(), $e);
  94. }
  95. }
  96. }
  97. /**
  98. * Initializes the request params array based on the current request.
  99. *
  100. * @throws REST_Exception
  101. *
  102. * @return array
  103. */
  104. protected function _parse_params() : array
  105. {
  106. // If method is GET, fetch params from query
  107. if ($this->request->method() === HTTP_Request::GET)
  108. {
  109. return $this->request->query();
  110. }
  111. // Otherwise we have a PUT, POST, DELETE Method
  112. // If content_type is JSON we need to decode the body first
  113. if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== FALSE)
  114. {
  115. try
  116. {
  117. // Fix for PHP 7
  118. // json_decode() throws and error when decoding an empty string
  119. if ( ! empty($this->request->body()) )
  120. {
  121. $parsed_body = json_decode($this->request->body(), true, 512, JSON_THROW_ON_ERROR);
  122. }
  123. }
  124. catch (JsonException $e)
  125. {
  126. throw new REST_Exception($e->getMessage(), NULL, $e->getCode(), $e);
  127. }
  128. }
  129. else
  130. {
  131. parse_str($this->request->body(), $parsed_body);
  132. }
  133. return array_merge((array)$parsed_body, (array)$this->request->post());
  134. }
  135. }