PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/core/model/modx/rest/modrestcurlclient.class.php

https://github.com/francisreboucas/revolution
PHP | 279 lines | 145 code | 25 blank | 109 comment | 30 complexity | ce66a42a8051c3d672b220a3e2b3504b MD5 | raw file
  1. <?php
  2. /**
  3. * @package modx
  4. * @subpackage rest
  5. */
  6. require_once dirname(__FILE__) . '/modrestclient.class.php';
  7. /**
  8. * Handles REST requests through a cURL-based client
  9. * @package modx
  10. * @subpackage rest
  11. */
  12. class modRestCurlClient extends modRestClient {
  13. /**
  14. * @param modX $modx A reference to the modX object
  15. * @param array $config An array of configuration options
  16. */
  17. function __construct(modX &$modx,array $config = array()) {
  18. parent::__construct($modx, $config);
  19. $this->config = array_merge(array(
  20. ),$this->config);
  21. }
  22. /**
  23. * Extends modRestClient::request to provide cURL specific request handling
  24. *
  25. * @param string $host The host of the REST server.
  26. * @param string $path The path to request to on the REST server.
  27. * @param string $method The HTTP method to use for the request. May be GET,
  28. * PUT or POST.
  29. * @param array $params An array of parameters to send with the request.
  30. * @param array $options An array of options to pass to the request.
  31. * @return modRestResponse The response object.
  32. */
  33. public function request($host,$path,$method = 'GET',array $params = array(),array $options = array()) {
  34. /* start our cURL connection */
  35. $ch = curl_init();
  36. /* setup request */
  37. $this->setUrl($ch,$host,$path,$method,$params,$options);
  38. $this->setAuth($ch,$options);
  39. $this->setProxy($ch,$options);
  40. $this->setOptions($ch,$options);
  41. /* execute request */
  42. $result = trim(curl_exec($ch));
  43. /* make sure to close connection */
  44. curl_close($ch);
  45. return $result;
  46. }
  47. /**
  48. * Configure and set the URL to use, along with any request parameters.
  49. *
  50. * @param resource $ch The cURL connection resource
  51. * @param string $host The host to send the request to
  52. * @param string $path The path of the request
  53. * @param string $method The method of the request (GET/POST)
  54. * @param array $params An array of request parameters to attach to the URL
  55. * @param array $options An array of options when setting the URL
  56. * @return boolean Whether or not the URL was set
  57. * @see modRestClient::request for parameter documentation.
  58. */
  59. public function setUrl($ch,$host,$path,$method = 'GET',array $params = array(),array $options = array()) {
  60. $q = http_build_query($params);
  61. switch ($method) {
  62. case 'GET':
  63. $path .= '?'.$q;
  64. break;
  65. case 'POST':
  66. curl_setopt($ch,CURLOPT_POST,1);
  67. $contentType = $this->modx->getOption('contentType',$options,'xml');
  68. switch ($contentType) {
  69. case 'json':
  70. $json = $this->modx->toJSON($params);
  71. curl_setopt($ch,CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
  72. curl_setopt($ch,CURLOPT_POSTFIELDS,$json);
  73. break;
  74. case 'xml':
  75. curl_setopt($ch,CURLOPT_HTTPHEADER, array('Content-Type: application/xml'));
  76. $xml = modRestArrayToXML::toXML($params,!empty($options['rootNode']) ? $options['rootNode'] : 'request');
  77. curl_setopt($ch,CURLOPT_POSTFIELDS,$xml);
  78. break;
  79. default:
  80. curl_setopt($ch,CURLOPT_POSTFIELDS,$params);
  81. break;
  82. }
  83. break;
  84. }
  85. /* prevent invalid xhtml ampersands in request path */
  86. $url = str_replace('&amp;', '&', $host.$path);
  87. return curl_setopt($ch, CURLOPT_URL,$url);
  88. }
  89. /**
  90. * Set up cURL-specific options
  91. *
  92. * @param resource $ch The cURL connection resource
  93. * @param array $options An array of options
  94. */
  95. public function setOptions($ch,array $options = array()) {
  96. /* always return us the result */
  97. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  98. /* we dont want header gruft */
  99. curl_setopt($ch, CURLOPT_HEADER, 0);
  100. /* default timeout to 30 seconds */
  101. curl_setopt($ch, CURLOPT_TIMEOUT,$this->config[modRestClient::OPT_TIMEOUT]);
  102. /* disable verifypeer since it's not helpful on most environments */
  103. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  104. /* send a useragent to allow proper responses */
  105. curl_setopt($ch, CURLOPT_USERAGENT,$this->config[modRestCurlClient::OPT_USERAGENT]);
  106. /* can only use follow location if safe_mode and open_basedir are off */
  107. $safeMode = ini_get('safe_mode');
  108. $openBasedir = ini_get('open_basedir');
  109. if (empty($safeMode) && empty($openBasedir)) {
  110. curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  111. }
  112. }
  113. /**
  114. * Set up authentication configuration , if specified, to be used with REST request.
  115. *
  116. * @param resource $ch The cURL connection resource.
  117. * @param array $options An array of options
  118. * @return boolean True if authentication was used.
  119. */
  120. public function setAuth($ch,array $options = array()) {
  121. $auth = false;
  122. if (!empty($options[modRestClient::OPT_USERPWD])) {
  123. $options[modRestClient::OPT_AUTHTYPE] = $this->modx->getOption(modRestClient::OPT_AUTHTYPE,$options,'BASIC');
  124. switch ($options[modRestClient::OPT_AUTHTYPE]) {
  125. case 'ANY': curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); break;
  126. case 'ANYSAFE': curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE); break;
  127. case 'DIGEST': curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); break;
  128. case 'GSSNEGOTIATE': curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_GSSNEGOTIATE); break;
  129. case 'NTLM': curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM); break;
  130. default: case 'BASIC': curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); break;
  131. }
  132. $auth = curl_setopt($ch, CURLOPT_USERPWD, !empty($options[modRestClient::OPT_USERPWD]) ? $options[modRestClient::OPT_USERPWD] : 'username:password');
  133. }
  134. return $auth;
  135. }
  136. /**
  137. * Set up proxy configuration , if specified, to be used with REST request.
  138. *
  139. * @param resource $ch The cURL connection resource.
  140. * @param array $options An array of options
  141. * @return boolean True if the proxy was setup.
  142. */
  143. public function setProxy($ch,array $options = array()) {
  144. $proxyEnabled = false;
  145. /* if proxy is set, attempt to use it */
  146. $proxyHost = $this->modx->getOption('proxy_host',null,'');
  147. if (!empty($proxyHost)) {
  148. $proxyEnabled = curl_setopt($ch, CURLOPT_PROXY,$proxyHost);
  149. $proxyPort = $this->modx->getOption('proxy_port',null,'');
  150. if (!empty($proxyPort)) {
  151. curl_setopt($ch, CURLOPT_PROXYPORT,$proxyPort);
  152. }
  153. $proxyUserpwd = $this->modx->getOption('proxy_username',null,'');
  154. if (!empty($proxyUserpwd)) {
  155. $proxyAuthType = $this->modx->getOption('proxy_auth_type',null,'BASIC');
  156. $proxyAuthType = $proxyAuthType == 'NTLM' ? CURLAUTH_NTLM : CURLAUTH_BASIC;
  157. curl_setopt($ch, CURLOPT_PROXYAUTH,$proxyAuthType);
  158. $proxyPassword = $this->modx->getOption('proxy_password',null,'');
  159. if (!empty($proxyPassword)) $proxyUserpwd .= ':'.$proxyPassword;
  160. curl_setopt($ch, CURLOPT_PROXYUSERPWD,$proxyUserpwd);
  161. }
  162. }
  163. return $proxyEnabled;
  164. }
  165. }
  166. if (!class_exists('modRestArrayToXML')) {
  167. /**
  168. * Utility class for array-to-XML transformations.
  169. *
  170. * @package modx
  171. * @subpackage rest
  172. */
  173. class modRestArrayToXML {
  174. /**
  175. * The main function for converting to an XML document.
  176. * Pass in a multi dimensional array and this recrusively loops through and builds up an XML document.
  177. *
  178. * @param array $data
  179. * @param string $rootNodeName - what you want the root node to be - defaultsto data.
  180. * @param SimpleXMLElement $xml - should only be used recursively
  181. * @return string XML
  182. */
  183. public static function toXML( $data, $rootNodeName = 'ResultSet', &$xml=null ) {
  184. // turn off compatibility mode as simple xml throws a wobbly if you don't.
  185. if ( ini_get('zend.ze1_compatibility_mode') == 1 ) ini_set ( 'zend.ze1_compatibility_mode', 0 );
  186. if ( is_null( $xml ) ) $xml = simplexml_load_string('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><'.$rootNodeName.'></'.$rootNodeName.'>');
  187. // loop through the data passed in.
  188. foreach( $data as $key => $value ) {
  189. // no numeric keys in our xml please!
  190. if ( is_numeric( $key ) ) {
  191. $numeric = 1;
  192. $key = $rootNodeName;
  193. }
  194. // delete any char not allowed in XML element names
  195. $key = preg_replace('/[^a-z0-9\-\_\.\:]/i', '', $key);
  196. // if there is another array found recrusively call this function
  197. if ( is_array( $value ) ) {
  198. $node = modRestArrayToXML::isAssoc( $value ) || $numeric ? $xml->addChild( $key ) : $xml;
  199. // recrusive call.
  200. if ( $numeric ) $key = 'anon';
  201. modRestArrayToXML::toXml( $value, $key, $node );
  202. } else {
  203. // add single node.
  204. $value = htmlentities( $value );
  205. $xml->addChild( $key, $value );
  206. }
  207. }
  208. // pass back as XML
  209. //return $xml->asXML();
  210. // if you want the XML to be formatted, use the below instead to return the XML
  211. $doc = new DOMDocument('1.0');
  212. $doc->preserveWhiteSpace = false;
  213. $doc->loadXML( $xml->asXML() );
  214. $doc->formatOutput = true;
  215. return $doc->saveXML();
  216. }
  217. /**
  218. * Convert an XML document to a multi dimensional array
  219. * Pass in an XML document (or SimpleXMLElement object) and this recrusively loops through and builds a representative array
  220. *
  221. * @param string $xml - XML document - can optionally be a SimpleXMLElement object
  222. * @return array ARRAY
  223. */
  224. public static function toArray( $xml ) {
  225. if ( is_string( $xml ) ) $xml = new SimpleXMLElement( $xml );
  226. $children = $xml->children();
  227. if ( !$children ) return (string) $xml;
  228. $arr = array();
  229. foreach ( $children as $key => $node ) {
  230. $node = modRestArrayToXML::toArray( $node );
  231. // support for 'anon' non-associative arrays
  232. if ( $key == 'anon' ) $key = count( $arr );
  233. // if the node is already set, put it into an array
  234. if ( isset( $arr[$key] ) ) {
  235. if ( !is_array( $arr[$key] ) || $arr[$key][0] == null ) $arr[$key] = array( $arr[$key] );
  236. $arr[$key][] = $node;
  237. } else {
  238. $arr[$key] = $node;
  239. }
  240. }
  241. return $arr;
  242. }
  243. /**
  244. * Determine if a variable is an associative array
  245. *
  246. * @static
  247. * @param mixed $array The variable to check
  248. * @return boolean True if is an array
  249. */
  250. public static function isAssoc( $array ) {
  251. return (is_array($array) && 0 !== count(array_diff_key($array, array_keys(array_keys($array)))));
  252. }
  253. }
  254. }