Http.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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\data\source;
  9. use lithium\util\String;
  10. use lithium\data\model\Query;
  11. /**
  12. * Http class to access data sources using `lithium\net\http\Service`.
  13. */
  14. class Http extends \lithium\data\Source {
  15. /**
  16. * Service connection
  17. *
  18. * @var object lithium\net\http\Service
  19. */
  20. public $connection = null;
  21. /**
  22. * The set of array keys which will be auto-populated in the object's protected properties from
  23. * constructor parameters.
  24. *
  25. * @var array
  26. */
  27. protected $_autoConfig = array('classes' => 'merge', 'methods' => 'merge');
  28. /**
  29. * Fully-namespaced class references
  30. *
  31. * @var array
  32. */
  33. protected $_classes = array(
  34. 'schema' => 'lithium\data\Schema',
  35. 'service' => 'lithium\net\http\Service',
  36. 'relationship' => 'lithium\data\model\Relationship'
  37. );
  38. /**
  39. * Is Connected?
  40. *
  41. * @var boolean
  42. */
  43. protected $_isConnected = false;
  44. /**
  45. * List of methods and their corresponding HTTP method and path.
  46. *
  47. * @var array
  48. */
  49. protected $_methods = array(
  50. 'create' => array('method' => 'post', 'path' => "/{:source}"),
  51. 'read' => array('method' => 'get', 'path' => "/{:source}"),
  52. 'update' => array('method' => 'put', 'path' => "/{:source}/{:id}"),
  53. 'delete' => array('method' => 'delete', 'path' => "/{:source}/{:id}")
  54. );
  55. /**
  56. * Constructor
  57. *
  58. * @param array $config
  59. */
  60. public function __construct(array $config = array()) {
  61. $defaults = array(
  62. 'adapter' => null,
  63. 'persistent' => false,
  64. 'scheme' => 'http',
  65. 'host' => 'localhost',
  66. 'version' => '1.1',
  67. 'auth' => null,
  68. 'login' => '',
  69. 'password' => '',
  70. 'port' => 80,
  71. 'timeout' => 30,
  72. 'encoding' => 'UTF-8',
  73. 'methods' => array()
  74. );
  75. $config = $config + $defaults;
  76. $config['username'] = $config['login'];
  77. parent::__construct($config);
  78. }
  79. protected function _init() {
  80. $config = $this->_config;
  81. unset($config['type']);
  82. $this->connection = $this->_instance('service', $config);
  83. parent::_init();
  84. }
  85. /**
  86. * Pass properties to service connection
  87. *
  88. * @param string $property
  89. * @return mixed
  90. */
  91. public function __get($property) {
  92. return $this->connection->{$property};
  93. }
  94. /**
  95. * Pass methods to service connection. Path and method are determined from Http::$_method. If
  96. * not set, a GET request with the $method as the path will be used.
  97. *
  98. * @see lithium\data\source\Http::$_method
  99. * @param string $method
  100. * @param array $params
  101. * @return mixed
  102. * @filter
  103. */
  104. public function __call($method, $params) {
  105. if (!isset($this->_methods[$method])) {
  106. if (method_exists($this->connection, $method)) {
  107. return $this->connection->invokeMethod($method, $params);
  108. }
  109. $this->_methods[$method] = array('path' => "/{$method}");
  110. }
  111. $params += array(array(), array());
  112. if (!is_object($params[0])) {
  113. $config = (array) $params[0];
  114. if (count($config) === count($config, COUNT_RECURSIVE)) {
  115. $config = array('data' => $config);
  116. }
  117. $params[0] = new Query($this->_methods[$method] + $config);
  118. }
  119. $params[0] = new Query($params[0]->export($this) + $this->_methods[$method]);
  120. return $this->_filter(__CLASS__ . "::" . $method, $params, function($self, $params) {
  121. list($query, $options) = $params;
  122. return $self->send($query, $options);
  123. });
  124. }
  125. /**
  126. * Custom check to determine if our given magic methods can be responded to.
  127. *
  128. * @param string $method Method name.
  129. * @param bool $internal Interal call or not.
  130. * @return bool
  131. */
  132. public function respondsTo($method, $internal = false) {
  133. return isset($this->_methods[$method]) || parent::respondsTo($method, $internal);
  134. }
  135. /**
  136. * Method to send to a specific resource.
  137. *
  138. * @param array $query a query object
  139. * @param array $options array.
  140. * @return result
  141. */
  142. public function send($query = null, array $options = array()) {
  143. $query = !is_object($query) ? new Query((array) $query) : $query;
  144. $method = $query->method() ?: "get";
  145. $path = $query->path();
  146. $data = $query->data();
  147. $insert = (array) $options + $data + $query->export($this);
  148. if (preg_match_all('/\{:(\w+)\}/', $path, $matches)) {
  149. $keys = array_flip($matches[1]);
  150. $data = array_diff_key($data, array_flip($matches[1]));
  151. }
  152. $path = String::insert($path, $insert, array('clean' => true));
  153. $data += (array) $query->conditions() + array('limit' => $query->limit());
  154. return $this->connection->{$method}($path, $data, (array) $options);
  155. }
  156. /**
  157. * Fake the connection since service is called for every method.
  158. *
  159. * @return boolean
  160. */
  161. public function connect() {
  162. if (!$this->_isConnected) {
  163. $this->_isConnected = true;
  164. }
  165. return $this->_isConnected;
  166. }
  167. /**
  168. * Disconnect from socket.
  169. *
  170. * @return boolean
  171. */
  172. public function disconnect() {
  173. if ($this->_isConnected && $this->connection !== null) {
  174. $this->_isConnected = false;
  175. }
  176. return !$this->_isConnected;
  177. }
  178. /**
  179. * Returns available data sources (typically a list of REST resources collections).
  180. *
  181. * @param object $class
  182. * @return array
  183. */
  184. public function sources($class = null) {
  185. return array();
  186. }
  187. /**
  188. * Describe data source.
  189. *
  190. * @param string $entity
  191. * @param array $fields
  192. * @param array $meta
  193. * @return array - returns an empty array
  194. */
  195. public function describe($entity, $fields = array(), array $meta = array()) {
  196. return $this->_instance('schema', compact('fields', 'meta'));
  197. }
  198. /**
  199. * Create function used to POST.
  200. *
  201. * @param object $query
  202. * @param array $options
  203. * @return void
  204. * @filter
  205. */
  206. public function create($query, array $options = array()) {
  207. $query = !is_object($query) ? new Query() : $query;
  208. $query->method() ?: $query->method("post");
  209. $query->path() ?: $query->path("/{:source}");
  210. return $this->_filter(__METHOD__, array($query, $options), function($self, $params) {
  211. list($query, $options) = $params;
  212. return $self->send($query, $options);
  213. });
  214. }
  215. /**
  216. * Read used by model to GET.
  217. *
  218. * @param object $query
  219. * @param array $options
  220. * @return string
  221. * @filter
  222. */
  223. public function read($query, array $options = array()) {
  224. $query = !is_object($query) ? new Query() : $query;
  225. $query->method() ?: $query->method("get");
  226. $query->path() ?: $query->path("/{:source}");
  227. return $this->_filter(__METHOD__, array($query, $options), function($self, $params) {
  228. list($query, $options) = $params;
  229. return $self->send($query, $options);
  230. });
  231. }
  232. /**
  233. * Update used by model to PUT.
  234. *
  235. * @param object $query
  236. * @param array $options
  237. * @return string
  238. * @filter
  239. */
  240. public function update($query, array $options = array()) {
  241. $query = !is_object($query) ? new Query() : $query;
  242. $query->method() ?: $query->method("put");
  243. $query->path() ?: $query->path("/{:source}/{:id}");
  244. return $this->_filter(__METHOD__, array($query, $options), function($self, $params) {
  245. list($query, $options) = $params;
  246. return $self->send($query, $options);
  247. });
  248. }
  249. /**
  250. * Used by model to DELETE.
  251. *
  252. * @param object $query
  253. * @param array $options
  254. * @return string
  255. * @filter
  256. */
  257. public function delete($query, array $options = array()) {
  258. $query = !is_object($query) ? new Query() : $query;
  259. $query->method() ?: $query->method("delete");
  260. $query->path() ?: $query->path("/{:source}/{:id}");
  261. return $this->_filter(__METHOD__, array($query, $options), function($self, $params) {
  262. list($query, $options) = $params;
  263. return $self->send($query, $options);
  264. });
  265. }
  266. /**
  267. * Defines or modifies the default settings of a relationship between two models.
  268. *
  269. * @param string $class
  270. * @param string $type
  271. * @param string $name
  272. * @param array $options
  273. * @return array Returns an array containing the configuration for a model relationship.
  274. */
  275. public function relationship($class, $type, $name, array $options = array()) {
  276. if (isset($this->_classes['relationship'])) {
  277. return $this->_instance('relationship', compact('type', 'name') + $options);
  278. }
  279. return null;
  280. }
  281. }
  282. ?>