External.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. /**
  3. * [Request_Client_External] provides a wrapper for all external request
  4. * processing. This class should be extended by all drivers handling external
  5. * requests.
  6. *
  7. * Supported out of the box:
  8. * - Streams (default)
  9. * - Curl (default if loaded)
  10. * - PECL HTTP (default if curl not loaded and pecl_http loaded)
  11. *
  12. * @package KO7\Base
  13. *
  14. * @copyright (c) 2007-2016 Kohana Team
  15. * @copyright (c) since 2016 Koseven Team
  16. * @license https://koseven.dev/LICENSE
  17. *
  18. */
  19. abstract class KO7_Request_Client_External extends Request_Client {
  20. /**
  21. * Use:
  22. * - Request_Client_Stream (default)
  23. * - Request_Client_HTTP
  24. * - Request_Client_Curl
  25. *
  26. * @var string Defines the external client to use by default
  27. */
  28. public static $client;
  29. /**
  30. * Request options
  31. *
  32. * @var array
  33. * @link http://www.php.net/manual/function.curl-setopt
  34. * @link http://www.php.net/manual/http.request.options
  35. */
  36. protected $_options = [];
  37. /**
  38. * Sends the HTTP message [Request] to a remote server and processes
  39. * the response. This one needs to be implemented by all Request Drivers.
  40. *
  41. * @param Request $request Request to send
  42. * @param Response $response Response to send
  43. *
  44. * @return Response
  45. */
  46. abstract protected function _send_message(Request $request, Response $response) :\Response;
  47. /**
  48. * Factory method to create a new Request_Client_External object based on
  49. * the client name passed, or defaulting to Request_Client_External::$client
  50. * by default.
  51. *
  52. * Request_Client_External::$client can be set in the application bootstrap.
  53. *
  54. * @param array $options Request options to pass to the client
  55. * @param string $client External client to use (to override default one)
  56. *
  57. * @throws Request_Exception
  58. *
  59. * @return Request_Client_External
  60. */
  61. public static function factory(array $options = [], $client = NULL)
  62. {
  63. // If no client given determine which one to use (prefer the faster and mature ones)
  64. //@codeCoverageIgnoreStart
  65. if ($client === NULL)
  66. {
  67. if (static::$client === NULL)
  68. {
  69. if (extension_loaded('curl'))
  70. {
  71. static::$client = 'Request_Client_Curl';
  72. }
  73. elseif (extension_loaded('http'))
  74. {
  75. static::$client = 'Request_Client_HTTP';
  76. }
  77. else
  78. {
  79. static::$client = 'Request_Client_Stream';
  80. }
  81. }
  82. $client = static::$client;
  83. }
  84. //@codeCoverageIgnoreEnd
  85. $client = new $client($options);
  86. // Check if client extends Request_Client_External
  87. if ( ! $client instanceof Request_Client_External)
  88. {
  89. throw new Request_Exception(':client is not a valid external Request Client.', [
  90. 'client' => get_class($client)
  91. ]);
  92. }
  93. // Set Request Options
  94. $client->options($options);
  95. return $client;
  96. }
  97. /**
  98. * Processes the request, executing the controller action that handles this
  99. * request, determined by the [Route].
  100. *
  101. * 1. Before the controller action is called, the [Controller::before] method
  102. * will be called.
  103. * 2. Next the controller action will be called.
  104. * 3. After the controller action is called, the [Controller::after] method
  105. * will be called.
  106. *
  107. * By default, the output from the controller is captured and returned, and
  108. * no headers are sent.
  109. *
  110. * $request->execute();
  111. *
  112. * @param Request $request A request object
  113. * @param Response $response A response object
  114. *
  115. * @throws Exception
  116. * @return Response
  117. */
  118. public function execute_request(Request $request, Response $response)
  119. {
  120. //@codeCoverageIgnoreStart
  121. if (KO7::$profiling)
  122. {
  123. // Set the benchmark name
  124. $benchmark = '"' . $request->uri() . '"';
  125. if ($request !== Request::$initial AND Request::$current)
  126. {
  127. // Add the parent request uri
  128. $benchmark .= ' « "' . Request::$current->uri() . '"';
  129. }
  130. // Start benchmarking
  131. $benchmark = Profiler::start('Requests', $benchmark);
  132. }
  133. //@codeCoverageIgnoreEnd
  134. // Store the current active request and replace current with new request
  135. $previous = Request::$current;
  136. Request::$current = $request;
  137. // Resolve the POST fields
  138. if ($post = $request->post())
  139. {
  140. $request
  141. ->body(http_build_query($post, NULL, '&'))
  142. ->headers('content-type', 'application/x-www-form-urlencoded; charset=' . KO7::$charset);
  143. }
  144. $request->headers('content-length', (string)$request->content_length());
  145. // If KO7 expose, set the user-agent
  146. if (KO7::$expose)
  147. {
  148. $request->headers('user-agent', KO7::version());
  149. }
  150. try
  151. {
  152. $response = $this->_send_message($request, $response);
  153. }
  154. catch (Exception $e)
  155. {
  156. // Restore the previous request
  157. Request::$current = $previous;
  158. //@codeCoverageIgnoreStart
  159. if (isset($benchmark))
  160. {
  161. // Delete the benchmark, it is invalid
  162. Profiler::delete($benchmark);
  163. }
  164. //@codeCoverageIgnoreEnd
  165. // Re-throw the exception
  166. throw $e;
  167. }
  168. // Restore the previous request
  169. Request::$current = $previous;
  170. //@codeCoverageIgnoreStart
  171. if (isset($benchmark))
  172. {
  173. // Stop the benchmark
  174. Profiler::stop($benchmark);
  175. }
  176. //@codeCoverageIgnoreEnd
  177. // Return the response
  178. return $response;
  179. }
  180. /**
  181. * Set and get options for this request.
  182. *
  183. * @param mixed $key Option name, or array of options
  184. * @param mixed $value Option value
  185. *
  186. * @return mixed
  187. */
  188. public function options($key = NULL, $value = NULL)
  189. {
  190. if ($key === NULL)
  191. {
  192. return $this->_options;
  193. }
  194. if (is_array($key))
  195. {
  196. $this->_options = $key;
  197. }
  198. elseif ($value === NULL)
  199. {
  200. return Arr::get($this->_options, $key);
  201. }
  202. else
  203. {
  204. $this->_options[$key] = $value;
  205. }
  206. return $this;
  207. }
  208. }