Response.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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-read bool $isOk Whether response is OK. This property is read-only.
  14. * @property-read 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. * @throws Exception
  69. */
  70. public function getIsOk()
  71. {
  72. return strncmp('20', $this->getStatusCode(), 2) === 0;
  73. }
  74. /**
  75. * Returns default format automatically detected from headers and content.
  76. * @return string|null format name, 'null' - if detection failed.
  77. */
  78. protected function defaultFormat()
  79. {
  80. $format = $this->detectFormatByHeaders($this->getHeaders());
  81. if ($format === null) {
  82. $format = $this->detectFormatByContent($this->getContent());
  83. }
  84. return $format;
  85. }
  86. /**
  87. * Detects format from headers.
  88. * @param HeaderCollection $headers source headers.
  89. * @return null|string format name, 'null' - if detection failed.
  90. */
  91. protected function detectFormatByHeaders(HeaderCollection $headers)
  92. {
  93. $contentTypeHeaders = $headers->get('content-type', null, false);
  94. if (!empty($contentTypeHeaders)) {
  95. $contentType = end($contentTypeHeaders);
  96. if (stripos($contentType, 'json') !== false) {
  97. return Client::FORMAT_JSON;
  98. }
  99. if (stripos($contentType, 'urlencoded') !== false) {
  100. return Client::FORMAT_URLENCODED;
  101. }
  102. if (stripos($contentType, 'xml') !== false) {
  103. return Client::FORMAT_XML;
  104. }
  105. }
  106. return null;
  107. }
  108. /**
  109. * Detects response format from raw content.
  110. * @param string $content raw response content.
  111. * @return null|string format name, 'null' - if detection failed.
  112. */
  113. protected function detectFormatByContent($content)
  114. {
  115. if (preg_match('/^(\\{|\\[\\{).*(\\}|\\}\\])$/is', $content)) {
  116. return Client::FORMAT_JSON;
  117. }
  118. if (preg_match('/^([^=&])+=[^=&]+(&[^=&]+=[^=&]+)*$/', $content)) {
  119. return Client::FORMAT_URLENCODED;
  120. }
  121. if (preg_match('/^<\?xml.*>$/s', $content)) {
  122. return Client::FORMAT_XML;
  123. }
  124. return null;
  125. }
  126. /**
  127. * Parses cookie value string, creating a [[Cookie]] instance.
  128. * @param string $cookieString cookie header string.
  129. * @return Cookie cookie object.
  130. */
  131. private function parseCookie($cookieString)
  132. {
  133. $params = [];
  134. $pairs = explode(';', $cookieString);
  135. foreach ($pairs as $number => $pair) {
  136. $pair = trim($pair);
  137. if (strpos($pair, '=') === false) {
  138. $params[$this->normalizeCookieParamName($pair)] = true;
  139. } else {
  140. list($name, $value) = explode('=', $pair, 2);
  141. if ($number === 0) {
  142. $params['name'] = $name;
  143. $params['value'] = urldecode($value);
  144. } else {
  145. $params[$this->normalizeCookieParamName($name)] = urldecode($value);
  146. }
  147. }
  148. }
  149. $cookie = new Cookie();
  150. foreach ($params as $name => $value) {
  151. if ($cookie->canSetProperty($name)) {
  152. // Cookie string may contain custom unsupported params
  153. $cookie->$name = $value;
  154. }
  155. }
  156. return $cookie;
  157. }
  158. /**
  159. * @param string $rawName raw cookie parameter name.
  160. * @return string name of [[Cookie]] field.
  161. */
  162. private function normalizeCookieParamName($rawName)
  163. {
  164. static $nameMap = [
  165. 'expires' => 'expire',
  166. 'httponly' => 'httpOnly',
  167. 'max-age' => 'maxAge',
  168. ];
  169. $name = strtolower($rawName);
  170. if (isset($nameMap[$name])) {
  171. $name = $nameMap[$name];
  172. }
  173. return $name;
  174. }
  175. /**
  176. * @return ParserInterface message parser instance.
  177. * @throws Exception if unable to detect parser.
  178. * @throws \yii\base\InvalidConfigException
  179. */
  180. private function getParser()
  181. {
  182. $format = $this->getFormat();
  183. return $this->client->getParser($format);
  184. }
  185. }