CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.2
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • BasicAuthentication
  • DigestAuthentication
  • HttpResponse
  • HttpSocket
  1: <?php
  2: /**
  3:  * HTTP Response from HttpSocket.
  4:  *
  5:  * PHP 5
  6:  *
  7:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8:  * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9:  *
 10:  * Licensed under The MIT License
 11:  * Redistributions of files must retain the above copyright notice.
 12:  *
 13:  * @copyright     Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
 14:  * @link          http://cakephp.org CakePHP(tm) Project
 15:  * @package       Cake.Network.Http
 16:  * @since         CakePHP(tm) v 2.0.0
 17:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 18:  */
 19: 
 20: /**
 21:  * HTTP Response from HttpSocket.
 22:  *
 23:  * @package       Cake.Network.Http
 24:  */
 25: class HttpResponse implements ArrayAccess {
 26: 
 27: /**
 28:  * Body content
 29:  *
 30:  * @var string
 31:  */
 32:     public $body = '';
 33: 
 34: /**
 35:  * Headers
 36:  *
 37:  * @var array
 38:  */
 39:     public $headers = array();
 40: 
 41: /**
 42:  * Cookies
 43:  *
 44:  * @var array
 45:  */
 46:     public $cookies = array();
 47: 
 48: /**
 49:  * HTTP version
 50:  *
 51:  * @var string
 52:  */
 53:     public $httpVersion = 'HTTP/1.1';
 54: 
 55: /**
 56:  * Response code
 57:  *
 58:  * @var integer
 59:  */
 60:     public $code = 0;
 61: 
 62: /**
 63:  * Reason phrase
 64:  *
 65:  * @var string
 66:  */
 67:     public $reasonPhrase = '';
 68: 
 69: /**
 70:  * Pure raw content
 71:  *
 72:  * @var string
 73:  */
 74:     public $raw = '';
 75: 
 76: /**
 77:  * Constructor
 78:  *
 79:  * @param string $message
 80:  */
 81:     public function __construct($message = null) {
 82:         if ($message !== null) {
 83:             $this->parseResponse($message);
 84:         }
 85:     }
 86: 
 87: /**
 88:  * Body content
 89:  *
 90:  * @return string
 91:  */
 92:     public function body() {
 93:         return (string)$this->body;
 94:     }
 95: 
 96: /**
 97:  * Get header in case insensitive
 98:  *
 99:  * @param string $name Header name
100:  * @param array $headers
101:  * @return mixed String if header exists or null
102:  */
103:     public function getHeader($name, $headers = null) {
104:         if (!is_array($headers)) {
105:             $headers =& $this->headers;
106:         }
107:         if (isset($headers[$name])) {
108:             return $headers[$name];
109:         }
110:         foreach ($headers as $key => $value) {
111:             if (strcasecmp($key, $name) == 0) {
112:                 return $value;
113:             }
114:         }
115:         return null;
116:     }
117: 
118: /**
119:  * If return is a valid 2xx (OK or Successful)
120:  *
121:  * @return boolean
122:  */
123:     public function isOk() {
124:         return in_array($this->code, array(200, 201, 202, 203, 204, 205, 206));
125:     }
126: 
127: /**
128:  * If return is a valid 3xx (Redirection)
129:  *
130:  * @return boolean
131:  */
132:     public function isRedirect() {
133:         return in_array($this->code, array(301, 302, 303, 307)) && !is_null($this->getHeader('Location'));
134:     }
135: 
136: /**
137:  * Parses the given message and breaks it down in parts.
138:  *
139:  * @param string $message Message to parse
140:  * @return void
141:  * @throws SocketException
142:  */
143:     public function parseResponse($message) {
144:         if (!is_string($message)) {
145:             throw new SocketException(__d('cake_dev', 'Invalid response.'));
146:         }
147: 
148:         if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) {
149:             throw new SocketException(__d('cake_dev', 'Invalid HTTP response.'));
150:         }
151: 
152:         list(, $statusLine, $header) = $match;
153:         $this->raw = $message;
154:         $this->body = (string)substr($message, strlen($match[0]));
155: 
156:         if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $statusLine, $match)) {
157:             $this->httpVersion = $match[1];
158:             $this->code = $match[2];
159:             $this->reasonPhrase = $match[3];
160:         }
161: 
162:         $this->headers = $this->_parseHeader($header);
163:         $transferEncoding = $this->getHeader('Transfer-Encoding');
164:         $decoded = $this->_decodeBody($this->body, $transferEncoding);
165:         $this->body = $decoded['body'];
166: 
167:         if (!empty($decoded['header'])) {
168:             $this->headers = $this->_parseHeader($this->_buildHeader($this->headers) . $this->_buildHeader($decoded['header']));
169:         }
170: 
171:         if (!empty($this->headers)) {
172:             $this->cookies = $this->parseCookies($this->headers);
173:         }
174:     }
175: 
176: /**
177:  * Generic function to decode a $body with a given $encoding. Returns either an array with the keys
178:  * 'body' and 'header' or false on failure.
179:  *
180:  * @param string $body A string containing the body to decode.
181:  * @param string|boolean $encoding Can be false in case no encoding is being used, or a string representing the encoding.
182:  * @return mixed Array of response headers and body or false.
183:  */
184:     protected function _decodeBody($body, $encoding = 'chunked') {
185:         if (!is_string($body)) {
186:             return false;
187:         }
188:         if (empty($encoding)) {
189:             return array('body' => $body, 'header' => false);
190:         }
191:         $decodeMethod = '_decode' . Inflector::camelize(str_replace('-', '_', $encoding)) . 'Body';
192: 
193:         if (!is_callable(array(&$this, $decodeMethod))) {
194:             return array('body' => $body, 'header' => false);
195:         }
196:         return $this->{$decodeMethod}($body);
197:     }
198: 
199: /**
200:  * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as
201:  * a result.
202:  *
203:  * @param string $body A string containing the chunked body to decode.
204:  * @return mixed Array of response headers and body or false.
205:  * @throws SocketException
206:  */
207:     protected function _decodeChunkedBody($body) {
208:         if (!is_string($body)) {
209:             return false;
210:         }
211: 
212:         $decodedBody = null;
213:         $chunkLength = null;
214: 
215:         while ($chunkLength !== 0) {
216:             if (!preg_match('/^([0-9a-f]+) *(?:;(.+)=(.+))?(?:\r\n|\n)/iU', $body, $match)) {
217:                 throw new SocketException(__d('cake_dev', 'HttpSocket::_decodeChunkedBody - Could not parse malformed chunk.'));
218:             }
219: 
220:             $chunkSize = 0;
221:             $hexLength = 0;
222:             $chunkExtensionValue = '';
223:             if (isset($match[0])) {
224:                 $chunkSize = $match[0];
225:             }
226:             if (isset($match[1])) {
227:                 $hexLength = $match[1];
228:             }
229:             if (isset($match[3])) {
230:                 $chunkExtensionValue = $match[3];
231:             }
232: 
233:             $body = substr($body, strlen($chunkSize));
234:             $chunkLength = hexdec($hexLength);
235:             $chunk = substr($body, 0, $chunkLength);
236:             $decodedBody .= $chunk;
237:             if ($chunkLength !== 0) {
238:                 $body = substr($body, $chunkLength + strlen("\r\n"));
239:             }
240:         }
241: 
242:         $entityHeader = false;
243:         if (!empty($body)) {
244:             $entityHeader = $this->_parseHeader($body);
245:         }
246:         return array('body' => $decodedBody, 'header' => $entityHeader);
247:     }
248: 
249: /**
250:  * Parses an array based header.
251:  *
252:  * @param array $header Header as an indexed array (field => value)
253:  * @return array Parsed header
254:  */
255:     protected function _parseHeader($header) {
256:         if (is_array($header)) {
257:             return $header;
258:         } elseif (!is_string($header)) {
259:             return false;
260:         }
261: 
262:         preg_match_all("/(.+):(.+)(?:(?<![\t ])\r\n|\$)/Uis", $header, $matches, PREG_SET_ORDER);
263: 
264:         $header = array();
265:         foreach ($matches as $match) {
266:             list(, $field, $value) = $match;
267: 
268:             $value = trim($value);
269:             $value = preg_replace("/[\t ]\r\n/", "\r\n", $value);
270: 
271:             $field = $this->_unescapeToken($field);
272: 
273:             if (!isset($header[$field])) {
274:                 $header[$field] = $value;
275:             } else {
276:                 $header[$field] = array_merge((array)$header[$field], (array)$value);
277:             }
278:         }
279:         return $header;
280:     }
281: 
282: /**
283:  * Parses cookies in response headers.
284:  *
285:  * @param array $header Header array containing one ore more 'Set-Cookie' headers.
286:  * @return mixed Either false on no cookies, or an array of cookies received.
287:  */
288:     public function parseCookies($header) {
289:         $cookieHeader = $this->getHeader('Set-Cookie', $header);
290:         if (!$cookieHeader) {
291:             return false;
292:         }
293: 
294:         $cookies = array();
295:         foreach ((array)$cookieHeader as $cookie) {
296:             if (strpos($cookie, '";"') !== false) {
297:                 $cookie = str_replace('";"', "{__cookie_replace__}", $cookie);
298:                 $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie));
299:             } else {
300:                 $parts = preg_split('/\;[ \t]*/', $cookie);
301:             }
302: 
303:             list($name, $value) = explode('=', array_shift($parts), 2);
304:             $cookies[$name] = compact('value');
305: 
306:             foreach ($parts as $part) {
307:                 if (strpos($part, '=') !== false) {
308:                     list($key, $value) = explode('=', $part);
309:                 } else {
310:                     $key = $part;
311:                     $value = true;
312:                 }
313: 
314:                 $key = strtolower($key);
315:                 if (!isset($cookies[$name][$key])) {
316:                     $cookies[$name][$key] = $value;
317:                 }
318:             }
319:         }
320:         return $cookies;
321:     }
322: 
323: /**
324:  * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs)
325:  *
326:  * @param string $token Token to unescape
327:  * @param array $chars
328:  * @return string Unescaped token
329:  */
330:     protected function _unescapeToken($token, $chars = null) {
331:         $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/';
332:         $token = preg_replace($regex, '\\1', $token);
333:         return $token;
334:     }
335: 
336: /**
337:  * Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
338:  *
339:  * @param boolean $hex true to get them as HEX values, false otherwise
340:  * @param array $chars
341:  * @return array Escape chars
342:  */
343:     protected function _tokenEscapeChars($hex = true, $chars = null) {
344:         if (!empty($chars)) {
345:             $escape = $chars;
346:         } else {
347:             $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " ");
348:             for ($i = 0; $i <= 31; $i++) {
349:                 $escape[] = chr($i);
350:             }
351:             $escape[] = chr(127);
352:         }
353: 
354:         if ($hex == false) {
355:             return $escape;
356:         }
357:         foreach ($escape as $key => $char) {
358:             $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT);
359:         }
360:         return $escape;
361:     }
362: 
363: /**
364:  * ArrayAccess - Offset Exists
365:  *
366:  * @param string $offset
367:  * @return boolean
368:  */
369:     public function offsetExists($offset) {
370:         return in_array($offset, array('raw', 'status', 'header', 'body', 'cookies'));
371:     }
372: 
373: /**
374:  * ArrayAccess - Offset Get
375:  *
376:  * @param string $offset
377:  * @return mixed
378:  */
379:     public function offsetGet($offset) {
380:         switch ($offset) {
381:             case 'raw':
382:                 $firstLineLength = strpos($this->raw, "\r\n") + 2;
383:                 if ($this->raw[$firstLineLength] === "\r") {
384:                     $header = null;
385:                 } else {
386:                     $header = substr($this->raw, $firstLineLength, strpos($this->raw, "\r\n\r\n") - $firstLineLength) . "\r\n";
387:                 }
388:                 return array(
389:                     'status-line' => $this->httpVersion . ' ' . $this->code . ' ' . $this->reasonPhrase . "\r\n",
390:                     'header' => $header,
391:                     'body' => $this->body,
392:                     'response' => $this->raw
393:                 );
394:             case 'status':
395:                 return array(
396:                     'http-version' => $this->httpVersion,
397:                     'code' => $this->code,
398:                     'reason-phrase' => $this->reasonPhrase
399:                 );
400:             case 'header':
401:                 return $this->headers;
402:             case 'body':
403:                 return $this->body;
404:             case 'cookies':
405:                 return $this->cookies;
406:         }
407:         return null;
408:     }
409: 
410: /**
411:  * ArrayAccess - Offset Set
412:  *
413:  * @param string $offset
414:  * @param mixed $value
415:  * @return void
416:  */
417:     public function offsetSet($offset, $value) {
418:     }
419: 
420: /**
421:  * ArrayAccess - Offset Unset
422:  *
423:  * @param string $offset
424:  * @return void
425:  */
426:     public function offsetUnset($offset) {
427:     }
428: 
429: /**
430:  * Instance as string
431:  *
432:  * @return string
433:  */
434:     public function __toString() {
435:         return $this->body();
436:     }
437: 
438: }
439: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs