Builder.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. <?php namespace Ixudra\Curl;
  2. use stdClass;
  3. class Builder {
  4. /** @var resource $curlObject cURL request */
  5. protected $curlObject = null;
  6. /** @var array $curlOptions Array of cURL options */
  7. protected $curlOptions = array(
  8. 'RETURNTRANSFER' => true,
  9. 'FAILONERROR' => false,
  10. 'FOLLOWLOCATION' => false,
  11. 'CONNECTTIMEOUT' => '',
  12. 'TIMEOUT' => 30,
  13. 'USERAGENT' => '',
  14. 'URL' => '',
  15. 'POST' => false,
  16. 'HTTPHEADER' => array(),
  17. 'SSL_VERIFYPEER' => false,
  18. );
  19. /** @var array $packageOptions Array with options that are not specific to cURL but are used by the package */
  20. protected $packageOptions = array(
  21. 'data' => array(),
  22. 'files' => array(),
  23. 'asJsonRequest' => false,
  24. 'asJsonResponse' => false,
  25. 'returnAsArray' => false,
  26. 'responseObject' => false,
  27. 'responseArray' => false,
  28. 'enableDebug' => false,
  29. 'xDebugSessionName' => '',
  30. 'containsFile' => false,
  31. 'debugFile' => '',
  32. 'saveFile' => '',
  33. );
  34. /**
  35. * Set the URL to which the request is to be sent
  36. *
  37. * @param $url string The URL to which the request is to be sent
  38. * @return Builder
  39. */
  40. public function to($url)
  41. {
  42. return $this->withCurlOption( 'URL', $url );
  43. }
  44. /**
  45. * Set the request timeout
  46. *
  47. * @param float $timeout The timeout for the request (in seconds, fractions of a second are okay. Default: 30 seconds)
  48. * @return Builder
  49. */
  50. public function withTimeout($timeout = 30.0)
  51. {
  52. return $this->withCurlOption( 'TIMEOUT_MS', ($timeout * 1000) );
  53. }
  54. /**
  55. * Add GET or POST data to the request
  56. *
  57. * @param mixed $data Array of data that is to be sent along with the request
  58. * @return Builder
  59. */
  60. public function withData($data = array())
  61. {
  62. return $this->withPackageOption( 'data', $data );
  63. }
  64. /**
  65. * Add a file to the request
  66. *
  67. * @param string $key Identifier of the file (how it will be referenced by the server in the $_FILES array)
  68. * @param string $path Full path to the file you want to send
  69. * @param string $mimeType Mime type of the file
  70. * @param string $postFileName Name of the file when sent. Defaults to file name
  71. *
  72. * @return Builder
  73. */
  74. public function withFile($key, $path, $mimeType = '', $postFileName = '')
  75. {
  76. $fileData = array(
  77. 'fileName' => $path,
  78. 'mimeType' => $mimeType,
  79. 'postFileName' => $postFileName,
  80. );
  81. $this->packageOptions[ 'files' ][ $key ] = $fileData;
  82. return $this->containsFile();
  83. }
  84. /**
  85. * Allow for redirects in the request
  86. *
  87. * @return Builder
  88. */
  89. public function allowRedirect()
  90. {
  91. return $this->withCurlOption( 'FOLLOWLOCATION', true );
  92. }
  93. /**
  94. * Configure the package to encode and decode the request data
  95. *
  96. * @param boolean $asArray Indicates whether or not the data should be returned as an array. Default: false
  97. * @return Builder
  98. */
  99. public function asJson($asArray = false)
  100. {
  101. return $this->asJsonRequest()
  102. ->asJsonResponse( $asArray );
  103. }
  104. /**
  105. * Configure the package to encode the request data to json before sending it to the server
  106. *
  107. * @return Builder
  108. */
  109. public function asJsonRequest()
  110. {
  111. return $this->withPackageOption( 'asJsonRequest', true );
  112. }
  113. /**
  114. * Configure the package to decode the request data from json to object or associative array
  115. *
  116. * @param boolean $asArray Indicates whether or not the data should be returned as an array. Default: false
  117. * @return Builder
  118. */
  119. public function asJsonResponse($asArray = false)
  120. {
  121. return $this->withPackageOption( 'asJsonResponse', true )
  122. ->withPackageOption( 'returnAsArray', $asArray );
  123. }
  124. // /**
  125. // * Send the request over a secure connection
  126. // *
  127. // * @return Builder
  128. // */
  129. // public function secure()
  130. // {
  131. // return $this;
  132. // }
  133. /**
  134. * Set any specific cURL option
  135. *
  136. * @param string $key The name of the cURL option
  137. * @param string $value The value to which the option is to be set
  138. * @return Builder
  139. */
  140. public function withOption($key, $value)
  141. {
  142. return $this->withCurlOption( $key, $value );
  143. }
  144. /**
  145. * Set Cookie File
  146. *
  147. * @param string $cookieFile File name to read cookies from
  148. * @return Builder
  149. */
  150. public function setCookieFile($cookieFile)
  151. {
  152. return $this->withOption( 'COOKIEFILE', $cookieFile );
  153. }
  154. /**
  155. * Set Cookie Jar
  156. *
  157. * @param string $cookieJar File name to store cookies to
  158. * @return Builder
  159. */
  160. public function setCookieJar($cookieJar)
  161. {
  162. return $this->withOption( 'COOKIEJAR', $cookieJar );
  163. }
  164. /**
  165. * Set any specific cURL option
  166. *
  167. * @param string $key The name of the cURL option
  168. * @param string $value The value to which the option is to be set
  169. * @return Builder
  170. */
  171. protected function withCurlOption($key, $value)
  172. {
  173. $this->curlOptions[ $key ] = $value;
  174. return $this;
  175. }
  176. /**
  177. * Set any specific package option
  178. *
  179. * @param string $key The name of the cURL option
  180. * @param string $value The value to which the option is to be set
  181. * @return Builder
  182. */
  183. protected function withPackageOption($key, $value)
  184. {
  185. $this->packageOptions[ $key ] = $value;
  186. return $this;
  187. }
  188. /**
  189. * Add a HTTP header to the request
  190. *
  191. * @param string $header The HTTP header that is to be added to the request
  192. * @return Builder
  193. */
  194. public function withHeader($header)
  195. {
  196. $this->curlOptions[ 'HTTPHEADER' ][] = $header;
  197. return $this;
  198. }
  199. /**
  200. * Add multiple HTTP header at the same time to the request
  201. *
  202. * @param array $headers Array of HTTP headers that must be added to the request
  203. * @return Builder
  204. */
  205. public function withHeaders(array $headers)
  206. {
  207. $this->curlOptions[ 'HTTPHEADER' ] = array_merge(
  208. $this->curlOptions[ 'HTTPHEADER' ], $headers
  209. );
  210. return $this;
  211. }
  212. /**
  213. * Add a content type HTTP header to the request
  214. *
  215. * @param string $contentType The content type of the file you would like to download
  216. * @return Builder
  217. */
  218. public function withContentType($contentType)
  219. {
  220. return $this->withHeader( 'Content-Type: '. $contentType )
  221. ->withHeader( 'Connection: Keep-Alive' );
  222. }
  223. /**
  224. * Return a full response object with HTTP status and headers instead of only the content
  225. *
  226. * @return Builder
  227. */
  228. public function returnResponseObject()
  229. {
  230. return $this->withPackageOption( 'responseObject', true );
  231. }
  232. /**
  233. * Return a full response array with HTTP status and headers instead of only the content
  234. *
  235. * @return Builder
  236. */
  237. public function returnResponseArray()
  238. {
  239. return $this->withPackageOption( 'responseArray', true );
  240. }
  241. /**
  242. * Enable debug mode for the cURL request
  243. *
  244. * @param string $logFile The full path to the log file you want to use
  245. * @return Builder
  246. */
  247. public function enableDebug($logFile)
  248. {
  249. return $this->withPackageOption( 'enableDebug', true )
  250. ->withPackageOption( 'debugFile', $logFile )
  251. ->withOption( 'VERBOSE', true );
  252. }
  253. /**
  254. * Enable Proxy for the cURL request
  255. *
  256. * @param string $proxy Hostname
  257. * @param string $port Port to be used
  258. * @param string $type Scheme to be used by the proxy
  259. * @param string $username Authentication username
  260. * @param string $password Authentication password
  261. * @return Builder
  262. */
  263. public function withProxy($proxy, $port = '', $type = '', $username = '', $password = '')
  264. {
  265. $this->withOption( 'PROXY', $proxy );
  266. if( !empty($port) ) {
  267. $this->withOption( 'PROXYPORT', $port );
  268. }
  269. if( !empty($type) ) {
  270. $this->withOption( 'PROXYTYPE', $type );
  271. }
  272. if( !empty($username) && !empty($password) ) {
  273. $this->withOption( 'PROXYUSERPWD', $username .':'. $password );
  274. }
  275. return $this;
  276. }
  277. /**
  278. * Enable File sending
  279. *
  280. * @return Builder
  281. */
  282. public function containsFile()
  283. {
  284. return $this->withPackageOption( 'containsFile', true );
  285. }
  286. /**
  287. * Add the XDebug session name to the request to allow for easy debugging
  288. *
  289. * @param string $sessionName
  290. * @return Builder
  291. */
  292. public function enableXDebug($sessionName = 'session_1')
  293. {
  294. $this->packageOptions[ 'xDebugSessionName' ] = $sessionName;
  295. return $this;
  296. }
  297. /**
  298. * Send a GET request to a URL using the specified cURL options
  299. *
  300. * @return mixed
  301. */
  302. public function get()
  303. {
  304. $this->appendDataToURL();
  305. return $this->send();
  306. }
  307. /**
  308. * Send a POST request to a URL using the specified cURL options
  309. *
  310. * @return mixed
  311. */
  312. public function post()
  313. {
  314. $this->setPostParameters();
  315. return $this->send();
  316. }
  317. /**
  318. * Send a download request to a URL using the specified cURL options
  319. *
  320. * @param string $fileName
  321. * @return mixed
  322. */
  323. public function download($fileName)
  324. {
  325. $this->packageOptions[ 'saveFile' ] = $fileName;
  326. return $this->send();
  327. }
  328. /**
  329. * Add POST parameters to the curlOptions array
  330. */
  331. protected function setPostParameters()
  332. {
  333. $this->curlOptions[ 'POST' ] = true;
  334. $parameters = $this->packageOptions[ 'data' ];
  335. if( !empty($this->packageOptions[ 'files' ]) ) {
  336. foreach( $this->packageOptions[ 'files' ] as $key => $file ) {
  337. $parameters[ $key ] = $this->getCurlFileValue( $file[ 'fileName' ], $file[ 'mimeType' ], $file[ 'postFileName'] );
  338. }
  339. }
  340. if( $this->packageOptions[ 'asJsonRequest' ] ) {
  341. $parameters = json_encode($parameters);
  342. }
  343. $this->curlOptions[ 'POSTFIELDS' ] = $parameters;
  344. }
  345. protected function getCurlFileValue($filename, $mimeType, $postFileName)
  346. {
  347. // PHP 5 >= 5.5.0, PHP 7
  348. if( function_exists('curl_file_create') ) {
  349. return curl_file_create($filename, $mimeType, $postFileName);
  350. }
  351. // Use the old style if using an older version of PHP
  352. $value = "@{$filename};filename=" . $postFileName;
  353. if( $mimeType ) {
  354. $value .= ';type=' . $mimeType;
  355. }
  356. return $value;
  357. }
  358. /**
  359. * Send a PUT request to a URL using the specified cURL options
  360. *
  361. * @return mixed
  362. */
  363. public function put()
  364. {
  365. $this->setPostParameters();
  366. return $this->withOption('CUSTOMREQUEST', 'PUT')
  367. ->send();
  368. }
  369. /**
  370. * Send a PATCH request to a URL using the specified cURL options
  371. *
  372. * @return mixed
  373. */
  374. public function patch()
  375. {
  376. $this->setPostParameters();
  377. return $this->withOption('CUSTOMREQUEST', 'PATCH')
  378. ->send();
  379. }
  380. /**
  381. * Send a DELETE request to a URL using the specified cURL options
  382. *
  383. * @return mixed
  384. */
  385. public function delete()
  386. {
  387. $this->appendDataToURL();
  388. return $this->withOption('CUSTOMREQUEST', 'DELETE')
  389. ->send();
  390. }
  391. /**
  392. * Send the request
  393. *
  394. * @return mixed
  395. */
  396. protected function send()
  397. {
  398. // Add JSON header if necessary
  399. if( $this->packageOptions[ 'asJsonRequest' ] ) {
  400. $this->withHeader( 'Content-Type: application/json' );
  401. }
  402. if( $this->packageOptions[ 'enableDebug' ] ) {
  403. $debugFile = fopen( $this->packageOptions[ 'debugFile' ], 'w');
  404. $this->withOption('STDERR', $debugFile);
  405. }
  406. // Create the request with all specified options
  407. $this->curlObject = curl_init();
  408. $options = $this->forgeOptions();
  409. curl_setopt_array( $this->curlObject, $options );
  410. // Send the request
  411. $response = curl_exec( $this->curlObject );
  412. // Capture additional request information if needed
  413. $responseData = array();
  414. if( $this->packageOptions[ 'responseObject' ] || $this->packageOptions[ 'responseArray' ] ) {
  415. $responseData = curl_getinfo( $this->curlObject );
  416. if( curl_errno($this->curlObject) ) {
  417. $responseData[ 'errorMessage' ] = curl_error($this->curlObject);
  418. }
  419. }
  420. curl_close( $this->curlObject );
  421. if( $this->packageOptions[ 'saveFile' ] ) {
  422. // Save to file if a filename was specified
  423. $file = fopen($this->packageOptions[ 'saveFile' ], 'w');
  424. fwrite($file, $response);
  425. fclose($file);
  426. } else if( $this->packageOptions[ 'asJsonResponse' ] ) {
  427. // Decode the request if necessary
  428. $response = json_decode($response, $this->packageOptions[ 'returnAsArray' ]);
  429. }
  430. if( $this->packageOptions[ 'enableDebug' ] ) {
  431. fclose( $debugFile );
  432. }
  433. // Return the result
  434. return $this->returnResponse( $response, $responseData );
  435. }
  436. /**
  437. * @param mixed $content Content of the request
  438. * @param array $responseData Additional response information
  439. * @return mixed
  440. */
  441. protected function returnResponse($content, array $responseData = array())
  442. {
  443. if( !$this->packageOptions[ 'responseObject' ] && !$this->packageOptions[ 'responseArray' ] ) {
  444. return $content;
  445. }
  446. $object = new stdClass();
  447. $object->content = $content;
  448. $object->status = $responseData[ 'http_code' ];
  449. $object->contentType = $responseData[ 'content_type' ];
  450. if( array_key_exists('errorMessage', $responseData) ) {
  451. $object->error = $responseData[ 'errorMessage' ];
  452. }
  453. if( $this->packageOptions[ 'responseObject' ] ) {
  454. return $object;
  455. }
  456. if( $this->packageOptions[ 'responseArray' ] ) {
  457. return (array) $object;
  458. }
  459. return $content;
  460. }
  461. /**
  462. * Convert the curlOptions to an array of usable options for the cURL request
  463. *
  464. * @return array
  465. */
  466. protected function forgeOptions()
  467. {
  468. $results = array();
  469. foreach( $this->curlOptions as $key => $value ) {
  470. $arrayKey = constant( 'CURLOPT_' . $key );
  471. if( !$this->packageOptions[ 'containsFile' ] && $key == 'POSTFIELDS' && is_array( $value ) ) {
  472. $results[ $arrayKey ] = http_build_query( $value, null, '&' );
  473. } else {
  474. $results[ $arrayKey ] = $value;
  475. }
  476. }
  477. if( !empty($this->packageOptions[ 'xDebugSessionName' ]) ) {
  478. $char = strpos($this->curlOptions[ 'URL' ], '?') ? '&' : '?';
  479. $this->curlOptions[ 'URL' ] .= $char . 'XDEBUG_SESSION_START='. $this->packageOptions[ 'xDebugSessionName' ];
  480. }
  481. return $results;
  482. }
  483. /**
  484. * Append set data to the query string for GET and DELETE cURL requests
  485. *
  486. * @return string
  487. */
  488. protected function appendDataToURL()
  489. {
  490. $parameterString = '';
  491. if( is_array($this->packageOptions[ 'data' ]) && count($this->packageOptions[ 'data' ]) != 0 ) {
  492. $parameterString = '?'. http_build_query( $this->packageOptions[ 'data' ], null, '&' );
  493. }
  494. return $this->curlOptions[ 'URL' ] .= $parameterString;
  495. }
  496. }