Response.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\httpclient;
  8. use yii\web\Cookie;
  9. use yii\web\HeaderCollection;
  10. /**
  11. * Response represents HTTP request response.
  12. *
  13. * @property bool $isOk Whether response is OK. This property is read-only.
  14. * @property string $statusCode Status code. This property is read-only.
  15. *
  16. * @author Paul Klimov <klimov.paul@gmail.com>
  17. * @since 2.0
  18. */
  19. class Response extends Message
  20. {
  21. /**
  22. * @inheritdoc
  23. */
  24. public function getData()
  25. {
  26. $data = parent::getData();
  27. if ($data === null) {
  28. $content = $this->getContent();
  29. if (!empty($content)) {
  30. $data = $this->getParser()->parse($this);
  31. $this->setData($data);
  32. }
  33. }
  34. return $data;
  35. }
  36. /**
  37. * @inheritdoc
  38. */
  39. public function getCookies()
  40. {
  41. $cookieCollection = parent::getCookies();
  42. if ($cookieCollection->getCount() === 0 && $this->getHeaders()->has('set-cookie')) {
  43. $cookieStrings = $this->getHeaders()->get('set-cookie', [], false);
  44. foreach ($cookieStrings as $cookieString) {
  45. $cookieCollection->add($this->parseCookie($cookieString));
  46. }
  47. }
  48. return $cookieCollection;
  49. }
  50. /**
  51. * Returns status code.
  52. * @throws Exception on failure.
  53. * @return string status code.
  54. */
  55. public function getStatusCode()
  56. {
  57. $headers = $this->getHeaders();
  58. if ($headers->has('http-code')) {
  59. // take into account possible 'follow location'
  60. $statusCodeHeaders = $headers->get('http-code', null, false);
  61. return empty($statusCodeHeaders) ? null : end($statusCodeHeaders);
  62. }
  63. throw new Exception('Unable to get status code: referred header information is missing.');
  64. }
  65. /**
  66. * Checks if response status code is OK (status code = 20x)
  67. * @return bool whether response is OK.
  68. */
  69. public function getIsOk()
  70. {
  71. return strncmp('20', $this->getStatusCode(), 2) === 0;
  72. }
  73. /**
  74. * Returns default format automatically detected from headers and content.
  75. * @return string|null format name, 'null' - if detection failed.
  76. */
  77. protected function defaultFormat()
  78. {
  79. $format = $this->detectFormatByHeaders($this->getHeaders());
  80. if ($format === null) {
  81. $format = $this->detectFormatByContent($this->getContent());
  82. }
  83. return $format;
  84. }
  85. /**
  86. * Detects format from headers.
  87. * @param HeaderCollection $headers source headers.
  88. * @return null|string format name, 'null' - if detection failed.
  89. */
  90. protected function detectFormatByHeaders(HeaderCollection $headers)
  91. {
  92. $contentType = $headers->get('content-type');
  93. if (!empty($contentType)) {
  94. if (stripos($contentType, 'json') !== false) {
  95. return Client::FORMAT_JSON;
  96. }
  97. if (stripos($contentType, 'urlencoded') !== false) {
  98. return Client::FORMAT_URLENCODED;
  99. }
  100. if (stripos($contentType, 'xml') !== false) {
  101. return Client::FORMAT_XML;
  102. }
  103. }
  104. return null;
  105. }
  106. /**
  107. * Detects response format from raw content.
  108. * @param string $content raw response content.
  109. * @return null|string format name, 'null' - if detection failed.
  110. */
  111. protected function detectFormatByContent($content)
  112. {
  113. if (preg_match('/^\\{.*\\}$/is', $content)) {
  114. return Client::FORMAT_JSON;
  115. }
  116. if (preg_match('/^([^=&])+=[^=&]+(&[^=&]+=[^=&]+)*$/', $content)) {
  117. return Client::FORMAT_URLENCODED;
  118. }
  119. if (preg_match('/^<.*>$/s', $content)) {
  120. return Client::FORMAT_XML;
  121. }
  122. return null;
  123. }
  124. /**
  125. * Parses cookie value string, creating a [[Cookie]] instance.
  126. * @param string $cookieString cookie header string.
  127. * @return Cookie cookie object.
  128. */
  129. private function parseCookie($cookieString)
  130. {
  131. $params = [];
  132. $pairs = explode(';', $cookieString);
  133. foreach ($pairs as $number => $pair) {
  134. $pair = trim($pair);
  135. if (strpos($pair, '=') === false) {
  136. $params[$this->normalizeCookieParamName($pair)] = true;
  137. } else {
  138. list($name, $value) = explode('=', $pair, 2);
  139. if ($number === 0) {
  140. $params['name'] = $name;
  141. $params['value'] = urldecode($value);
  142. } else {
  143. $params[$this->normalizeCookieParamName($name)] = urldecode($value);
  144. }
  145. }
  146. }
  147. $cookie = new Cookie();
  148. foreach ($params as $name => $value) {
  149. if ($cookie->canSetProperty($name)) {
  150. // Cookie string may contain custom unsupported params
  151. $cookie->$name = $value;
  152. }
  153. }
  154. return $cookie;
  155. }
  156. /**
  157. * @param string $rawName raw cookie parameter name.
  158. * @return string name of [[Cookie]] field.
  159. */
  160. private function normalizeCookieParamName($rawName)
  161. {
  162. static $nameMap = [
  163. 'expires' => 'expire',
  164. 'httponly' => 'httpOnly',
  165. 'max-age' => 'maxAge',
  166. ];
  167. $name = strtolower($rawName);
  168. if (isset($nameMap[$name])) {
  169. $name = $nameMap[$name];
  170. }
  171. return $name;
  172. }
  173. /**
  174. * @return ParserInterface message parser instance.
  175. * @throws Exception if unable to detect parser.
  176. */
  177. private function getParser()
  178. {
  179. $format = $this->getFormat();
  180. if ($format === null) {
  181. throw new Exception("Unable to detect format for content parsing. Raw response:\n\n" . $this->toString());
  182. }
  183. return $this->client->getParser($format);
  184. }
  185. }