Rest.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. <?php
  2. namespace Dotdigitalgroup\Email\Model\Apiconnector;
  3. /**
  4. * Rest class to make cURL requests.
  5. */
  6. class Rest
  7. {
  8. /**
  9. * @var string|null
  10. */
  11. private $url;
  12. /**
  13. * @var string
  14. */
  15. private $verb;
  16. /**
  17. * @var string|null
  18. */
  19. private $requestBody;
  20. /**
  21. * @var int
  22. */
  23. private $requestLength;
  24. /**
  25. * @var string
  26. */
  27. private $apiUsername;
  28. /**
  29. * @var string
  30. */
  31. private $apiPassword;
  32. /**
  33. * @var string
  34. */
  35. private $acceptType;
  36. /**
  37. * @var mixed
  38. */
  39. private $responseBody;
  40. /**
  41. * @var mixed
  42. */
  43. private $responseInfo;
  44. /**
  45. * @var string
  46. */
  47. private $curlError;
  48. /**
  49. * @var \Dotdigitalgroup\Email\Helper\Data
  50. */
  51. private $helper;
  52. /**
  53. * @var bool
  54. */
  55. protected $isNotJson = false;
  56. /**
  57. * Rest constructor.
  58. * @param \Dotdigitalgroup\Email\Helper\Data $data
  59. * @param int $website
  60. *
  61. * @return null
  62. */
  63. public function __construct(
  64. \Dotdigitalgroup\Email\Helper\Data $data,
  65. $website = 0
  66. ) {
  67. $this->helper = $data;
  68. $this->url = null;
  69. $this->verb = 'GET';
  70. $this->requestBody = null;
  71. $this->requestLength = 0;
  72. $this->apiUsername = (string)$this->helper->getApiUsername($website);
  73. $this->apiPassword = (string)$this->helper->getApiPassword($website);
  74. $this->acceptType = 'application/json';
  75. $this->responseBody = null;
  76. $this->responseInfo = null;
  77. if ($this->requestBody !== null) {
  78. $this->buildPostBody();
  79. }
  80. }
  81. /**
  82. * @param mixed $json
  83. *
  84. * @return string
  85. *
  86. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  87. */
  88. private function prettyPrint($json)
  89. {
  90. $result = '';
  91. $level = 0;
  92. $prevChar = '';
  93. $inQuotes = false;
  94. $endsLineLevel = null;
  95. $jsonLength = strlen($json);
  96. for ($i = 0; $i < $jsonLength; ++$i) {
  97. $char = $json[$i];
  98. $newLIneLevel = null;
  99. $post = '';
  100. if ($endsLineLevel !== null) {
  101. $newLIneLevel = $endsLineLevel;
  102. $endsLineLevel = null;
  103. }
  104. if ($char === '"' && $prevChar != '\\') {
  105. $inQuotes = !$inQuotes;
  106. } elseif (!$inQuotes) {
  107. switch ($char) {
  108. case '}':
  109. case ']':
  110. $level--;
  111. $endsLineLevel = null;
  112. $newLIneLevel = $level;
  113. break;
  114. case '{':
  115. case '[':
  116. $level++;
  117. break;
  118. case ',':
  119. $endsLineLevel = $level;
  120. break;
  121. case ':':
  122. $post = ' ';
  123. break;
  124. case ' ':
  125. case "\t":
  126. case "\n":
  127. case "\r":
  128. $char = '';
  129. $endsLineLevel = $newLIneLevel;
  130. $newLIneLevel = null;
  131. break;
  132. }
  133. }
  134. if ($newLIneLevel !== null) {
  135. $result .= "\n" . str_repeat("\t", $newLIneLevel);
  136. }
  137. $result .= $char . $post;
  138. $prevChar = $char;
  139. }
  140. return $result;
  141. }
  142. /**
  143. * Returns the object as JSON.
  144. *
  145. * @param bool $pretty
  146. *
  147. * @return string
  148. */
  149. public function toJSON($pretty = false)
  150. {
  151. if (!$pretty) {
  152. return json_encode($this->expose());
  153. } else {
  154. return $this->prettyPrint(json_encode($this->expose()));
  155. }
  156. }
  157. /**
  158. * Exposes the class as an array of objects.
  159. *
  160. * @return array
  161. */
  162. public function expose()
  163. {
  164. return get_object_vars($this);
  165. }
  166. /**
  167. * Reset the client.
  168. *
  169. * @return $this
  170. */
  171. public function flush()
  172. {
  173. $this->apiUsername = '';
  174. $this->apiPassword = '';
  175. $this->requestBody = null;
  176. $this->requestLength = 0;
  177. $this->verb = 'GET';
  178. $this->responseBody = null;
  179. $this->responseInfo = null;
  180. return $this;
  181. }
  182. /**
  183. * @throws \Exception
  184. *
  185. * @return mixed
  186. */
  187. public function execute()
  188. {
  189. $ch = curl_init();
  190. $this->setAuth($ch);
  191. try {
  192. switch (strtoupper($this->verb)) {
  193. case 'GET':
  194. $this->executeGet($ch);
  195. break;
  196. case 'POST':
  197. $this->executePost($ch);
  198. break;
  199. case 'PUT':
  200. $this->executePut($ch);
  201. break;
  202. case 'DELETE':
  203. $this->executeDelete($ch);
  204. break;
  205. default:
  206. throw new \InvalidArgumentException(
  207. 'Current verb (' . $this->verb
  208. . ') is an invalid REST verb.'
  209. );
  210. }
  211. } catch (\InvalidArgumentException $e) {
  212. curl_close($ch);
  213. throw $e;
  214. } catch (\Exception $e) {
  215. curl_close($ch);
  216. throw $e;
  217. }
  218. /*
  219. * check and debug api request total time
  220. */
  221. $this->processDebugApi();
  222. return $this->responseBody;
  223. }
  224. /**
  225. * @return void
  226. */
  227. private function processDebugApi()
  228. {
  229. if ($this->helper->isDebugEnabled()) {
  230. $info = $this->getResponseInfo();
  231. //the response info data is set
  232. if (isset($info['url']) && isset($info['total_time'])) {
  233. $url = $info['url'];
  234. $time = $info['total_time'];
  235. $totalTime = sprintf(' time : %g sec', $time);
  236. $check = $this->helper->getApiResponseTimeLimit();
  237. $limit = ($check) ? $check : '2';
  238. $message = $this->verb . ', ' . $url . $totalTime;
  239. //check for slow queries
  240. if ($time > $limit) {
  241. //log the slow queries
  242. $this->helper->log($message);
  243. }
  244. }
  245. }
  246. }
  247. /**
  248. * Post data.
  249. *
  250. * @param null $data
  251. *
  252. * @return $this
  253. */
  254. public function buildPostBody($data = null)
  255. {
  256. $this->requestBody = json_encode($data);
  257. return $this;
  258. }
  259. /**
  260. * Execute curl get request.
  261. *
  262. * @param mixed $ch
  263. *
  264. * @return null
  265. */
  266. private function executeGet($ch)
  267. {
  268. $this->doExecute($ch);
  269. }
  270. /**
  271. * Execute post request.
  272. *
  273. * @param mixed $ch
  274. *
  275. * @return null
  276. */
  277. private function executePost($ch)
  278. {
  279. if (!is_string($this->requestBody)) {
  280. $this->buildPostBody();
  281. }
  282. curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);
  283. curl_setopt($ch, CURLOPT_POST, true);
  284. $this->doExecute($ch);
  285. }
  286. /**
  287. * Post from the file.
  288. *
  289. * @param mixed $filename
  290. *
  291. * @return null
  292. */
  293. public function buildPostBodyFromFile($filename)
  294. {
  295. $this->requestBody = [
  296. 'file' => '@' . $filename,
  297. ];
  298. }
  299. /**
  300. * Execute put.
  301. *
  302. * @param mixed $ch
  303. *
  304. * @return null
  305. */
  306. private function executePut($ch)
  307. {
  308. if (!is_string($this->requestBody)) {
  309. $this->buildPostBody();
  310. }
  311. $this->requestLength = strlen($this->requestBody);
  312. $fh = fopen('php://memory', 'rw');
  313. fwrite($fh, $this->requestBody);
  314. rewind($fh);
  315. curl_setopt($ch, CURLOPT_INFILE, $fh);
  316. curl_setopt($ch, CURLOPT_INFILESIZE, $this->requestLength);
  317. curl_setopt($ch, CURLOPT_PUT, true);
  318. $this->doExecute($ch);
  319. fclose($fh);
  320. }
  321. /**
  322. * Ececute delete.
  323. *
  324. * @param mixed $ch
  325. *
  326. * @return null
  327. */
  328. private function executeDelete($ch)
  329. {
  330. curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
  331. $this->doExecute($ch);
  332. }
  333. /**
  334. * Execute request.
  335. *
  336. * @param mixed $ch
  337. *
  338. * @return null
  339. */
  340. private function doExecute(&$ch)
  341. {
  342. $this->setCurlOpts($ch);
  343. if ($this->isNotJson) {
  344. $this->responseBody = curl_exec($ch);
  345. } else {
  346. $this->responseBody = json_decode(curl_exec($ch));
  347. }
  348. $this->responseInfo = curl_getinfo($ch);
  349. //if curl error found
  350. if (curl_errno($ch)) {
  351. //save the error
  352. $this->curlError = curl_error($ch);
  353. }
  354. curl_close($ch);
  355. }
  356. /**
  357. * Curl options.
  358. *
  359. * @param mixed $ch
  360. *
  361. * @return null
  362. */
  363. private function setCurlOpts(&$ch)
  364. {
  365. curl_setopt($ch, CURLOPT_TIMEOUT, 60);
  366. curl_setopt($ch, CURLOPT_URL, $this->url);
  367. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  368. curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
  369. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  370. curl_setopt(
  371. $ch,
  372. CURLOPT_HTTPHEADER,
  373. [
  374. 'Accept: ' . $this->acceptType,
  375. 'Content-Type: application/json',
  376. ]
  377. );
  378. }
  379. /**
  380. * Basic auth.
  381. *
  382. * @param mixed $ch
  383. *
  384. * @return null
  385. */
  386. private function setAuth(&$ch)
  387. {
  388. if ($this->apiUsername !== null && $this->apiPassword !== null) {
  389. curl_setopt($ch, CURLAUTH_BASIC, CURLAUTH_DIGEST);
  390. curl_setopt(
  391. $ch,
  392. CURLOPT_USERPWD,
  393. $this->apiUsername . ':' . $this->apiPassword
  394. );
  395. }
  396. }
  397. /**
  398. * Get accept type.
  399. *
  400. * @return string
  401. */
  402. public function getAcceptType()
  403. {
  404. return $this->acceptType;
  405. }
  406. /**
  407. * Set accept type.
  408. *
  409. * @param mixed $acceptType
  410. *
  411. * @return null
  412. */
  413. public function setAcceptType($acceptType)
  414. {
  415. $this->acceptType = $acceptType;
  416. }
  417. /**
  418. * Get api username.
  419. *
  420. * @return string
  421. */
  422. public function getApiUsername()
  423. {
  424. return $this->apiUsername;
  425. }
  426. /**
  427. * Set api username.
  428. *
  429. * @param mixed $apiUsername
  430. *
  431. * @return $this
  432. */
  433. public function setApiUsername($apiUsername)
  434. {
  435. $this->apiUsername = trim($apiUsername);
  436. return $this;
  437. }
  438. /**
  439. * Get api password.
  440. *
  441. * @return string
  442. */
  443. public function getApiPassword()
  444. {
  445. return $this->apiPassword;
  446. }
  447. /**
  448. * Set api password.
  449. *
  450. * @param mixed $apiPassword
  451. *
  452. * @return $this
  453. */
  454. public function setApiPassword($apiPassword)
  455. {
  456. $this->apiPassword = trim($apiPassword);
  457. return $this;
  458. }
  459. /**
  460. * Get response body.
  461. *
  462. * @return string/object
  463. */
  464. public function getResponseBody()
  465. {
  466. return $this->responseBody;
  467. }
  468. /**
  469. * Get response info.
  470. *
  471. * @return mixed
  472. */
  473. private function getResponseInfo()
  474. {
  475. return $this->responseInfo;
  476. }
  477. /**
  478. * Get url.
  479. *
  480. * @return string
  481. */
  482. public function getUrl()
  483. {
  484. return $this->url;
  485. }
  486. /**
  487. * Set url.
  488. *
  489. * @param mixed $url
  490. *
  491. * @return $this
  492. */
  493. public function setUrl($url)
  494. {
  495. $this->url = $url;
  496. return $this;
  497. }
  498. /**
  499. * get the verb.
  500. *
  501. * @return string
  502. */
  503. public function getVerb()
  504. {
  505. return $this->verb;
  506. }
  507. /**
  508. * Set the verb.
  509. *
  510. * @param mixed $verb
  511. *
  512. * @return $this
  513. */
  514. public function setVerb($verb)
  515. {
  516. $this->verb = $verb;
  517. return $this;
  518. }
  519. /**
  520. * @return mixed
  521. */
  522. public function getCurlError()
  523. {
  524. //if curl error
  525. if (!empty($this->curlError)) {
  526. //log curl error
  527. $message = 'CURL ERROR ' . $this->curlError;
  528. $this->helper->log($message);
  529. return $this->curlError;
  530. }
  531. return false;
  532. }
  533. }