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

/lib/soundcloud/Soundcloud.php

https://gitlab.com/x33n/SellCloudMusic
PHP | 942 lines | 376 code | 95 blank | 471 comment | 28 complexity | 667e3b40565fd0448e73bf9cbe5e8e73 MD5 | raw file
  1. <?php
  2. require_once 'Exception.php';
  3. require_once 'Version.php';
  4. /**
  5. * SoundCloud API wrapper with support for authentication using OAuth 2
  6. *
  7. * @category Services
  8. * @package Services_Soundcloud
  9. * @author Anton Lindqvist <anton@qvister.se>
  10. * @copyright 2010 Anton Lindqvist <anton@qvister.se>
  11. * @license http://www.opensource.org/licenses/mit-license.php MIT
  12. * @link http://github.com/mptre/php-soundcloud
  13. */
  14. class Services_Soundcloud
  15. {
  16. /**
  17. * Custom cURL option
  18. *
  19. * @var integer
  20. *
  21. * @access public
  22. */
  23. const CURLOPT_OAUTH_TOKEN = 173;
  24. /**
  25. * Access token returned by the service provider after a successful authentication
  26. *
  27. * @var string
  28. *
  29. * @access private
  30. */
  31. private $_accessToken;
  32. /**
  33. * Version of the API to use
  34. *
  35. * @var integer
  36. *
  37. * @access private
  38. * @static
  39. */
  40. private static $_apiVersion = 1;
  41. /**
  42. * Supported audio MIME types
  43. *
  44. * @var array
  45. *
  46. * @access private
  47. * @static
  48. */
  49. private static $_audioMimeTypes = array(
  50. 'aac' => 'video/mp4',
  51. 'aiff' => 'audio/x-aiff',
  52. 'flac' => 'audio/flac',
  53. 'mp3' => 'audio/mpeg',
  54. 'ogg' => 'audio/ogg',
  55. 'wav' => 'audio/x-wav'
  56. );
  57. /**
  58. * OAuth client id
  59. *
  60. * @var string
  61. *
  62. * @access private
  63. */
  64. private $_clientId;
  65. /**
  66. * OAuth client secret
  67. *
  68. * @var string
  69. *
  70. * @access private
  71. */
  72. private $_clientSecret;
  73. /**
  74. * Default cURL options
  75. *
  76. * @var array
  77. *
  78. * @access private
  79. * @static
  80. */
  81. private static $_curlDefaultOptions = array(
  82. CURLOPT_HEADER => true,
  83. CURLOPT_RETURNTRANSFER => true,
  84. CURLOPT_USERAGENT => ''
  85. );
  86. /**
  87. * cURL options
  88. *
  89. * @var array
  90. *
  91. * @access private
  92. */
  93. private $_curlOptions;
  94. /**
  95. * Development mode
  96. *
  97. * @var boolean
  98. *
  99. * @access private
  100. */
  101. private $_development;
  102. /**
  103. * Available API domains
  104. *
  105. * @var array
  106. *
  107. * @access private
  108. * @static
  109. */
  110. private static $_domains = array(
  111. 'development' => 'sandbox-soundcloud.com',
  112. 'production' => 'soundcloud.com'
  113. );
  114. /**
  115. * HTTP response body from the last request
  116. *
  117. * @var string
  118. *
  119. * @access private
  120. */
  121. private $_lastHttpResponseBody;
  122. /**
  123. * HTTP response code from the last request
  124. *
  125. * @var integer
  126. *
  127. * @access private
  128. */
  129. private $_lastHttpResponseCode;
  130. /**
  131. * HTTP response headers from last request
  132. *
  133. * @var array
  134. *
  135. * @access private
  136. */
  137. private $_lastHttpResponseHeaders;
  138. /**
  139. * OAuth paths
  140. *
  141. * @var array
  142. *
  143. * @access private
  144. * @static
  145. */
  146. private static $_paths = array(
  147. 'authorize' => 'connect',
  148. 'access_token' => 'oauth2/token',
  149. );
  150. /**
  151. * OAuth redirect URI
  152. *
  153. * @var string
  154. *
  155. * @access private
  156. */
  157. private $_redirectUri;
  158. /**
  159. * API response format MIME type
  160. *
  161. * @var string
  162. *
  163. * @access private
  164. */
  165. private $_requestFormat;
  166. /**
  167. * Available response formats
  168. *
  169. * @var array
  170. *
  171. * @access private
  172. * @static
  173. */
  174. private static $_responseFormats = array(
  175. '*' => '*/*',
  176. 'json' => 'application/json',
  177. 'xml' => 'application/xml'
  178. );
  179. /**
  180. * HTTP user agent
  181. *
  182. * @var string
  183. *
  184. * @access private
  185. * @static
  186. */
  187. private static $_userAgent = 'PHP-SoundCloud';
  188. /**
  189. * Class constructor
  190. *
  191. * @param string $clientId OAuth client id
  192. * @param string $clientSecret OAuth client secret
  193. * @param string $redirectUri OAuth redirect URI
  194. * @param boolean $development Sandbox mode
  195. *
  196. * @return void
  197. * @throws Services_Soundcloud_Missing_Client_Id_Exception
  198. *
  199. * @access public
  200. */
  201. function __construct($clientId, $clientSecret, $redirectUri = null, $development = false)
  202. {
  203. if (empty($clientId)) {
  204. throw new Services_Soundcloud_Missing_Client_Id_Exception();
  205. }
  206. $this->_clientId = $clientId;
  207. $this->_clientSecret = $clientSecret;
  208. $this->_redirectUri = $redirectUri;
  209. $this->_development = $development;
  210. $this->_responseFormat = self::$_responseFormats['json'];
  211. $this->_curlOptions = self::$_curlDefaultOptions;
  212. $this->_curlOptions[CURLOPT_USERAGENT] .= $this->_getUserAgent();
  213. }
  214. /**
  215. * Get authorization URL
  216. *
  217. * @param array $params Optional query string parameters
  218. *
  219. * @return string
  220. *
  221. * @access public
  222. * @see Soundcloud::_buildUrl()
  223. */
  224. function getAuthorizeUrl($params = array())
  225. {
  226. $defaultParams = array(
  227. 'client_id' => $this->_clientId,
  228. 'redirect_uri' => $this->_redirectUri,
  229. 'response_type' => 'code'
  230. );
  231. $params = array_merge($defaultParams, $params);
  232. return $this->_buildUrl(self::$_paths['authorize'], $params, false);
  233. }
  234. /**
  235. * Get access token URL
  236. *
  237. * @param array $params Optional query string parameters
  238. *
  239. * @return string
  240. *
  241. * @access public
  242. * @see Soundcloud::_buildUrl()
  243. */
  244. function getAccessTokenUrl($params = array())
  245. {
  246. return $this->_buildUrl(self::$_paths['access_token'], $params, false);
  247. }
  248. /**
  249. * Retrieve access token through credentials flow
  250. *
  251. * @param string $username Username
  252. * @param string $password Password
  253. *
  254. * @return mixed
  255. *
  256. * @access public
  257. */
  258. function credentialsFlow($username, $password)
  259. {
  260. $postData = array(
  261. 'client_id' => $this->_clientId,
  262. 'client_secret' => $this->_clientSecret,
  263. 'username' => $username,
  264. 'password' => $password,
  265. 'grant_type' => 'password'
  266. );
  267. $options = array(CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData);
  268. $response = json_decode(
  269. $this->_request($this->getAccessTokenUrl(), $options),
  270. true
  271. );
  272. if (array_key_exists('access_token', $response)) {
  273. $this->_accessToken = $response['access_token'];
  274. return $response;
  275. } else {
  276. return false;
  277. }
  278. }
  279. /**
  280. * Retrieve access token
  281. *
  282. * @param string $code Optional OAuth code returned from the service provider
  283. * @param array $postData Optional post data
  284. * @param array $curlOptions Optional cURL options
  285. *
  286. * @return mixed
  287. *
  288. * @access public
  289. * @see Soundcloud::_getAccessToken()
  290. */
  291. function accessToken($code = null, $postData = array(), $curlOptions = array())
  292. {
  293. $defaultPostData = array(
  294. 'code' => $code,
  295. 'client_id' => $this->_clientId,
  296. 'client_secret' => $this->_clientSecret,
  297. 'redirect_uri' => $this->_redirectUri,
  298. 'grant_type' => 'authorization_code'
  299. );
  300. $postData = array_filter(array_merge($defaultPostData, $postData));
  301. return $this->_getAccessToken($postData, $curlOptions);
  302. }
  303. /**
  304. * Refresh access token
  305. *
  306. * @param string $refreshToken The token to refresh
  307. * @param array $postData Optional post data
  308. * @param array $curlOptions Optional cURL options
  309. *
  310. * @return mixed
  311. * @see Soundcloud::_getAccessToken()
  312. *
  313. * @access public
  314. */
  315. function accessTokenRefresh($refreshToken, $postData = array(), $curlOptions = array())
  316. {
  317. $defaultPostData = array(
  318. 'refresh_token' => $refreshToken,
  319. 'client_id' => $this->_clientId,
  320. 'client_secret' => $this->_clientSecret,
  321. 'redirect_uri' => $this->_redirectUri,
  322. 'grant_type' => 'refresh_token'
  323. );
  324. $postData = array_merge($defaultPostData, $postData);
  325. return $this->_getAccessToken($postData, $curlOptions);
  326. }
  327. /**
  328. * Get access token
  329. *
  330. * @return mixed
  331. *
  332. * @access public
  333. */
  334. function getAccessToken()
  335. {
  336. return $this->_accessToken;
  337. }
  338. /**
  339. * Get API version
  340. *
  341. * @return integer
  342. *
  343. * @access public
  344. */
  345. function getApiVersion()
  346. {
  347. return self::$_apiVersion;
  348. }
  349. /**
  350. * Get the corresponding MIME type for a given file extension
  351. *
  352. * @param string $extension Given extension
  353. *
  354. * @return string
  355. * @throws Services_Soundcloud_Unsupported_Audio_Format_Exception
  356. *
  357. * @access public
  358. */
  359. function getAudioMimeType($extension)
  360. {
  361. if (array_key_exists($extension, self::$_audioMimeTypes)) {
  362. return self::$_audioMimeTypes[$extension];
  363. } else {
  364. throw new Services_Soundcloud_Unsupported_Audio_Format_Exception();
  365. }
  366. }
  367. /**
  368. * Get cURL options
  369. *
  370. * @param string $key Optional options key
  371. *
  372. * @return mixed
  373. *
  374. * @access public
  375. */
  376. function getCurlOptions($key = null)
  377. {
  378. if ($key) {
  379. return (array_key_exists($key, $this->_curlOptions))
  380. ? $this->_curlOptions[$key]
  381. : false;
  382. } else {
  383. return $this->_curlOptions;
  384. }
  385. }
  386. /**
  387. * Get development mode
  388. *
  389. * @return boolean
  390. *
  391. * @access public
  392. */
  393. function getDevelopment()
  394. {
  395. return $this->_development;
  396. }
  397. /**
  398. * Get HTTP response header
  399. *
  400. * @param string $header Name of the header
  401. *
  402. * @return mixed
  403. *
  404. * @access public
  405. */
  406. function getHttpHeader($header)
  407. {
  408. if (is_array($this->_lastHttpResponseHeaders)
  409. && array_key_exists($header, $this->_lastHttpResponseHeaders)
  410. ) {
  411. return $this->_lastHttpResponseHeaders[$header];
  412. } else {
  413. return false;
  414. }
  415. }
  416. /**
  417. * Get redirect URI
  418. *
  419. * @return string
  420. *
  421. * @access public
  422. */
  423. function getRedirectUri()
  424. {
  425. return $this->_redirectUri;
  426. }
  427. /**
  428. * Get response format
  429. *
  430. * @return string
  431. *
  432. * @access public
  433. */
  434. function getResponseFormat()
  435. {
  436. return $this->_responseFormat;
  437. }
  438. /**
  439. * Set access token
  440. *
  441. * @param string $accessToken Access token
  442. *
  443. * @return object
  444. *
  445. * @access public
  446. */
  447. function setAccessToken($accessToken)
  448. {
  449. $this->_accessToken = $accessToken;
  450. return $this;
  451. }
  452. /**
  453. * Set cURL options
  454. *
  455. * The method accepts arguments in two ways.
  456. *
  457. * You could pass two arguments when adding a single option.
  458. * <code>
  459. * $soundcloud->setCurlOptions(CURLOPT_SSL_VERIFYHOST, 0);
  460. * </code>
  461. *
  462. * You could also pass an associative array when adding multiple options.
  463. * <code>
  464. * $soundcloud->setCurlOptions(array(
  465. * CURLOPT_SSL_VERIFYHOST => 0,
  466. * CURLOPT_SSL_VERIFYPEER => 0
  467. * ));
  468. * </code>
  469. *
  470. * @return object
  471. *
  472. * @access public
  473. */
  474. function setCurlOptions()
  475. {
  476. $args = func_get_args();
  477. $options = (is_array($args[0]))
  478. ? $args[0]
  479. : array($args[0] => $args[1]);
  480. foreach ($options as $key => $val) {
  481. $this->_curlOptions[$key] = $val;
  482. }
  483. return $this;
  484. }
  485. /**
  486. * Set redirect URI
  487. *
  488. * @param string $redirectUri Redirect URI
  489. *
  490. * @return object
  491. *
  492. * @access public
  493. */
  494. function setRedirectUri($redirectUri)
  495. {
  496. $this->_redirectUri = $redirectUri;
  497. return $this;
  498. }
  499. /**
  500. * Set response format
  501. *
  502. * @param string $format Response format, could either be XML or JSON
  503. *
  504. * @return object
  505. * @throws Services_Soundcloud_Unsupported_Response_Format_Exception
  506. *
  507. * @access public
  508. */
  509. function setResponseFormat($format)
  510. {
  511. if (array_key_exists($format, self::$_responseFormats)) {
  512. $this->_responseFormat = self::$_responseFormats[$format];
  513. } else {
  514. throw new Services_Soundcloud_Unsupported_Response_Format_Exception();
  515. }
  516. return $this;
  517. }
  518. /**
  519. * Set development mode
  520. *
  521. * @param boolean $development Development mode
  522. *
  523. * @return object
  524. *
  525. * @access public
  526. */
  527. function setDevelopment($development)
  528. {
  529. $this->_development = $development;
  530. return $this;
  531. }
  532. /**
  533. * Send a GET HTTP request
  534. *
  535. * @param string $path Request path
  536. * @param array $params Optional query string parameters
  537. * @param array $curlOptions Optional cURL options
  538. *
  539. * @return mixed
  540. *
  541. * @access public
  542. * @see Soundcloud::_request()
  543. */
  544. function get($path, $params = array(), $curlOptions = array())
  545. {
  546. $url = $this->_buildUrl($path, $params);
  547. return $this->_request($url, $curlOptions);
  548. }
  549. /**
  550. * Send a POST HTTP request
  551. *
  552. * @param string $path Request path
  553. * @param array $postData Optional post data
  554. * @param array $curlOptions Optional cURL options
  555. *
  556. * @return mixed
  557. *
  558. * @access public
  559. * @see Soundcloud::_request()
  560. */
  561. function post($path, $postData = array(), $curlOptions = array())
  562. {
  563. $url = $this->_buildUrl($path);
  564. $options = array(CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData);
  565. $options += $curlOptions;
  566. return $this->_request($url, $options);
  567. }
  568. /**
  569. * Send a PUT HTTP request
  570. *
  571. * @param string $path Request path
  572. * @param array $postData Optional post data
  573. * @param array $curlOptions Optional cURL options
  574. *
  575. * @return mixed
  576. *
  577. * @access public
  578. * @see Soundcloud::_request()
  579. */
  580. function put($path, $postData, $curlOptions = array())
  581. {
  582. $url = $this->_buildUrl($path);
  583. $options = array(
  584. CURLOPT_CUSTOMREQUEST => 'PUT',
  585. CURLOPT_POSTFIELDS => $postData
  586. );
  587. $options += $curlOptions;
  588. return $this->_request($url, $options);
  589. }
  590. /**
  591. * Send a DELETE HTTP request
  592. *
  593. * @param string $path Request path
  594. * @param array $params Optional query string parameters
  595. * @param array $curlOptions Optional cURL options
  596. *
  597. * @return mixed
  598. *
  599. * @access public
  600. * @see Soundcloud::_request()
  601. */
  602. function delete($path, $params = array(), $curlOptions = array())
  603. {
  604. $url = $this->_buildUrl($path, $params);
  605. $options = array(CURLOPT_CUSTOMREQUEST => 'DELETE');
  606. $options += $curlOptions;
  607. return $this->_request($url, $options);
  608. }
  609. /**
  610. * Download track
  611. *
  612. * @param integer $trackId Track id to download
  613. * @param array $params Optional query string parameters
  614. * @param array $curlOptions Optional cURL options
  615. *
  616. * @return mixed
  617. *
  618. * @access public
  619. * @see Soundcloud::_request()
  620. */
  621. function download($trackId, $params = array(), $curlOptions = array())
  622. {
  623. $lastResponseFormat = array_pop(explode('/', $this->getResponseFormat()));
  624. $defaultParams = array('oauth_token' => $this->getAccessToken());
  625. $defaultCurlOptions = array(
  626. CURLOPT_FOLLOWLOCATION => true,
  627. self::CURLOPT_OAUTH_TOKEN => false
  628. );
  629. $url = $this->_buildUrl(
  630. 'tracks/' . $trackId . '/download',
  631. array_merge($defaultParams, $params)
  632. );
  633. $options = $defaultCurlOptions + $curlOptions;
  634. $this->setResponseFormat('*');
  635. $response = $this->_request($url, $options);
  636. // rollback to the previously defined response format.
  637. $this->setResponseFormat($lastResponseFormat);
  638. return $response;
  639. }
  640. /**
  641. * Update a existing playlist
  642. *
  643. * @param integer $playlistId The playlist id
  644. * @param array $trackIds Tracks to add to the playlist
  645. * @param array $optionalPostData Optional playlist fields to update
  646. *
  647. * @return mixed
  648. *
  649. * @access public
  650. * @see Soundcloud::_request()
  651. */
  652. public function updatePlaylist($playlistId, $trackIds, $optionalPostData = null)
  653. {
  654. $url = $this->_buildUrl('playlists/' . $playlistId);
  655. $postData = array_map(function ($track) {
  656. return 'playlist[tracks][][id]=' . $track;
  657. }, $trackIds);
  658. if (is_array($optionalPostData)) {
  659. foreach ($optionalPostData as $key => $val) {
  660. array_push($postData, 'playlist[' . $key . ']=' . $val);
  661. }
  662. }
  663. $postData = implode('&', $postData);
  664. $curlOptions = array(
  665. CURLOPT_CUSTOMREQUEST => 'PUT',
  666. CURLOPT_HTTPHEADER => array('Content-Length' => strlen($postData)),
  667. CURLOPT_POSTFIELDS => $postData
  668. );
  669. return $this->_request($url, $curlOptions);
  670. }
  671. /**
  672. * Construct default HTTP request headers
  673. *
  674. * @param boolean $includeAccessToken Include access token
  675. *
  676. * @return array $headers
  677. *
  678. * @access protected
  679. */
  680. protected function _buildDefaultHeaders($includeAccessToken = true)
  681. {
  682. $headers = array();
  683. if ($this->_responseFormat) {
  684. array_push($headers, 'Accept: ' . $this->_responseFormat);
  685. }
  686. if ($includeAccessToken && $this->_accessToken) {
  687. array_push($headers, 'Authorization: OAuth ' . $this->_accessToken);
  688. }
  689. return $headers;
  690. }
  691. /**
  692. * Construct a URL
  693. *
  694. * @param string $path Relative or absolute URI
  695. * @param array $params Optional query string parameters
  696. * @param boolean $includeVersion Include API version
  697. *
  698. * @return string $url
  699. *
  700. * @access protected
  701. */
  702. protected function _buildUrl($path, $params = array(), $includeVersion = true)
  703. {
  704. if (!$this->_accessToken) {
  705. $params['consumer_key'] = $this->_clientId;
  706. }
  707. if (preg_match('/^https?\:\/\//', $path)) {
  708. $url = $path;
  709. } else {
  710. $url = 'https://';
  711. $url .= (!preg_match('/connect/', $path)) ? 'api.' : '';
  712. $url .= ($this->_development)
  713. ? self::$_domains['development']
  714. : self::$_domains['production'];
  715. $url .= '/';
  716. $url .= ($includeVersion) ? 'v' . self::$_apiVersion . '/' : '';
  717. $url .= $path;
  718. }
  719. $url .= (count($params)) ? '?' . http_build_query($params) : '';
  720. return $url;
  721. }
  722. /**
  723. * Retrieve access token
  724. *
  725. * @param array $postData Post data
  726. * @param array $curlOptions Optional cURL options
  727. *
  728. * @return mixed
  729. *
  730. * @access protected
  731. */
  732. protected function _getAccessToken($postData, $curlOptions = array())
  733. {
  734. $options = array(CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData);
  735. $options += $curlOptions;
  736. $response = json_decode(
  737. $this->_request($this->getAccessTokenUrl(), $options),
  738. true
  739. );
  740. if (array_key_exists('access_token', $response)) {
  741. $this->_accessToken = $response['access_token'];
  742. return $response;
  743. } else {
  744. return false;
  745. }
  746. }
  747. /**
  748. * Get HTTP user agent
  749. *
  750. * @return string
  751. *
  752. * @access protected
  753. */
  754. protected function _getUserAgent()
  755. {
  756. return self::$_userAgent . '/' . new Services_Soundcloud_Version;
  757. }
  758. /**
  759. * Parse HTTP headers
  760. *
  761. * @param string $headers HTTP headers
  762. *
  763. * @return array $parsedHeaders
  764. *
  765. * @access protected
  766. */
  767. protected function _parseHttpHeaders($headers)
  768. {
  769. $headers = explode("\n", trim($headers));
  770. $parsedHeaders = array();
  771. foreach ($headers as $header) {
  772. if (!preg_match('/\:\s/', $header)) {
  773. continue;
  774. }
  775. list($key, $val) = explode(': ', $header, 2);
  776. $key = str_replace('-', '_', strtolower($key));
  777. $val = trim($val);
  778. $parsedHeaders[$key] = $val;
  779. }
  780. return $parsedHeaders;
  781. }
  782. /**
  783. * Validate HTTP response code
  784. *
  785. * @param integer $code HTTP code
  786. *
  787. * @return boolean
  788. *
  789. * @access protected
  790. */
  791. protected function _validResponseCode($code)
  792. {
  793. return (bool)preg_match('/^20[0-9]{1}$/', $code);
  794. }
  795. /**
  796. * Performs the actual HTTP request using cURL
  797. *
  798. * @param string $url Absolute URL to request
  799. * @param array $curlOptions Optional cURL options
  800. *
  801. * @return mixed
  802. * @throws Services_Soundcloud_Invalid_Http_Response_Code_Exception
  803. *
  804. * @access protected
  805. */
  806. protected function _request($url, $curlOptions = array())
  807. {
  808. $ch = curl_init($url);
  809. $options = $this->_curlOptions;
  810. $options += $curlOptions;
  811. if (array_key_exists(self::CURLOPT_OAUTH_TOKEN, $options)) {
  812. $includeAccessToken = $options[self::CURLOPT_OAUTH_TOKEN];
  813. unset($options[self::CURLOPT_OAUTH_TOKEN]);
  814. } else {
  815. $includeAccessToken = true;
  816. }
  817. if (array_key_exists(CURLOPT_HTTPHEADER, $options)) {
  818. $options[CURLOPT_HTTPHEADER] = array_merge(
  819. $this->_buildDefaultHeaders(),
  820. $curlOptions[CURLOPT_HTTPHEADER]
  821. );
  822. } else {
  823. $options[CURLOPT_HTTPHEADER] = $this->_buildDefaultHeaders(
  824. $includeAccessToken
  825. );
  826. }
  827. curl_setopt_array($ch, $options);
  828. $data = curl_exec($ch);
  829. $info = curl_getinfo($ch);
  830. curl_close($ch);
  831. $this->_lastHttpResponseHeaders = $this->_parseHttpHeaders(
  832. substr($data, 0, $info['header_size'])
  833. );
  834. $this->_lastHttpResponseBody = substr($data, $info['header_size']);
  835. $this->_lastHttpResponseCode = $info['http_code'];
  836. if ($this->_validResponseCode($this->_lastHttpResponseCode)) {
  837. return $this->_lastHttpResponseBody;
  838. } else {
  839. throw new Services_Soundcloud_Invalid_Http_Response_Code_Exception(
  840. null,
  841. 0,
  842. $this->_lastHttpResponseBody,
  843. $this->_lastHttpResponseCode
  844. );
  845. }
  846. }
  847. }