Builder.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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 File sending
  255. *
  256. * @return Builder
  257. */
  258. public function containsFile()
  259. {
  260. return $this->withPackageOption( 'containsFile', true );
  261. }
  262. /**
  263. * Add the XDebug session name to the request to allow for easy debugging
  264. *
  265. * @param string $sessionName
  266. * @return Builder
  267. */
  268. public function enableXDebug($sessionName = 'session_1')
  269. {
  270. $this->packageOptions[ 'xDebugSessionName' ] = $sessionName;
  271. return $this;
  272. }
  273. /**
  274. * Send a GET request to a URL using the specified cURL options
  275. *
  276. * @return mixed
  277. */
  278. public function get()
  279. {
  280. $this->appendDataToURL();
  281. return $this->send();
  282. }
  283. /**
  284. * Send a POST request to a URL using the specified cURL options
  285. *
  286. * @return mixed
  287. */
  288. public function post()
  289. {
  290. $this->setPostParameters();
  291. return $this->send();
  292. }
  293. /**
  294. * Send a download request to a URL using the specified cURL options
  295. *
  296. * @param string $fileName
  297. * @return mixed
  298. */
  299. public function download($fileName)
  300. {
  301. $this->packageOptions[ 'saveFile' ] = $fileName;
  302. return $this->send();
  303. }
  304. /**
  305. * Add POST parameters to the curlOptions array
  306. */
  307. protected function setPostParameters()
  308. {
  309. $this->curlOptions[ 'POST' ] = true;
  310. $parameters = $this->packageOptions[ 'data' ];
  311. if( !empty($this->packageOptions[ 'files' ]) ) {
  312. foreach( $this->packageOptions[ 'files' ] as $key => $file ) {
  313. $parameters[ $key ] = $this->getCurlFileValue( $file[ 'fileName' ], $file[ 'mimeType' ], $file[ 'postFileName'] );
  314. }
  315. }
  316. if( $this->packageOptions[ 'asJsonRequest' ] ) {
  317. $parameters = json_encode($parameters);
  318. }
  319. $this->curlOptions[ 'POSTFIELDS' ] = $parameters;
  320. }
  321. protected function getCurlFileValue($filename, $mimeType, $postFileName)
  322. {
  323. // PHP 5 >= 5.5.0, PHP 7
  324. if( function_exists('curl_file_create') ) {
  325. return curl_file_create($filename, $mimeType, $postFileName);
  326. }
  327. // Use the old style if using an older version of PHP
  328. $value = "@{$filename};filename=" . $postFileName;
  329. if( $mimeType ) {
  330. $value .= ';type=' . $mimeType;
  331. }
  332. return $value;
  333. }
  334. /**
  335. * Send a PUT request to a URL using the specified cURL options
  336. *
  337. * @return mixed
  338. */
  339. public function put()
  340. {
  341. $this->setPostParameters();
  342. return $this->withOption('CUSTOMREQUEST', 'PUT')
  343. ->send();
  344. }
  345. /**
  346. * Send a PATCH request to a URL using the specified cURL options
  347. *
  348. * @return mixed
  349. */
  350. public function patch()
  351. {
  352. $this->setPostParameters();
  353. return $this->withOption('CUSTOMREQUEST', 'PATCH')
  354. ->send();
  355. }
  356. /**
  357. * Send a DELETE request to a URL using the specified cURL options
  358. *
  359. * @return mixed
  360. */
  361. public function delete()
  362. {
  363. $this->appendDataToURL();
  364. return $this->withOption('CUSTOMREQUEST', 'DELETE')
  365. ->send();
  366. }
  367. /**
  368. * Send the request
  369. *
  370. * @return mixed
  371. */
  372. protected function send()
  373. {
  374. // Add JSON header if necessary
  375. if( $this->packageOptions[ 'asJsonRequest' ] ) {
  376. $this->withHeader( 'Content-Type: application/json' );
  377. }
  378. if( $this->packageOptions[ 'enableDebug' ] ) {
  379. $debugFile = fopen( $this->packageOptions[ 'debugFile' ], 'w');
  380. $this->withOption('STDERR', $debugFile);
  381. }
  382. // Create the request with all specified options
  383. $this->curlObject = curl_init();
  384. $options = $this->forgeOptions();
  385. curl_setopt_array( $this->curlObject, $options );
  386. // Send the request
  387. $response = curl_exec( $this->curlObject );
  388. // Capture additional request information if needed
  389. $responseData = array();
  390. if( $this->packageOptions[ 'responseObject' ] || $this->packageOptions[ 'responseArray' ] ) {
  391. $responseData = curl_getinfo( $this->curlObject );
  392. if( curl_errno($this->curlObject) ) {
  393. $responseData[ 'errorMessage' ] = curl_error($this->curlObject);
  394. }
  395. }
  396. curl_close( $this->curlObject );
  397. if( $this->packageOptions[ 'saveFile' ] ) {
  398. // Save to file if a filename was specified
  399. $file = fopen($this->packageOptions[ 'saveFile' ], 'w');
  400. fwrite($file, $response);
  401. fclose($file);
  402. } else if( $this->packageOptions[ 'asJsonResponse' ] ) {
  403. // Decode the request if necessary
  404. $response = json_decode($response, $this->packageOptions[ 'returnAsArray' ]);
  405. }
  406. if( $this->packageOptions[ 'enableDebug' ] ) {
  407. fclose( $debugFile );
  408. }
  409. // Return the result
  410. return $this->returnResponse( $response, $responseData );
  411. }
  412. /**
  413. * @param mixed $content Content of the request
  414. * @param array $responseData Additional response information
  415. * @return mixed
  416. */
  417. protected function returnResponse($content, array $responseData = array())
  418. {
  419. if( !$this->packageOptions[ 'responseObject' ] && !$this->packageOptions[ 'responseArray' ] ) {
  420. return $content;
  421. }
  422. $object = new stdClass();
  423. $object->content = $content;
  424. $object->status = $responseData[ 'http_code' ];
  425. if( array_key_exists('errorMessage', $responseData) ) {
  426. $object->error = $responseData[ 'errorMessage' ];
  427. }
  428. if( $this->packageOptions[ 'responseObject' ] ) {
  429. return $object;
  430. }
  431. if( $this->packageOptions[ 'responseArray' ] ) {
  432. return (array) $object;
  433. }
  434. return $content;
  435. }
  436. /**
  437. * Convert the curlOptions to an array of usable options for the cURL request
  438. *
  439. * @return array
  440. */
  441. protected function forgeOptions()
  442. {
  443. $results = array();
  444. foreach( $this->curlOptions as $key => $value ) {
  445. $arrayKey = constant( 'CURLOPT_' . $key );
  446. if( !$this->packageOptions[ 'containsFile' ] && $key == 'POSTFIELDS' && is_array( $value ) ) {
  447. $results[ $arrayKey ] = http_build_query( $value, null, '&' );
  448. } else {
  449. $results[ $arrayKey ] = $value;
  450. }
  451. }
  452. if( !empty($this->packageOptions[ 'xDebugSessionName' ]) ) {
  453. $char = strpos($this->curlOptions[ 'URL' ], '?') ? '&' : '?';
  454. $this->curlOptions[ 'URL' ] .= $char . 'XDEBUG_SESSION_START='. $this->packageOptions[ 'xDebugSessionName' ];
  455. }
  456. return $results;
  457. }
  458. /**
  459. * Append set data to the query string for GET and DELETE cURL requests
  460. *
  461. * @return string
  462. */
  463. protected function appendDataToURL()
  464. {
  465. $parameterString = '';
  466. if( is_array($this->packageOptions[ 'data' ]) && count($this->packageOptions[ 'data' ]) != 0 ) {
  467. $parameterString = '?'. http_build_query( $this->packageOptions[ 'data' ], null, '&' );
  468. }
  469. return $this->curlOptions[ 'URL' ] .= $parameterString;
  470. }
  471. }