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.1 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.1
      • 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
    • Network
      • Email
      • Http
    • Routing
      • 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 200 (OK)
120:  *
121:  * @return boolean
122:  */
123:     public function isOk() {
124:         return $this->code == 200;
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 mixed $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:             $chunkExtensionName = '';
223:             $chunkExtensionValue = '';
224:             if (isset($match[0])) {
225:                 $chunkSize = $match[0];
226:             }
227:             if (isset($match[1])) {
228:                 $hexLength = $match[1];
229:             }
230:             if (isset($match[2])) {
231:                 $chunkExtensionName = $match[2];
232:             }
233:             if (isset($match[3])) {
234:                 $chunkExtensionValue = $match[3];
235:             }
236: 
237:             $body = substr($body, strlen($chunkSize));
238:             $chunkLength = hexdec($hexLength);
239:             $chunk = substr($body, 0, $chunkLength);
240:             if (!empty($chunkExtensionName)) {
241:                  // @todo See if there are popular chunk extensions we should implement
242:             }
243:             $decodedBody .= $chunk;
244:             if ($chunkLength !== 0) {
245:                 $body = substr($body, $chunkLength + strlen("\r\n"));
246:             }
247:         }
248: 
249:         $entityHeader = false;
250:         if (!empty($body)) {
251:             $entityHeader = $this->_parseHeader($body);
252:         }
253:         return array('body' => $decodedBody, 'header' => $entityHeader);
254:     }
255: 
256: /**
257:  * Parses an array based header.
258:  *
259:  * @param array $header Header as an indexed array (field => value)
260:  * @return array Parsed header
261:  */
262:     protected function _parseHeader($header) {
263:         if (is_array($header)) {
264:             return $header;
265:         } elseif (!is_string($header)) {
266:             return false;
267:         }
268: 
269:         preg_match_all("/(.+):(.+)(?:(?<![\t ])\r\n|\$)/Uis", $header, $matches, PREG_SET_ORDER);
270: 
271:         $header = array();
272:         foreach ($matches as $match) {
273:             list(, $field, $value) = $match;
274: 
275:             $value = trim($value);
276:             $value = preg_replace("/[\t ]\r\n/", "\r\n", $value);
277: 
278:             $field = $this->_unescapeToken($field);
279: 
280:             if (!isset($header[$field])) {
281:                 $header[$field] = $value;
282:             } else {
283:                 $header[$field] = array_merge((array)$header[$field], (array)$value);
284:             }
285:         }
286:         return $header;
287:     }
288: 
289: /**
290:  * Parses cookies in response headers.
291:  *
292:  * @param array $header Header array containing one ore more 'Set-Cookie' headers.
293:  * @return mixed Either false on no cookies, or an array of cookies received.
294:  * @todo Make this 100% RFC 2965 confirm
295:  */
296:     public function parseCookies($header) {
297:         $cookieHeader = $this->getHeader('Set-Cookie', $header);
298:         if (!$cookieHeader) {
299:             return false;
300:         }
301: 
302:         $cookies = array();
303:         foreach ((array)$cookieHeader as $cookie) {
304:             if (strpos($cookie, '";"') !== false) {
305:                 $cookie = str_replace('";"', "{__cookie_replace__}", $cookie);
306:                 $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie));
307:             } else {
308:                 $parts = preg_split('/\;[ \t]*/', $cookie);
309:             }
310: 
311:             list($name, $value) = explode('=', array_shift($parts), 2);
312:             $cookies[$name] = compact('value');
313: 
314:             foreach ($parts as $part) {
315:                 if (strpos($part, '=') !== false) {
316:                     list($key, $value) = explode('=', $part);
317:                 } else {
318:                     $key = $part;
319:                     $value = true;
320:                 }
321: 
322:                 $key = strtolower($key);
323:                 if (!isset($cookies[$name][$key])) {
324:                     $cookies[$name][$key] = $value;
325:                 }
326:             }
327:         }
328:         return $cookies;
329:     }
330: 
331: /**
332:  * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs)
333:  *
334:  * @param string $token Token to unescape
335:  * @param array $chars
336:  * @return string Unescaped token
337:  * @todo Test $chars parameter
338:  */
339:     protected function _unescapeToken($token, $chars = null) {
340:         $regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/';
341:         $token = preg_replace($regex, '\\1', $token);
342:         return $token;
343:     }
344: 
345: /**
346:  * Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
347:  *
348:  * @param boolean $hex true to get them as HEX values, false otherwise
349:  * @param array $chars
350:  * @return array Escape chars
351:  * @todo Test $chars parameter
352:  */
353:     protected function _tokenEscapeChars($hex = true, $chars = null) {
354:         if (!empty($chars)) {
355:             $escape = $chars;
356:         } else {
357:             $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " ");
358:             for ($i = 0; $i <= 31; $i++) {
359:                 $escape[] = chr($i);
360:             }
361:             $escape[] = chr(127);
362:         }
363: 
364:         if ($hex == false) {
365:             return $escape;
366:         }
367:         foreach ($escape as $key => $char) {
368:             $escape[$key] = '\\x' . str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT);
369:         }
370:         return $escape;
371:     }
372: 
373: /**
374:  * ArrayAccess - Offset Exists
375:  *
376:  * @param mixed $offset
377:  * @return boolean
378:  */
379:     public function offsetExists($offset) {
380:         return in_array($offset, array('raw', 'status', 'header', 'body', 'cookies'));
381:     }
382: 
383: /**
384:  * ArrayAccess - Offset Get
385:  *
386:  * @param mixed $offset
387:  * @return mixed
388:  */
389:     public function offsetGet($offset) {
390:         switch ($offset) {
391:             case 'raw':
392:                 $firstLineLength = strpos($this->raw, "\r\n") + 2;
393:                 if ($this->raw[$firstLineLength] === "\r") {
394:                     $header = null;
395:                 } else {
396:                     $header = substr($this->raw, $firstLineLength, strpos($this->raw, "\r\n\r\n") - $firstLineLength) . "\r\n";
397:                 }
398:                 return array(
399:                     'status-line' => $this->httpVersion . ' ' . $this->code . ' ' . $this->reasonPhrase . "\r\n",
400:                     'header' => $header,
401:                     'body' => $this->body,
402:                     'response' => $this->raw
403:                 );
404:             case 'status':
405:                 return array(
406:                     'http-version' => $this->httpVersion,
407:                     'code' => $this->code,
408:                     'reason-phrase' => $this->reasonPhrase
409:                 );
410:             case 'header':
411:                 return $this->headers;
412:             case 'body':
413:                 return $this->body;
414:             case 'cookies':
415:                 return $this->cookies;
416:         }
417:         return null;
418:     }
419: 
420: /**
421:  * ArrayAccess - Offset Set
422:  *
423:  * @param mixed $offset
424:  * @param mixed $value
425:  * @return void
426:  */
427:     public function offsetSet($offset, $value) {
428:     }
429: 
430: /**
431:  * ArrayAccess - Offset Unset
432:  *
433:  * @param mixed $offset
434:  * @return void
435:  */
436:     public function offsetUnset($offset) {
437:     }
438: 
439: /**
440:  * Instance as string
441:  *
442:  * @return string
443:  */
444:     public function __toString() {
445:         return $this->body();
446:     }
447: 
448: }
449: 
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