PageRenderTime 169ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

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

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