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

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