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

/src/Couch.php

http://github.com/dready92/PHP-on-Couch
PHP | 706 lines | 369 code | 47 blank | 290 comment | 63 complexity | 4575935da945adb118f46a6536a8c7c5 MD5 | raw file
Possible License(s): LGPL-3.0
  1. <?PHP
  2. /*
  3. Copyright (C) 2009 Mickael Bailly
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. namespace PHPOnCouch;
  16. use Exception,
  17. InvalidArgumentException;
  18. /**
  19. * couch class
  20. *
  21. * basics to implement JSON / REST / HTTP CouchDB protocol
  22. *
  23. */
  24. class Couch
  25. {
  26. /**
  27. * @var string database source name
  28. */
  29. protected $dsn = '';
  30. /**
  31. * @var array database source name parsed
  32. */
  33. protected $dsn_parsed = null;
  34. /**
  35. * @var array couch options
  36. */
  37. protected $options = null;
  38. /**
  39. * @var array allowed HTTP methods for REST dialog
  40. */
  41. protected $HTTP_METHODS = array('GET', 'POST', 'PUT', 'DELETE', 'COPY');
  42. /**
  43. * @var resource HTTP server socket
  44. * @see _connect()
  45. */
  46. protected $socket = NULL;
  47. /**
  48. * @var boolean tell if curl PHP extension has been detected
  49. */
  50. protected $curl = FALSE;
  51. /**
  52. * @var string the session cookie
  53. */
  54. protected $sessioncookie = null;
  55. /**
  56. * class constructor
  57. *
  58. * @param string $dsn CouchDB Data Source Name
  59. * @param array $options Couch options
  60. */
  61. public function __construct($dsn, $options = array())
  62. {
  63. $this->dsn = preg_replace('@/+$@', '', $dsn);
  64. $this->options = $options;
  65. $this->dsn_parsed = parse_url($this->dsn);
  66. if (!isset($this->dsn_parsed['port'])) {
  67. $this->dsn_parsed['port'] = 80;
  68. }
  69. if (function_exists('curl_init'))
  70. $this->curl = TRUE;
  71. }
  72. /**
  73. * returns the DSN, untouched
  74. *
  75. * @return string DSN
  76. */
  77. public function dsn()
  78. {
  79. return $this->dsn;
  80. }
  81. /**
  82. * returns the options array
  83. *
  84. * @return string DSN
  85. */
  86. public function options()
  87. {
  88. return $this->options;
  89. }
  90. /**
  91. * get the session cookie
  92. * @return string cookie
  93. */
  94. public function getSessionCookie()
  95. {
  96. return $this->sessioncookie;
  97. }
  98. /**
  99. * set the session cookie to send in the headers
  100. * @param string $cookie the session cookie ( example : AuthSession=Y291Y2g6NENGNDgzNz )
  101. *
  102. * @return \Couch
  103. */
  104. public function setSessionCookie($cookie)
  105. {
  106. $this->sessioncookie = $cookie;
  107. return $this;
  108. }
  109. /**
  110. * return a part of the data source name
  111. *
  112. * if $part parameter is empty, returns dns array
  113. *
  114. * @param string $part part to return
  115. * @return string DSN part
  116. */
  117. public function dsn_part($part = null)
  118. {
  119. if (!$part) {
  120. return $this->dsn_parsed;
  121. }
  122. if (isset($this->dsn_parsed[$part])) {
  123. return $this->dsn_parsed[$part];
  124. }
  125. }
  126. /**
  127. * parse a CouchDB server response and sends back an array
  128. * the array contains keys :
  129. * status_code : the HTTP status code returned by the server
  130. * status_message : the HTTP message related to the status code
  131. * body : the response body (if any). If CouchDB server response Content-Type is application/json
  132. * the body will by json_decode()d
  133. *
  134. * @static
  135. * @param string $raw_data data sent back by the server
  136. * @param boolean $json_as_array is true, the json response will be decoded as an array. Is false, it's decoded as an object
  137. * @return array CouchDB response
  138. * @throws InvalidArgumentException
  139. */
  140. public static function parseRawResponse($raw_data, $json_as_array = FALSE)
  141. {
  142. if (!strlen($raw_data))
  143. throw new InvalidArgumentException("no data to parse");
  144. while (!substr_compare($raw_data, "HTTP/1.1 100 Continue\r\n\r\n", 0, 25)) {
  145. $raw_data = substr($raw_data, 25);
  146. }
  147. $response = array('body' => null);
  148. list($headers, $body) = explode("\r\n\r\n", $raw_data, 2);
  149. $headers_array = explode("\n", $headers);
  150. $status_line = reset($headers_array);
  151. $status_array = explode(' ', $status_line, 3);
  152. $response['status_code'] = trim($status_array[1]);
  153. $response['status_message'] = trim($status_array[2]);
  154. if (strlen($body)) {
  155. $response['body'] = preg_match('@Content-Type:\s+application/json@i', $headers) ? json_decode($body, $json_as_array) : $body;
  156. }
  157. return $response;
  158. }
  159. /**
  160. * send a query to the CouchDB server
  161. *
  162. * @param string $method HTTP method to use (GET, POST, ...)
  163. * @param string $url URL to fetch
  164. * @param array $parameters additionnal parameters to send with the request
  165. * @param string $content_type the content type of the sent data (defaults to application/json)
  166. * @param string|array|object $data request body
  167. *
  168. * @return string|false server response on success, false on error
  169. */
  170. public function query($method, $url, $parameters = array(), $data = NULL, $content_type = NULL)
  171. {
  172. if ($this->curl)
  173. return $this->_curl_query($method, $url, $parameters, $data, $content_type);
  174. else
  175. return $this->_socket_query($method, $url, $parameters, $data, $content_type);
  176. }
  177. /**
  178. * record a file located on the disk as a CouchDB attachment
  179. *
  180. * @param string $url CouchDB URL to store the file to
  181. * @param string $file path to the on-disk file
  182. * @param string $content_type attachment content_type
  183. *
  184. * @return string server response
  185. */
  186. public function storeFile($url, $file, $content_type)
  187. {
  188. if ($this->curl)
  189. return $this->_curl_storeFile($url, $file, $content_type);
  190. else
  191. return $this->_socket_storeFile($url, $file, $content_type);
  192. }
  193. /**
  194. * store some data as a CouchDB attachment
  195. *
  196. * @param string $url CouchDB URL to store the file to
  197. * @param string $data data to send as the attachment content
  198. * @param string $content_type attachment content_type
  199. *
  200. * @return string server response
  201. */
  202. public function storeAsFile($url, $data, $content_type)
  203. {
  204. if ($this->curl)
  205. return $this->_curl_storeAsFile($url, $data, $content_type);
  206. else
  207. return $this->_socket_storeAsFile($url, $data, $content_type);
  208. }
  209. /**
  210. * send a query to the CouchDB server
  211. *
  212. * In a continuous query, the server send headers, and then a JSON object per line.
  213. * On each line received, the $callable callback is fired, with two arguments :
  214. *
  215. * - the JSON object decoded as a PHP object
  216. *
  217. * - a couchClient instance to use to make queries inside the callback
  218. *
  219. * If the callable returns the boolean FALSE , continuous reading stops.
  220. *
  221. * @param callable $callable PHP function name / callable array ( see http://php.net/is_callable )
  222. * @param string $method HTTP method to use (GET, POST, ...)
  223. * @param string $url URL to fetch
  224. * @param array $parameters additionnal parameters to send with the request
  225. * @param string|array|object $data request body
  226. *
  227. * @return string|false server response on success, false on error
  228. *
  229. * @throws Exception|InvalidArgumentException|CouchException|CouchNoResponseException
  230. */
  231. public function continuousQuery($callable, $method, $url, $parameters = array(), $data = null)
  232. {
  233. if (!in_array($method, $this->HTTP_METHODS))
  234. throw new Exception("Bad HTTP method: $method");
  235. if (!is_callable($callable))
  236. throw new InvalidArgumentException("callable argument have to success to is_callable PHP function");
  237. if (is_array($parameters) AND count($parameters))
  238. $url = $url . '?' . http_build_query($parameters);
  239. //Send the request to the socket
  240. $request = $this->_socket_buildRequest($method, $url, $data, null);
  241. if (!$this->_connect())
  242. return FALSE;
  243. fwrite($this->socket, $request);
  244. //Read the headers and check that the response is valid
  245. $response = '';
  246. $headers = false;
  247. while (!feof($this->socket) && !$headers) {
  248. $response.=fgets($this->socket);
  249. if ($response == "HTTP/1.1 100 Continue\r\n\r\n") {
  250. $response = '';
  251. continue;
  252. } //Ignore 'continue' headers, they will be followed by the real header.
  253. elseif (preg_match("/\r\n\r\n$/", $response)) {
  254. $headers = true;
  255. }
  256. }
  257. $headers = explode("\n", trim($response));
  258. $split = explode(" ", trim(reset($headers)));
  259. $code = $split[1];
  260. unset($split);
  261. //If an invalid response is sent, read the rest of the response and throw an appropriate CouchException
  262. if (!in_array($code, array(200, 201))) {
  263. stream_set_blocking($this->socket, false);
  264. $response .= stream_get_contents($this->socket);
  265. fclose($this->socket);
  266. throw CouchException::factory($response, $method, $url, $parameters);
  267. }
  268. //For as long as the socket is open, read lines and pass them to the callback
  269. $c = clone $this;
  270. while ($this->socket && !feof($this->socket)) {
  271. $e = NULL;
  272. $e2 = NULL;
  273. $read = array($this->socket);
  274. if (false === ($num_changed_streams = stream_select($read, $e, $e2, 1))) {
  275. $this->socket = null;
  276. } elseif ($num_changed_streams > 0) {
  277. $line = fgets($this->socket);
  278. if (strlen(trim($line))) {
  279. $break = call_user_func($callable, json_decode($line), $c);
  280. if ($break === FALSE) {
  281. fclose($this->socket);
  282. }
  283. }
  284. }
  285. }
  286. return $code;
  287. }
  288. /**
  289. * send a query to the CouchDB server
  290. *
  291. * @param string $method HTTP method to use (GET, POST, ...)
  292. * @param string $url URL to fetch
  293. * @param array $parameters additionnal parameters to send with the request
  294. * @param string $content_type the content type of the sent data (defaults to application/json)
  295. * @param string|array|object $data request body
  296. *
  297. * @return string|false server response on success, false on error
  298. *
  299. * @throws Exception
  300. */
  301. public function _socket_query($method, $url, $parameters = array(), $data = NULL, $content_type = NULL)
  302. {
  303. if (!in_array($method, $this->HTTP_METHODS))
  304. throw new Exception("Bad HTTP method: $method");
  305. if (is_array($parameters) AND count($parameters))
  306. $url = $url . '?' . http_build_query($parameters);
  307. $request = $this->_socket_buildRequest($method, $url, $data, $content_type);
  308. if (!$this->_connect())
  309. return FALSE;
  310. // echo "DEBUG: Request ------------------ \n$request\n";
  311. $raw_response = $this->_execute($request);
  312. $this->_disconnect();
  313. // echo 'debug',"COUCH : Executed query $method $url";
  314. // echo 'debug',"COUCH : ".$raw_response;
  315. return $raw_response;
  316. }
  317. /**
  318. * returns first lines of request headers
  319. *
  320. * lines :
  321. * <code>
  322. * VERB HTTP/1.0
  323. * Host: my.super.server.com
  324. * Authorization: Basic...
  325. * Accept: application/json,text/html,text/plain,* /*
  326. * </code>
  327. *
  328. * @param string $method HTTP method to use
  329. * @param string $url the request URL
  330. * @return string start of HTTP request
  331. */
  332. protected function _socket_startRequestHeaders($method, $url)
  333. {
  334. if ($this->dsn_part('path'))
  335. $url = $this->dsn_part('path') . $url;
  336. $req = "$method $url HTTP/1.0\r\nHost: " . $this->dsn_part('host') . "\r\n";
  337. if ($this->dsn_part('user') && $this->dsn_part('pass')) {
  338. $req .= 'Authorization: Basic ' . base64_encode($this->dsn_part('user') . ':' .
  339. $this->dsn_part('pass')) . "\r\n";
  340. } elseif ($this->sessioncookie) {
  341. $req .= "Cookie: " . $this->sessioncookie . "\r\n";
  342. }
  343. $req.="Accept: application/json,text/html,text/plain,*/*\r\n";
  344. return $req;
  345. }
  346. /**
  347. * build HTTP request to send to the server
  348. *
  349. * @param string $method HTTP method to use
  350. * @param string $url the request URL
  351. * @param string|object|array $data the request body. If it's an array or an object, $data is json_encode()d
  352. * @param string $content_type the content type of the sent data (defaults to application/json)
  353. * @return string HTTP request
  354. */
  355. protected function _socket_buildRequest($method, $url, $data, $content_type)
  356. {
  357. if (is_object($data) OR is_array($data))
  358. $data = json_encode($data);
  359. $req = $this->_socket_startRequestHeaders($method, $url);
  360. if ($content_type) {
  361. $req .= 'Content-Type: ' . $content_type . "\r\n";
  362. } else {
  363. $req .= 'Content-Type: application/json' . "\r\n";
  364. }
  365. if ($method == 'COPY') {
  366. $req .= 'Destination: ' . $data . "\r\n\r\n";
  367. } elseif ($data) {
  368. $req .= 'Content-Length: ' . strlen($data) . "\r\n\r\n";
  369. $req .= $data . "\r\n";
  370. } else {
  371. $req .= "\r\n";
  372. }
  373. return $req;
  374. }
  375. /**
  376. * record a file located on the disk as a CouchDB attachment
  377. * uses PHP socket API
  378. *
  379. * @param string $url CouchDB URL to store the file to
  380. * @param string $file path to the on-disk file
  381. * @param string $content_type attachment content_type
  382. *
  383. * @return string server response
  384. *
  385. * @throws InvalidArgumentException
  386. */
  387. protected function _socket_storeFile($url, $file, $content_type)
  388. {
  389. if (!strlen($url))
  390. throw new InvalidArgumentException("Attachment URL can't be empty");
  391. if (!strlen($file) OR ! is_file($file) OR ! is_readable($file))
  392. throw new InvalidArgumentException("Attachment file does not exist or is not readable");
  393. if (!strlen($content_type))
  394. throw new InvalidArgumentException("Attachment Content Type can't be empty");
  395. $req = $this->_socket_startRequestHeaders('PUT', $url);
  396. $req .= 'Content-Length: ' . filesize($file) . "\r\n"
  397. . 'Content-Type: ' . $content_type . "\r\n\r\n";
  398. $fstream = fopen($file, 'r');
  399. $this->_connect();
  400. fwrite($this->socket, $req);
  401. stream_copy_to_stream($fstream, $this->socket);
  402. $response = '';
  403. while (!feof($this->socket))
  404. $response .= fgets($this->socket);
  405. $this->_disconnect();
  406. fclose($fstream);
  407. return $response;
  408. }
  409. /**
  410. * store some data as a CouchDB attachment
  411. * uses PHP socket API
  412. *
  413. * @param string $url CouchDB URL to store the file to
  414. * @param string $data data to send as the attachment content
  415. * @param string $content_type attachment content_type
  416. *
  417. * @return string server response
  418. *
  419. * @throws InvalidArgumentException
  420. */
  421. public function _socket_storeAsFile($url, $data, $content_type)
  422. {
  423. if (!strlen($url))
  424. throw new InvalidArgumentException("Attachment URL can't be empty");
  425. if (!strlen($content_type))
  426. throw new InvalidArgumentException("Attachment Content Type can't be empty");
  427. $req = $this->_socket_startRequestHeaders('PUT', $url);
  428. $req .= 'Content-Length: ' . strlen($data) . "\r\n"
  429. . 'Content-Type: ' . $content_type . "\r\n\r\n";
  430. $this->_connect();
  431. fwrite($this->socket, $req);
  432. fwrite($this->socket, $data);
  433. $response = '';
  434. while (!feof($this->socket))
  435. $response .= fgets($this->socket);
  436. $this->_disconnect();
  437. return $response;
  438. }
  439. /**
  440. * open the connection to the CouchDB server
  441. *
  442. * This function can throw an Exception if it fails
  443. *
  444. * @return boolean wheter the connection is successful
  445. *
  446. * @throws Exception
  447. */
  448. protected function _connect()
  449. {
  450. $ssl = $this->dsn_part('scheme') == 'https' ? 'ssl://' : '';
  451. $this->socket = @fsockopen($ssl . $this->dsn_part('host'), $this->dsn_part('port'), $err_num, $err_string);
  452. if (!$this->socket) {
  453. throw new Exception('Could not open connection to ' . $this->dsn_part('host') . ':' . $this->dsn_part('port') . ': ' . $err_string . ' (' . $err_num . ')');
  454. }
  455. return TRUE;
  456. }
  457. /**
  458. * send the HTTP request to the server and read the response
  459. *
  460. * @param string $request HTTP request to send
  461. * @return string $response HTTP response from the CouchDB server
  462. */
  463. protected function _execute($request)
  464. {
  465. fwrite($this->socket, $request);
  466. $response = '';
  467. while (!feof($this->socket))
  468. $response .= fgets($this->socket);
  469. return $response;
  470. }
  471. /**
  472. * closes the connection to the server
  473. *
  474. *
  475. */
  476. protected function _disconnect()
  477. {
  478. @fclose($this->socket);
  479. $this->socket = NULL;
  480. }
  481. /*
  482. * add user-defined options to Curl resource
  483. */
  484. protected function _curl_addCustomOptions($res)
  485. {
  486. if (array_key_exists("curl", $this->options) && is_array($this->options["curl"])) {
  487. curl_setopt_array($res, $this->options["curl"]);
  488. }
  489. }
  490. /**
  491. * build HTTP request to send to the server
  492. * uses PHP cURL API
  493. *
  494. * @param string $method HTTP method to use
  495. * @param string $url the request URL
  496. * @param string|object|array $data the request body. If it's an array or an object, $data is json_encode()d
  497. * @param string $content_type the content type of the sent data (defaults to application/json)
  498. * @return resource CURL request resource
  499. */
  500. protected function _curl_buildRequest($method, $url, $data, $content_type)
  501. {
  502. $http = curl_init($url);
  503. $http_headers = array('Accept: application/json,text/html,text/plain,*/*');
  504. if (is_object($data) OR is_array($data)) {
  505. $data = json_encode($data);
  506. }
  507. if ($content_type) {
  508. $http_headers[] = 'Content-Type: ' . $content_type;
  509. } else {
  510. $http_headers[] = 'Content-Type: application/json';
  511. }
  512. if ($this->sessioncookie) {
  513. $http_headers[] = "Cookie: " . $this->sessioncookie;
  514. }
  515. curl_setopt($http, CURLOPT_CUSTOMREQUEST, $method);
  516. if ($method == 'COPY') {
  517. $http_headers[] = "Destination: $data";
  518. } elseif ($data) {
  519. curl_setopt($http, CURLOPT_POSTFIELDS, $data);
  520. }
  521. $http_headers[] = 'Expect: ';
  522. curl_setopt($http, CURLOPT_HTTPHEADER, $http_headers);
  523. return $http;
  524. }
  525. /**
  526. * send a query to the CouchDB server
  527. * uses PHP cURL API
  528. *
  529. * @param string $method HTTP method to use (GET, POST, ...)
  530. * @param string $url URL to fetch
  531. * @param array $parameters additionnal parameters to send with the request
  532. * @param string|array|object $data request body
  533. * @param string $content_type the content type of the sent data (defaults to application/json)
  534. *
  535. * @return string|false server response on success, false on error
  536. *
  537. * @throws Exception
  538. */
  539. public function _curl_query($method, $url, $parameters = array(), $data = NULL, $content_type = NULL)
  540. {
  541. if (!in_array($method, $this->HTTP_METHODS))
  542. throw new Exception("Bad HTTP method: $method");
  543. $url = $this->dsn . $url;
  544. if (is_array($parameters) AND count($parameters))
  545. $url = $url . '?' . http_build_query($parameters);
  546. $http = $this->_curl_buildRequest($method, $url, $data, $content_type);
  547. $this->_curl_addCustomOptions($http);
  548. curl_setopt($http, CURLOPT_HEADER, true);
  549. curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
  550. if ($this->curlAllowFollowLocation()) {
  551. curl_setopt($http, CURLOPT_FOLLOWLOCATION, true);
  552. }
  553. $response = curl_exec($http);
  554. curl_close($http);
  555. return $response;
  556. }
  557. /**
  558. * record a file located on the disk as a CouchDB attachment
  559. * uses PHP cURL API
  560. *
  561. * @param string $url CouchDB URL to store the file to
  562. * @param string $file path to the on-disk file
  563. * @param string $content_type attachment content_type
  564. *
  565. * @return string server response
  566. *
  567. * @throws InvalidArgumentException
  568. */
  569. public function _curl_storeFile($url, $file, $content_type)
  570. {
  571. if (!strlen($url))
  572. throw new InvalidArgumentException("Attachment URL can't be empty");
  573. if (!strlen($file) OR ! is_file($file) OR ! is_readable($file))
  574. throw new InvalidArgumentException("Attachment file does not exist or is not readable");
  575. if (!strlen($content_type))
  576. throw new InvalidArgumentException("Attachment Content Type can't be empty");
  577. $url = $this->dsn . $url;
  578. $http = curl_init($url);
  579. $http_headers = array(
  580. 'Accept: application/json,text/html,text/plain,*/*',
  581. 'Content-Type: ' . $content_type,
  582. 'Expect: '
  583. );
  584. if ($this->sessioncookie) {
  585. $http_headers[] = "Cookie: " . $this->sessioncookie;
  586. }
  587. curl_setopt($http, CURLOPT_PUT, 1);
  588. curl_setopt($http, CURLOPT_HTTPHEADER, $http_headers);
  589. curl_setopt($http, CURLOPT_UPLOAD, true);
  590. curl_setopt($http, CURLOPT_HEADER, true);
  591. curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
  592. if ($this->curlAllowFollowLocation()) {
  593. curl_setopt($http, CURLOPT_FOLLOWLOCATION, true);
  594. }
  595. $fstream = fopen($file, 'r');
  596. curl_setopt($http, CURLOPT_INFILE, $fstream);
  597. curl_setopt($http, CURLOPT_INFILESIZE, filesize($file));
  598. $this->_curl_addCustomOptions($http);
  599. $response = curl_exec($http);
  600. fclose($fstream);
  601. curl_close($http);
  602. return $response;
  603. }
  604. /**
  605. * store some data as a CouchDB attachment
  606. * uses PHP cURL API
  607. *
  608. * @param string $url CouchDB URL to store the file to
  609. * @param string $data data to send as the attachment content
  610. * @param string $content_type attachment content_type
  611. *
  612. * @return string server response
  613. *
  614. * @throws InvalidArgumentException
  615. */
  616. public function _curl_storeAsFile($url, $data, $content_type)
  617. {
  618. if (!strlen($url))
  619. throw new InvalidArgumentException("Attachment URL can't be empty");
  620. if (!strlen($content_type))
  621. throw new InvalidArgumentException("Attachment Content Type can't be empty");
  622. $url = $this->dsn . $url;
  623. $http = curl_init($url);
  624. $http_headers = array(
  625. 'Accept: application/json,text/html,text/plain,*/*',
  626. 'Content-Type: ' . $content_type,
  627. 'Expect: ',
  628. 'Content-Length: ' . strlen($data)
  629. );
  630. if ($this->sessioncookie) {
  631. $http_headers[] = "Cookie: " . $this->sessioncookie;
  632. }
  633. curl_setopt($http, CURLOPT_CUSTOMREQUEST, 'PUT');
  634. curl_setopt($http, CURLOPT_HTTPHEADER, $http_headers);
  635. curl_setopt($http, CURLOPT_HEADER, true);
  636. curl_setopt($http, CURLOPT_RETURNTRANSFER, true);
  637. if ($this->curlAllowFollowLocation()) {
  638. curl_setopt($http, CURLOPT_FOLLOWLOCATION, true);
  639. }
  640. curl_setopt($http, CURLOPT_POSTFIELDS, $data);
  641. $this->_curl_addCustomOptions($http);
  642. $response = curl_exec($http);
  643. curl_close($http);
  644. return $response;
  645. }
  646. /**
  647. * Determine if the follow location feature needs to be enabled or not.
  648. * @return boolean Returns true to allow the FollowLocation. Otherwise false.
  649. */
  650. protected function curlAllowFollowLocation()
  651. {
  652. return ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off');
  653. }
  654. }