Service.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <?php
  2. /**
  3. * Lithium: the most rad php framework
  4. *
  5. * @copyright Copyright 2013, Union of RAD (http://union-of-rad.org)
  6. * @license http://opensource.org/licenses/bsd-license.php The BSD License
  7. */
  8. namespace lithium\net\http;
  9. use lithium\core\Libraries;
  10. use lithium\core\ClassNotFoundException;
  11. /**
  12. * Basic Http Service.
  13. *
  14. */
  15. class Service extends \lithium\core\Object {
  16. /**
  17. * The `Socket` instance used to send `Service` calls.
  18. *
  19. * @var lithium\net\Socket
  20. */
  21. public $connection = null;
  22. /**
  23. * Holds the last request and response object
  24. *
  25. * @var object
  26. */
  27. public $last = null;
  28. /**
  29. * Auto config
  30. *
  31. * @var array
  32. */
  33. protected $_autoConfig = array('classes' => 'merge', 'responseTypes');
  34. /**
  35. * Array of closures that return various pieces of information about an HTTP response.
  36. *
  37. * @var array
  38. */
  39. protected $_responseTypes = array();
  40. /**
  41. * Indicates whether `Service` can connect to the HTTP endpoint for which it is configured.
  42. * Defaults to true until a connection attempt fails.
  43. *
  44. * @var boolean
  45. */
  46. protected $_isConnected = false;
  47. /**
  48. * Fully-name-spaced class references to `Service` class dependencies.
  49. *
  50. * @var array
  51. */
  52. protected $_classes = array(
  53. 'media' => 'lithium\net\http\Media',
  54. 'request' => 'lithium\net\http\Request',
  55. 'response' => 'lithium\net\http\Response'
  56. );
  57. /**
  58. * Initializes a new `Service` instance with the default HTTP request settings and
  59. * transport- and format-handling classes.
  60. * @param array $config
  61. */
  62. public function __construct(array $config = array()) {
  63. $defaults = array(
  64. 'persistent' => false,
  65. 'scheme' => 'http',
  66. 'host' => 'localhost',
  67. 'port' => null,
  68. 'timeout' => 30,
  69. 'auth' => null,
  70. 'username' => null,
  71. 'password' => null,
  72. 'encoding' => 'UTF-8',
  73. 'socket' => 'Context'
  74. );
  75. parent::__construct($config + $defaults);
  76. }
  77. /**
  78. * Initialize connection
  79. *
  80. */
  81. protected function _init() {
  82. $config = array('classes' => $this->_classes) + $this->_config;
  83. try {
  84. $this->connection = Libraries::instance('socket', $config['socket'], $config);
  85. } catch(ClassNotFoundException $e) {
  86. $this->connection = null;
  87. }
  88. $this->_responseTypes += array(
  89. 'headers' => function($response) { return $response->headers; },
  90. 'body' => function($response) { return $response->body(); },
  91. 'code' => function($response) { return $response->status['code']; }
  92. );
  93. }
  94. /**
  95. * Magic method to handle other HTTP methods.
  96. *
  97. * @param string $method
  98. * @param string $params
  99. */
  100. public function __call($method, $params = array()) {
  101. array_unshift($params, $method);
  102. return $this->invokeMethod('send', $params);
  103. }
  104. /**
  105. * Custom check to determine if our given magic methods can be responded to.
  106. *
  107. * @param string $method Method name.
  108. * @param bool $internal Interal call or not.
  109. * @return bool
  110. */
  111. public function respondsTo($method, $internal = false) {
  112. return is_callable(array($this, $method), true);
  113. }
  114. /**
  115. * Send HEAD request.
  116. *
  117. * @param string $path
  118. * @param array $data
  119. * @param array $options
  120. * @return string
  121. */
  122. public function head($path = null, $data = array(), array $options = array()) {
  123. $defaults = array('return' => 'headers', 'type' => false);
  124. return $this->send(__FUNCTION__, $path, $data, $options + $defaults);
  125. }
  126. /**
  127. * Send GET request.
  128. *
  129. * @param string $path
  130. * @param array $data
  131. * @param array $options
  132. * @return string
  133. */
  134. public function get($path = null, $data = array(), array $options = array()) {
  135. $defaults = array('type' => false);
  136. return $this->send(__FUNCTION__, $path, $data, $options + $defaults);
  137. }
  138. /**
  139. * Send POST request.
  140. *
  141. * @param string $path
  142. * @param array $data
  143. * @param array $options
  144. * @return string
  145. */
  146. public function post($path = null, $data = array(), array $options = array()) {
  147. return $this->send(__FUNCTION__, $path, $data, $options);
  148. }
  149. /**
  150. * Send PUT request.
  151. *
  152. * @param string $path
  153. * @param array $data
  154. * @param array $options
  155. * @return string
  156. */
  157. public function put($path = null, $data = array(), array $options = array()) {
  158. return $this->send(__FUNCTION__, $path, $data, $options);
  159. }
  160. /**
  161. * Send PATCH request.
  162. *
  163. * @param string $path
  164. * @param array $data
  165. * @param array $options
  166. * @return string
  167. */
  168. public function patch($path = null, $data = array(), array $options = array()) {
  169. return $this->send(__FUNCTION__, $path, $data, $options);
  170. }
  171. /**
  172. * Send DELETE request.
  173. *
  174. * @param string $path
  175. * @param array $data
  176. * @param array $options
  177. * @return string
  178. */
  179. public function delete($path = null, $data = array(), array $options = array()) {
  180. $defaults = array('type' => false);
  181. return $this->send(__FUNCTION__, $path, $data, $options + $defaults);
  182. }
  183. /**
  184. * Send request and return response data.
  185. *
  186. * @param string $method
  187. * @param string $path
  188. * @param array $data the parameters for the request. For GET/DELETE this is the query string
  189. * for POST/PUT this is the body
  190. * @param array $options passed to request and socket
  191. * @return string
  192. */
  193. public function send($method, $path = null, $data = array(), array $options = array()) {
  194. $defaults = array('return' => 'body');
  195. $options += $defaults;
  196. $request = $this->_request($method, $path, $data, $options);
  197. $options += array('message' => $request);
  198. if (!$this->connection || !$this->connection->open($options)) {
  199. return;
  200. }
  201. $response = $this->connection->send($request, $options);
  202. $this->connection->close();
  203. if ($response->status['code'] == 401 && $auth = $response->digest()) {
  204. $request->auth = $auth;
  205. $this->connection->open(array('message' => $request) + $options);
  206. $response = $this->connection->send($request, $options);
  207. $this->connection->close();
  208. }
  209. $this->last = (object) compact('request', 'response');
  210. $handlers = $this->_responseTypes;
  211. $handler = isset($handlers[$options['return']]) ? $handlers[$options['return']] : null;
  212. return $handler ? $handler($response) : $response;
  213. }
  214. /**
  215. * Instantiates a request object (usually an instance of `http\Request`) and tests its
  216. * properties based on the request type and data to be sent.
  217. *
  218. * @param string $method The HTTP method of the request, i.e. `'GET'`, `'HEAD'`, `'OPTIONS'`,
  219. * etc. Can be passed in upper- or lower-case.
  220. * @param string $path The
  221. * @param string $data
  222. * @param string $options
  223. * @return object Returns an instance of `http\Request`, configured with an HTTP method, query
  224. * string or POST/PUT/PATCH data, and URL.
  225. */
  226. protected function _request($method, $path, $data, $options) {
  227. $defaults = array('type' => 'form');
  228. $options += $defaults + $this->_config;
  229. $request = $this->_instance('request', $options);
  230. $request->path = str_replace('//', '/', "{$request->path}{$path}");
  231. $request->method = $method = strtoupper($method);
  232. $hasBody = in_array($method, array('POST', 'PUT', 'PATCH'));
  233. $hasBody ? $request->body($data) : $request->query = $data;
  234. return $request;
  235. }
  236. }
  237. ?>