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 1.3 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 1.3
      • 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

Classes

  • AclBase
  • AclBehavior
  • AclComponent
  • AclNode
  • AclShell
  • Aco
  • AcoAction
  • AjaxHelper
  • ApcEngine
  • ApiShell
  • App
  • AppController
  • AppHelper
  • AppModel
  • Aro
  • AuthComponent
  • BakeShell
  • BakeTask
  • BehaviorCollection
  • Cache
  • CacheEngine
  • CacheHelper
  • CakeErrorController
  • CakeLog
  • CakeRoute
  • CakeSchema
  • CakeSession
  • CakeSocket
  • ClassRegistry
  • Component
  • Configure
  • ConnectionManager
  • ConsoleShell
  • ContainableBehavior
  • Controller
  • ControllerTask
  • CookieComponent
  • DataSource
  • DbAcl
  • DbConfigTask
  • DboMssql
  • DboMysql
  • DboMysqlBase
  • DboMysqli
  • DboOracle
  • DboPostgres
  • DboSource
  • DboSqlite
  • Debugger
  • EmailComponent
  • ErrorHandler
  • ExtractTask
  • File
  • FileEngine
  • FileLog
  • FixtureTask
  • Folder
  • FormHelper
  • Helper
  • HtmlHelper
  • HttpSocket
  • I18n
  • I18nModel
  • I18nShell
  • Inflector
  • IniAcl
  • JavascriptHelper
  • JqueryEngineHelper
  • JsBaseEngineHelper
  • JsHelper
  • L10n
  • MagicDb
  • MagicFileResource
  • MediaView
  • MemcacheEngine
  • Model
  • ModelBehavior
  • ModelTask
  • MootoolsEngineHelper
  • Multibyte
  • NumberHelper
  • Object
  • Overloadable
  • Overloadable2
  • PagesController
  • PaginatorHelper
  • Permission
  • PluginShortRoute
  • PluginTask
  • ProjectTask
  • PrototypeEngineHelper
  • RequestHandlerComponent
  • Router
  • RssHelper
  • Sanitize
  • Scaffold
  • ScaffoldView
  • SchemaShell
  • Security
  • SecurityComponent
  • SessionComponent
  • SessionHelper
  • Set
  • Shell
  • String
  • TemplateTask
  • TestSuiteShell
  • TestTask
  • TextHelper
  • ThemeView
  • TimeHelper
  • TranslateBehavior
  • TreeBehavior
  • Validation
  • View
  • ViewTask
  • XcacheEngine
  • Xml
  • XmlElement
  • XmlHelper
  • XmlManager
  • XmlNode
  • XmlTextNode

Functions

  • mb_encode_mimeheader
  • mb_stripos
  • mb_stristr
  • mb_strlen
  • mb_strpos
  • mb_strrchr
  • mb_strrichr
  • mb_strripos
  • mb_strrpos
  • mb_strstr
  • mb_strtolower
  • mb_strtoupper
  • mb_substr
  • mb_substr_count
   1: <?php
   2: /**
   3:  * HTTP Socket connection class.
   4:  *
   5:  * PHP versions 4 and 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
  16:  * @subpackage    cake.cake.libs
  17:  * @since         CakePHP(tm) v 1.2.0
  18:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
  19:  */
  20: App::import('Core', array('CakeSocket', 'Set', 'Router'));
  21: 
  22: /**
  23:  * Cake network socket connection class.
  24:  *
  25:  * Core base class for HTTP network communication. HttpSocket can be used as an
  26:  * Object Oriented replacement for cURL in many places.
  27:  *
  28:  * @package       cake
  29:  * @subpackage    cake.cake.libs
  30:  */
  31: class HttpSocket extends CakeSocket {
  32: 
  33: /**
  34:  * Object description
  35:  *
  36:  * @var string
  37:  * @access public
  38:  */
  39:     var $description = 'HTTP-based DataSource Interface';
  40: 
  41: /**
  42:  * When one activates the $quirksMode by setting it to true, all checks meant to
  43:  * enforce RFC 2616 (HTTP/1.1 specs).
  44:  * will be disabled and additional measures to deal with non-standard responses will be enabled.
  45:  *
  46:  * @var boolean
  47:  * @access public
  48:  */
  49:     var $quirksMode = false;
  50: 
  51: /**
  52:  * The default values to use for a request
  53:  *
  54:  * @var array
  55:  * @access public
  56:  */
  57:     var $request = array(
  58:         'method' => 'GET',
  59:         'uri' => array(
  60:             'scheme' => 'http',
  61:             'host' => null,
  62:             'port' => 80,
  63:             'user' => null,
  64:             'pass' => null,
  65:             'path' => null,
  66:             'query' => null,
  67:             'fragment' => null
  68:         ),
  69:         'auth' => array(
  70:             'method' => 'Basic',
  71:             'user' => null,
  72:             'pass' => null
  73:         ),
  74:         'version' => '1.1',
  75:         'body' => '',
  76:         'line' => null,
  77:         'header' => array(
  78:             'Connection' => 'close',
  79:             'User-Agent' => 'CakePHP'
  80:         ),
  81:         'raw' => null,
  82:         'cookies' => array()
  83:     );
  84: 
  85: /**
  86: * The default structure for storing the response
  87: *
  88: * @var array
  89: * @access public
  90: */
  91:     var $response = array(
  92:         'raw' => array(
  93:             'status-line' => null,
  94:             'header' => null,
  95:             'body' => null,
  96:             'response' => null
  97:         ),
  98:         'status' => array(
  99:             'http-version' => null,
 100:             'code' => null,
 101:             'reason-phrase' => null
 102:         ),
 103:         'header' => array(),
 104:         'body' => '',
 105:         'cookies' => array()
 106:     );
 107: 
 108: /**
 109:  * Default configuration settings for the HttpSocket
 110:  *
 111:  * @var array
 112:  * @access public
 113:  */
 114:     var $config = array(
 115:         'persistent' => false,
 116:         'host' => 'localhost',
 117:         'protocol' => 'tcp',
 118:         'port' => 80,
 119:         'timeout' => 30,
 120:         'request' => array(
 121:             'uri' => array(
 122:                 'scheme' => 'http',
 123:                 'host' => 'localhost',
 124:                 'port' => 80
 125:             ),
 126:             'auth' => array(
 127:                 'method' => 'Basic',
 128:                 'user' => null,
 129:                 'pass' => null
 130:             ),
 131:             'cookies' => array()
 132:         )
 133:     );
 134: 
 135: /**
 136:  * String that represents a line break.
 137:  *
 138:  * @var string
 139:  * @access public
 140:  */
 141:     var $lineBreak = "\r\n";
 142: 
 143: /**
 144:  * Build an HTTP Socket using the specified configuration.
 145:  *
 146:  * You can use a url string to set the url and use default configurations for
 147:  * all other options:
 148:  *
 149:  * `$http =& new HttpSocket('http://cakephp.org/');`
 150:  *
 151:  * Or use an array to configure multiple options:
 152:  *
 153:  * {{{
 154:  * $http =& new HttpSocket(array(
 155:  *    'host' => 'cakephp.org',
 156:  *    'timeout' => 20
 157:  * ));
 158:  * }}}
 159:  *
 160:  * See HttpSocket::$config for options that can be used.
 161:  *
 162:  * @param mixed $config Configuration information, either a string url or an array of options.
 163:  * @access public
 164:  */
 165:     function __construct($config = array()) {
 166:         if (is_string($config)) {
 167:             $this->_configUri($config);
 168:         } elseif (is_array($config)) {
 169:             if (isset($config['request']['uri']) && is_string($config['request']['uri'])) {
 170:                 $this->_configUri($config['request']['uri']);
 171:                 unset($config['request']['uri']);
 172:             }
 173:             $this->config = Set::merge($this->config, $config);
 174:         }
 175:         parent::__construct($this->config);
 176:     }
 177: 
 178: /**
 179:  * Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this
 180:  * method and provide a more granular interface.
 181:  *
 182:  * @param mixed $request Either an URI string, or an array defining host/uri
 183:  * @return mixed false on error, request body on success
 184:  * @access public
 185:  */
 186:     function request($request = array()) {
 187:         $this->reset(false);
 188: 
 189:         if (is_string($request)) {
 190:             $request = array('uri' => $request);
 191:         } elseif (!is_array($request)) {
 192:             return false;
 193:         }
 194: 
 195:         if (!isset($request['uri'])) {
 196:             $request['uri'] = null;
 197:         }
 198:         $uri = $this->_parseUri($request['uri']);
 199:         $hadAuth = false;
 200:         if (is_array($uri) && array_key_exists('user', $uri)) {
 201:             $hadAuth = true;
 202:         }
 203:         if (!isset($uri['host'])) {
 204:             $host = $this->config['host'];
 205:         }
 206:         if (isset($request['host'])) {
 207:             $host = $request['host'];
 208:             unset($request['host']);
 209:         }
 210:         $request['uri'] = $this->url($request['uri']);
 211:         $request['uri'] = $this->_parseUri($request['uri'], true);
 212:         $this->request = Set::merge($this->request, $this->config['request'], $request);
 213: 
 214:         if (!$hadAuth && !empty($this->config['request']['auth']['user'])) {
 215:             $this->request['uri']['user'] = $this->config['request']['auth']['user'];
 216:             $this->request['uri']['pass'] = $this->config['request']['auth']['pass'];
 217:         }
 218:         $this->_configUri($this->request['uri']);
 219: 
 220:         if (isset($host)) {
 221:             $this->config['host'] = $host;
 222:         }
 223:         $cookies = null;
 224: 
 225:         if (is_array($this->request['header'])) {
 226:             $this->request['header'] = $this->_parseHeader($this->request['header']);
 227:             if (!empty($this->request['cookies'])) {
 228:                 $cookies = $this->buildCookies($this->request['cookies']);
 229:             }
 230:             $Host = $this->request['uri']['host'];
 231:             $schema = '';
 232:             $port = 0;
 233:             if (isset($this->request['uri']['schema'])) {
 234:                 $schema = $this->request['uri']['schema'];
 235:             }
 236:             if (isset($this->request['uri']['port'])) {
 237:                 $port = $this->request['uri']['port'];
 238:             }
 239:             if (
 240:                 ($schema === 'http' && $port != 80) ||
 241:                 ($schema === 'https' && $port != 443) ||
 242:                 ($port != 80 && $port != 443)
 243:             ) {
 244:                 $Host .= ':' . $port;
 245:             }
 246:             $this->request['header'] = array_merge(compact('Host'), $this->request['header']);
 247:         }
 248: 
 249:         if (isset($this->request['auth']['user']) && isset($this->request['auth']['pass'])) {
 250:             $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['auth']['user'] . ":" . $this->request['auth']['pass']);
 251:         }
 252:         if (isset($this->request['uri']['user']) && isset($this->request['uri']['pass'])) {
 253:             $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['uri']['user'] . ":" . $this->request['uri']['pass']);
 254:         }
 255: 
 256:         if (is_array($this->request['body'])) {
 257:             $this->request['body'] = $this->_httpSerialize($this->request['body']);
 258:         }
 259: 
 260:         if (!empty($this->request['body']) && !isset($this->request['header']['Content-Type'])) {
 261:             $this->request['header']['Content-Type'] = 'application/x-www-form-urlencoded';
 262:         }
 263: 
 264:         if (!empty($this->request['body']) && !isset($this->request['header']['Content-Length'])) {
 265:             $this->request['header']['Content-Length'] = strlen($this->request['body']);
 266:         }
 267: 
 268:         $connectionType = null;
 269:         if (isset($this->request['header']['Connection'])) {
 270:             $connectionType = $this->request['header']['Connection'];
 271:         }
 272:         $this->request['header'] = $this->_buildHeader($this->request['header']) . $cookies;
 273: 
 274:         if (empty($this->request['line'])) {
 275:             $this->request['line'] = $this->_buildRequestLine($this->request);
 276:         }
 277: 
 278:         if ($this->quirksMode === false && $this->request['line'] === false) {
 279:             return $this->response = false;
 280:         }
 281: 
 282:         if ($this->request['line'] !== false) {
 283:             $this->request['raw'] = $this->request['line'];
 284:         }
 285: 
 286:         if ($this->request['header'] !== false) {
 287:             $this->request['raw'] .= $this->request['header'];
 288:         }
 289: 
 290:         $this->request['raw'] .= "\r\n";
 291:         $this->request['raw'] .= $this->request['body'];
 292:         $this->write($this->request['raw']);
 293: 
 294:         $response = null;
 295:         while ($data = $this->read()) {
 296:             $response .= $data;
 297:         }
 298: 
 299:         if ($connectionType == 'close') {
 300:             $this->disconnect();
 301:         }
 302: 
 303:         $this->response = $this->_parseResponse($response);
 304:         if (!empty($this->response['cookies'])) {
 305:             $this->config['request']['cookies'] = array_merge($this->config['request']['cookies'], $this->response['cookies']);
 306:         }
 307: 
 308:         return $this->response['body'];
 309:     }
 310: 
 311: /**
 312:  * Issues a GET request to the specified URI, query, and request.
 313:  *
 314:  * Using a string uri and an array of query string parameters:
 315:  *
 316:  * `$response = $http->get('http://google.com/search', array('q' => 'cakephp', 'client' => 'safari'));`
 317:  *
 318:  * Would do a GET request to `http://google.com/search?q=cakephp&client=safari`
 319:  *
 320:  * You could express the same thing using a uri array and query string parameters:
 321:  *
 322:  * {{{
 323:  * $response = $http->get(
 324:  *     array('host' => 'google.com', 'path' => '/search'),
 325:  *     array('q' => 'cakephp', 'client' => 'safari')
 326:  * );
 327:  * }}}
 328:  *
 329:  * @param mixed $uri URI to request. Either a string uri, or a uri array, see HttpSocket::_parseUri()
 330:  * @param array $query Querystring parameters to append to URI
 331:  * @param array $request An indexed array with indexes such as 'method' or uri
 332:  * @return mixed Result of request, either false on failure or the response to the request.
 333:  * @access public
 334:  */
 335:     function get($uri = null, $query = array(), $request = array()) {
 336:         if (!empty($query)) {
 337:             $uri = $this->_parseUri($uri);
 338:             if (isset($uri['query'])) {
 339:                 $uri['query'] = array_merge($uri['query'], $query);
 340:             } else {
 341:                 $uri['query'] = $query;
 342:             }
 343:             $uri = $this->_buildUri($uri);
 344:         }
 345: 
 346:         $request = Set::merge(array('method' => 'GET', 'uri' => $uri), $request);
 347:         return $this->request($request);
 348:     }
 349: 
 350: /**
 351:  * Issues a POST request to the specified URI, query, and request.
 352:  *
 353:  * `post()` can be used to post simple data arrays to a url:
 354:  *
 355:  * {{{
 356:  * $response = $http->post('http://example.com', array(
 357:  *     'username' => 'batman',
 358:  *     'password' => 'bruce_w4yne'
 359:  * ));
 360:  * }}}
 361:  *
 362:  * @param mixed $uri URI to request. See HttpSocket::_parseUri()
 363:  * @param array $data Array of POST data keys and values.
 364:  * @param array $request An indexed array with indexes such as 'method' or uri
 365:  * @return mixed Result of request, either false on failure or the response to the request.
 366:  * @access public
 367:  */
 368:     function post($uri = null, $data = array(), $request = array()) {
 369:         $request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request);
 370:         return $this->request($request);
 371:     }
 372: 
 373: /**
 374:  * Issues a PUT request to the specified URI, query, and request.
 375:  *
 376:  * @param mixed $uri URI to request, See HttpSocket::_parseUri()
 377:  * @param array $data Array of PUT data keys and values.
 378:  * @param array $request An indexed array with indexes such as 'method' or uri
 379:  * @return mixed Result of request
 380:  * @access public
 381:  */
 382:     function put($uri = null, $data = array(), $request = array()) {
 383:         $request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request);
 384:         return $this->request($request);
 385:     }
 386: 
 387: /**
 388:  * Issues a DELETE request to the specified URI, query, and request.
 389:  *
 390:  * @param mixed $uri URI to request (see {@link _parseUri()})
 391:  * @param array $data Query to append to URI
 392:  * @param array $request An indexed array with indexes such as 'method' or uri
 393:  * @return mixed Result of request
 394:  * @access public
 395:  */
 396:     function delete($uri = null, $data = array(), $request = array()) {
 397:         $request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request);
 398:         return $this->request($request);
 399:     }
 400: 
 401: /**
 402:  * Normalizes urls into a $uriTemplate.  If no template is provided
 403:  * a default one will be used. Will generate the url using the
 404:  * current config information.
 405:  *
 406:  * ### Usage:
 407:  *
 408:  * After configuring part of the request parameters, you can use url() to generate
 409:  * urls.
 410:  *
 411:  * {{{
 412:  * $http->configUri('http://www.cakephp.org');
 413:  * $url = $http->url('/search?q=bar');
 414:  * }}}
 415:  *
 416:  * Would return `http://www.cakephp.org/search?q=bar`
 417:  *
 418:  * url() can also be used with custom templates:
 419:  *
 420:  * `$url = $http->url('http://www.cakephp/search?q=socket', '/%path?%query');`
 421:  *
 422:  * Would return `/search?q=socket`.
 423:  *
 424:  * @param mixed $url Either a string or array of url options to create a url with.
 425:  * @param string $uriTemplate A template string to use for url formatting.
 426:  * @return mixed Either false on failure or a string containing the composed url.
 427:  * @access public
 428:  */
 429:     function url($url = null, $uriTemplate = null) {
 430:         if (is_null($url)) {
 431:             $url = '/';
 432:         }
 433:         if (is_string($url)) {
 434:             if ($url{0} == '/') {
 435:                 $url = $this->config['request']['uri']['host'].':'.$this->config['request']['uri']['port'] . $url;
 436:             }
 437:             if (!preg_match('/^.+:\/\/|\*|^\//', $url)) {
 438:                 $url = $this->config['request']['uri']['scheme'].'://'.$url;
 439:             }
 440:         } elseif (!is_array($url) && !empty($url)) {
 441:             return false;
 442:         }
 443: 
 444:         $base = array_merge($this->config['request']['uri'], array('scheme' => array('http', 'https'), 'port' => array(80, 443)));
 445:         $url = $this->_parseUri($url, $base);
 446: 
 447:         if (empty($url)) {
 448:             $url = $this->config['request']['uri'];
 449:         }
 450: 
 451:         if (!empty($uriTemplate)) {
 452:             return $this->_buildUri($url, $uriTemplate);
 453:         }
 454:         return $this->_buildUri($url);
 455:     }
 456: 
 457: /**
 458:  * Parses the given message and breaks it down in parts.
 459:  *
 460:  * @param string $message Message to parse
 461:  * @return array Parsed message (with indexed elements such as raw, status, header, body)
 462:  * @access protected
 463:  */
 464:     function _parseResponse($message) {
 465:         if (is_array($message)) {
 466:             return $message;
 467:         } elseif (!is_string($message)) {
 468:             return false;
 469:         }
 470: 
 471:         static $responseTemplate;
 472: 
 473:         if (empty($responseTemplate)) {
 474:             $classVars = get_class_vars(__CLASS__);
 475:             $responseTemplate = $classVars['response'];
 476:         }
 477: 
 478:         $response = $responseTemplate;
 479: 
 480:         if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) {
 481:             return false;
 482:         }
 483: 
 484:         list($null, $response['raw']['status-line'], $response['raw']['header']) = $match;
 485:         $response['raw']['response'] = $message;
 486:         $response['raw']['body'] = substr($message, strlen($match[0]));
 487: 
 488:         if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $response['raw']['status-line'], $match)) {
 489:             $response['status']['http-version'] = $match[1];
 490:             $response['status']['code'] = (int)$match[2];
 491:             $response['status']['reason-phrase'] = $match[3];
 492:         }
 493: 
 494:         $response['header'] = $this->_parseHeader($response['raw']['header']);
 495:         $transferEncoding = null;
 496:         if (isset($response['header']['Transfer-Encoding'])) {
 497:             $transferEncoding = $response['header']['Transfer-Encoding'];
 498:         }
 499:         $decoded = $this->_decodeBody($response['raw']['body'], $transferEncoding);
 500:         $response['body'] = $decoded['body'];
 501: 
 502:         if (!empty($decoded['header'])) {
 503:             $response['header'] = $this->_parseHeader($this->_buildHeader($response['header']).$this->_buildHeader($decoded['header']));
 504:         }
 505: 
 506:         if (!empty($response['header'])) {
 507:             $response['cookies'] = $this->parseCookies($response['header']);
 508:         }
 509: 
 510:         foreach ($response['raw'] as $field => $val) {
 511:             if ($val === '') {
 512:                 $response['raw'][$field] = null;
 513:             }
 514:         }
 515: 
 516:         return $response;
 517:     }
 518: 
 519: /**
 520:  * Generic function to decode a $body with a given $encoding. Returns either an array with the keys
 521:  * 'body' and 'header' or false on failure.
 522:  *
 523:  * @param string $body A string continaing the body to decode.
 524:  * @param mixed $encoding Can be false in case no encoding is being used, or a string representing the encoding.
 525:  * @return mixed Array of response headers and body or false.
 526:  * @access protected
 527:  */
 528:     function _decodeBody($body, $encoding = 'chunked') {
 529:         if (!is_string($body)) {
 530:             return false;
 531:         }
 532:         if (empty($encoding)) {
 533:             return array('body' => $body, 'header' => false);
 534:         }
 535:         $decodeMethod = '_decode'.Inflector::camelize(str_replace('-', '_', $encoding)).'Body';
 536: 
 537:         if (!is_callable(array(&$this, $decodeMethod))) {
 538:             if (!$this->quirksMode) {
 539:                 trigger_error(sprintf(__('HttpSocket::_decodeBody - Unknown encoding: %s. Activate quirks mode to surpress error.', true), h($encoding)), E_USER_WARNING);
 540:             }
 541:             return array('body' => $body, 'header' => false);
 542:         }
 543:         return $this->{$decodeMethod}($body);
 544:     }
 545: 
 546: /**
 547:  * Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as
 548:  * a result.
 549:  *
 550:  * @param string $body A string continaing the chunked body to decode.
 551:  * @return mixed Array of response headers and body or false.
 552:  * @access protected
 553:  */
 554:     function _decodeChunkedBody($body) {
 555:         if (!is_string($body)) {
 556:             return false;
 557:         }
 558: 
 559:         $decodedBody = null;
 560:         $chunkLength = null;
 561: 
 562:         while ($chunkLength !== 0) {
 563:             if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) {
 564:                 if (!$this->quirksMode) {
 565:                     trigger_error(__('HttpSocket::_decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.', true), E_USER_WARNING);
 566:                     return false;
 567:                 }
 568:                 break;
 569:             }
 570: 
 571:             $chunkSize = 0;
 572:             $hexLength = 0;
 573:             $chunkExtensionName = '';
 574:             $chunkExtensionValue = '';
 575:             if (isset($match[0])) {
 576:                 $chunkSize = $match[0];
 577:             }
 578:             if (isset($match[1])) {
 579:                 $hexLength = $match[1];
 580:             }
 581:             if (isset($match[2])) {
 582:                 $chunkExtensionName = $match[2];
 583:             }
 584:             if (isset($match[3])) {
 585:                 $chunkExtensionValue = $match[3];
 586:             }
 587: 
 588:             $body = substr($body, strlen($chunkSize));
 589:             $chunkLength = hexdec($hexLength);
 590:             $chunk = substr($body, 0, $chunkLength);
 591:             if (!empty($chunkExtensionName)) {
 592:                 /**
 593:                  * @todo See if there are popular chunk extensions we should implement
 594:                  */
 595:             }
 596:             $decodedBody .= $chunk;
 597:             if ($chunkLength !== 0) {
 598:                 $body = substr($body, $chunkLength+strlen("\r\n"));
 599:             }
 600:         }
 601: 
 602:         $entityHeader = false;
 603:         if (!empty($body)) {
 604:             $entityHeader = $this->_parseHeader($body);
 605:         }
 606:         return array('body' => $decodedBody, 'header' => $entityHeader);
 607:     }
 608: 
 609: /**
 610:  * Parses and sets the specified URI into current request configuration.
 611:  *
 612:  * @param mixed $uri URI, See HttpSocket::_parseUri()
 613:  * @return array Current configuration settings
 614:  * @access protected
 615:  */
 616:     function _configUri($uri = null) {
 617:         if (empty($uri)) {
 618:             return false;
 619:         }
 620: 
 621:         if (is_array($uri)) {
 622:             $uri = $this->_parseUri($uri);
 623:         } else {
 624:             $uri = $this->_parseUri($uri, true);
 625:         }
 626: 
 627:         if (!isset($uri['host'])) {
 628:             return false;
 629:         }
 630:         $config = array(
 631:             'request' => array(
 632:                 'uri' => array_intersect_key($uri, $this->config['request']['uri']),
 633:                 'auth' => array_intersect_key($uri, $this->config['request']['auth'])
 634:             )
 635:         );
 636:         $this->config = Set::merge($this->config, $config);
 637:         $this->config = Set::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config));
 638:         return $this->config;
 639:     }
 640: 
 641: /**
 642:  * Takes a $uri array and turns it into a fully qualified URL string
 643:  *
 644:  * @param mixed $uri Either A $uri array, or a request string.  Will use $this->config if left empty.
 645:  * @param string $uriTemplate The Uri template/format to use.
 646:  * @return mixed A fully qualified URL formated according to $uriTemplate, or false on failure
 647:  * @access protected
 648:  */
 649:     function _buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') {
 650:         if (is_string($uri)) {
 651:             $uri = array('host' => $uri);
 652:         }
 653:         $uri = $this->_parseUri($uri, true);
 654: 
 655:         if (!is_array($uri) || empty($uri)) {
 656:             return false;
 657:         }
 658: 
 659:         $uri['path'] = preg_replace('/^\//', null, $uri['path']);
 660:         $uri['query'] = $this->_httpSerialize($uri['query']);
 661:         $stripIfEmpty = array(
 662:             'query' => '?%query',
 663:             'fragment' => '#%fragment',
 664:             'user' => '%user:%pass@',
 665:             'host' => '%host:%port/'
 666:         );
 667: 
 668:         foreach ($stripIfEmpty as $key => $strip) {
 669:             if (empty($uri[$key])) {
 670:                 $uriTemplate = str_replace($strip, null, $uriTemplate);
 671:             }
 672:         }
 673: 
 674:         $defaultPorts = array('http' => 80, 'https' => 443);
 675:         if (array_key_exists($uri['scheme'], $defaultPorts) && $defaultPorts[$uri['scheme']] == $uri['port']) {
 676:             $uriTemplate = str_replace(':%port', null, $uriTemplate);
 677:         }
 678:         foreach ($uri as $property => $value) {
 679:             $uriTemplate = str_replace('%'.$property, $value, $uriTemplate);
 680:         }
 681: 
 682:         if ($uriTemplate === '/*') {
 683:             $uriTemplate = '*';
 684:         }
 685:         return $uriTemplate;
 686:     }
 687: 
 688: /**
 689:  * Parses the given URI and breaks it down into pieces as an indexed array with elements
 690:  * such as 'scheme', 'port', 'query'.
 691:  *
 692:  * @param string $uri URI to parse
 693:  * @param mixed $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc.
 694:  * @return array Parsed URI
 695:  * @access protected
 696:  */
 697:     function _parseUri($uri = null, $base = array()) {
 698:         $uriBase = array(
 699:             'scheme' => array('http', 'https'),
 700:             'host' => null,
 701:             'port' => array(80, 443),
 702:             'user' => null,
 703:             'pass' => null,
 704:             'path' => '/',
 705:             'query' => null,
 706:             'fragment' => null
 707:         );
 708: 
 709:         if (is_string($uri)) {
 710:             $uri = parse_url($uri);
 711:         }
 712:         if (!is_array($uri) || empty($uri)) {
 713:             return false;
 714:         }
 715:         if ($base === true) {
 716:             $base = $uriBase;
 717:         }
 718: 
 719:         if (isset($base['port'], $base['scheme']) && is_array($base['port']) && is_array($base['scheme'])) {
 720:             if (isset($uri['scheme']) && !isset($uri['port'])) {
 721:                 $base['port'] = $base['port'][array_search($uri['scheme'], $base['scheme'])];
 722:             } elseif (isset($uri['port']) && !isset($uri['scheme'])) {
 723:                 $base['scheme'] = $base['scheme'][array_search($uri['port'], $base['port'])];
 724:             }
 725:         }
 726: 
 727:         if (is_array($base) && !empty($base)) {
 728:             $uri = array_merge($base, $uri);
 729:         }
 730: 
 731:         if (isset($uri['scheme']) && is_array($uri['scheme'])) {
 732:             $uri['scheme'] = array_shift($uri['scheme']);
 733:         }
 734:         if (isset($uri['port']) && is_array($uri['port'])) {
 735:             $uri['port'] = array_shift($uri['port']);
 736:         }
 737: 
 738:         if (array_key_exists('query', $uri)) {
 739:             $uri['query'] = $this->_parseQuery($uri['query']);
 740:         }
 741: 
 742:         if (!array_intersect_key($uriBase, $uri)) {
 743:             return false;
 744:         }
 745:         return $uri;
 746:     }
 747: 
 748: /**
 749:  * This function can be thought of as a reverse to PHP5's http_build_query(). It takes a given query string and turns it into an array and
 750:  * supports nesting by using the php bracket syntax. So this menas you can parse queries like:
 751:  *
 752:  * - ?key[subKey]=value
 753:  * - ?key[]=value1&key[]=value2
 754:  *
 755:  * A leading '?' mark in $query is optional and does not effect the outcome of this function. 
 756:  * For the complete capabilities of this implementation take a look at HttpSocketTest::testparseQuery()
 757:  *
 758:  * @param mixed $query A query string to parse into an array or an array to return directly "as is"
 759:  * @return array The $query parsed into a possibly multi-level array. If an empty $query is
 760:  *     given, an empty array is returned.
 761:  * @access protected
 762:  */
 763:     function _parseQuery($query) {
 764:         if (is_array($query)) {
 765:             return $query;
 766:         }
 767:         $parsedQuery = array();
 768: 
 769:         if (is_string($query) && !empty($query)) {
 770:             $query = preg_replace('/^\?/', '', $query);
 771:             $items = explode('&', $query);
 772: 
 773:             foreach ($items as $item) {
 774:                 if (strpos($item, '=') !== false) {
 775:                     list($key, $value) = explode('=', $item, 2);
 776:                 } else {
 777:                     $key = $item;
 778:                     $value = null;
 779:                 }
 780: 
 781:                 $key = urldecode($key);
 782:                 $value = urldecode($value);
 783: 
 784:                 if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) {
 785:                     $subKeys = $matches[1];
 786:                     $rootKey = substr($key, 0, strpos($key, '['));
 787:                     if (!empty($rootKey)) {
 788:                         array_unshift($subKeys, $rootKey);
 789:                     }
 790:                     $queryNode =& $parsedQuery;
 791: 
 792:                     foreach ($subKeys as $subKey) {
 793:                         if (!is_array($queryNode)) {
 794:                             $queryNode = array();
 795:                         }
 796: 
 797:                         if ($subKey === '') {
 798:                             $queryNode[] = array();
 799:                             end($queryNode);
 800:                             $subKey = key($queryNode);
 801:                         }
 802:                         $queryNode =& $queryNode[$subKey];
 803:                     }
 804:                     $queryNode = $value;
 805:                 } else {
 806:                     $parsedQuery[$key] = $value;
 807:                 }
 808:             }
 809:         }
 810:         return $parsedQuery;
 811:     }
 812: 
 813: /**
 814:  * Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs.
 815:  *
 816:  * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET.
 817:  * @param string $versionToken The version token to use, defaults to HTTP/1.1
 818:  * @return string Request line
 819:  * @access protected
 820:  */
 821:     function _buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') {
 822:         $asteriskMethods = array('OPTIONS');
 823: 
 824:         if (is_string($request)) {
 825:             $isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match);
 826:             if (!$this->quirksMode && (!$isValid || ($match[2] == '*' && !in_array($match[3], $asteriskMethods)))) {
 827:                 trigger_error(__('HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.', true), E_USER_WARNING);
 828:                 return false;
 829:             }
 830:             return $request;
 831:         } elseif (!is_array($request)) {
 832:             return false;
 833:         } elseif (!array_key_exists('uri', $request)) {
 834:             return false;
 835:         }
 836: 
 837:         $request['uri'] = $this->_parseUri($request['uri']);
 838:         $request = array_merge(array('method' => 'GET'), $request);
 839:         $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query');
 840: 
 841:         if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) {
 842:             trigger_error(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', true), join(',', $asteriskMethods)), E_USER_WARNING);
 843:             return false;
 844:         }
 845:         return $request['method'].' '.$request['uri'].' '.$versionToken.$this->lineBreak;
 846:     }
 847: 
 848: /**
 849:  * Serializes an array for transport.
 850:  *
 851:  * @param array $data Data to serialize
 852:  * @return string Serialized variable
 853:  * @access protected
 854:  */
 855:     function _httpSerialize($data = array()) {
 856:         if (is_string($data)) {
 857:             return $data;
 858:         }
 859:         if (empty($data) || !is_array($data)) {
 860:             return false;
 861:         }
 862:         return substr(Router::queryString($data), 1);
 863:     }
 864: 
 865: /**
 866:  * Builds the header.
 867:  *
 868:  * @param array $header Header to build
 869:  * @return string Header built from array
 870:  * @access protected
 871:  */
 872:     function _buildHeader($header, $mode = 'standard') {
 873:         if (is_string($header)) {
 874:             return $header;
 875:         } elseif (!is_array($header)) {
 876:             return false;
 877:         }
 878: 
 879:         $returnHeader = '';
 880:         foreach ($header as $field => $contents) {
 881:             if (is_array($contents) && $mode == 'standard') {
 882:                 $contents = implode(',', $contents);
 883:             }
 884:             foreach ((array)$contents as $content) {
 885:                 $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content);
 886:                 $field = $this->_escapeToken($field);
 887: 
 888:                 $returnHeader .= $field.': '.$contents.$this->lineBreak;
 889:             }
 890:         }
 891:         return $returnHeader;
 892:     }
 893: 
 894: /**
 895:  * Parses an array based header.
 896:  *
 897:  * @param array $header Header as an indexed array (field => value)
 898:  * @return array Parsed header
 899:  * @access protected
 900:  */
 901:     function _parseHeader($header) {
 902:         if (is_array($header)) {
 903:             foreach ($header as $field => $value) {
 904:                 unset($header[$field]);
 905:                 $field = strtolower($field);
 906:                 preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE);
 907: 
 908:                 foreach ($offsets[0] as $offset) {
 909:                     $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1);
 910:                 }
 911:                 $header[$field] = $value;
 912:             }
 913:             return $header;
 914:         } elseif (!is_string($header)) {
 915:             return false;
 916:         }
 917: 
 918:         preg_match_all("/(.+):(.+)(?:(?<![\t ])" . $this->lineBreak . "|\$)/Uis", $header, $matches, PREG_SET_ORDER);
 919: 
 920:         $header = array();
 921:         foreach ($matches as $match) {
 922:             list(, $field, $value) = $match;
 923: 
 924:             $value = trim($value);
 925:             $value = preg_replace("/[\t ]\r\n/", "\r\n", $value);
 926: 
 927:             $field = $this->_unescapeToken($field);
 928: 
 929:             $field = strtolower($field);
 930:             preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE);
 931:             foreach ($offsets[0] as $offset) {
 932:                 $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1);
 933:             }
 934: 
 935:             if (!isset($header[$field])) {
 936:                 $header[$field] = $value;
 937:             } else {
 938:                 $header[$field] = array_merge((array)$header[$field], (array)$value);
 939:             }
 940:         }
 941:         return $header;
 942:     }
 943: 
 944: /**
 945:  * Parses cookies in response headers.
 946:  *
 947:  * @param array $header Header array containing one ore more 'Set-Cookie' headers.
 948:  * @return mixed Either false on no cookies, or an array of cookies received.
 949:  * @access public
 950:  * @todo Make this 100% RFC 2965 confirm
 951:  */
 952:     function parseCookies($header) {
 953:         if (!isset($header['Set-Cookie'])) {
 954:             return false;
 955:         }
 956: 
 957:         $cookies = array();
 958:         foreach ((array)$header['Set-Cookie'] as $cookie) {
 959:             if (strpos($cookie, '";"') !== false) {
 960:                 $cookie = str_replace('";"', "{__cookie_replace__}", $cookie);
 961:                 $parts  = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie));
 962:             } else {
 963:                 $parts = preg_split('/\;[ \t]*/', $cookie);
 964:             }
 965: 
 966:             list($name, $value) = explode('=', array_shift($parts), 2);
 967:             $cookies[$name] = compact('value');
 968: 
 969:             foreach ($parts as $part) {
 970:                 if (strpos($part, '=') !== false) {
 971:                     list($key, $value) = explode('=', $part);
 972:                 } else {
 973:                     $key = $part;
 974:                     $value = true;
 975:                 }
 976: 
 977:                 $key = strtolower($key);
 978:                 if (!isset($cookies[$name][$key])) {
 979:                     $cookies[$name][$key] = $value;
 980:                 }
 981:             }
 982:         }
 983:         return $cookies;
 984:     }
 985: 
 986: /**
 987:  * Builds cookie headers for a request.
 988:  *
 989:  * @param array $cookies Array of cookies to send with the request.
 990:  * @return string Cookie header string to be sent with the request.
 991:  * @access public
 992:  * @todo Refactor token escape mechanism to be configurable
 993:  */
 994:     function buildCookies($cookies) {
 995:         $header = array();
 996:         foreach ($cookies as $name => $cookie) {
 997:             $header[] = $name.'='.$this->_escapeToken($cookie['value'], array(';'));
 998:         }
 999:         $header = $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic');
1000:         return $header;
1001:     }
1002: 
1003: /**
1004:  * Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs)
1005:  *
1006:  * @param string $token Token to unescape
1007:  * @return string Unescaped token
1008:  * @access protected
1009:  * @todo Test $chars parameter
1010:  */
1011:     function _unescapeToken($token, $chars = null) {
1012:         $regex = '/"(['.join('', $this->_tokenEscapeChars(true, $chars)).'])"/';
1013:         $token = preg_replace($regex, '\\1', $token);
1014:         return $token;
1015:     }
1016: 
1017: /**
1018:  * Escapes a given $token according to RFC 2616 (HTTP 1.1 specs)
1019:  *
1020:  * @param string $token Token to escape
1021:  * @return string Escaped token
1022:  * @access protected
1023:  * @todo Test $chars parameter
1024:  */
1025:     function _escapeToken($token, $chars = null) {
1026:         $regex = '/(['.join('', $this->_tokenEscapeChars(true, $chars)).'])/';
1027:         $token = preg_replace($regex, '"\\1"', $token);
1028:         return $token;
1029:     }
1030: 
1031: /**
1032:  * Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
1033:  *
1034:  * @param boolean $hex true to get them as HEX values, false otherwise
1035:  * @return array Escape chars
1036:  * @access protected
1037:  * @todo Test $chars parameter
1038:  */
1039:     function _tokenEscapeChars($hex = true, $chars = null) {
1040:         if (!empty($chars)) {
1041:             $escape = $chars;
1042:         } else {
1043:             $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " ");
1044:             for ($i = 0; $i <= 31; $i++) {
1045:                 $escape[] = chr($i);
1046:             }
1047:             $escape[] = chr(127);
1048:         }
1049: 
1050:         if ($hex == false) {
1051:             return $escape;
1052:         }
1053:         $regexChars = '';
1054:         foreach ($escape as $key => $char) {
1055:             $escape[$key] = '\\x'.str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT);
1056:         }
1057:         return $escape;
1058:     }
1059: 
1060: /**
1061:  * Resets the state of this HttpSocket instance to it's initial state (before Object::__construct got executed) or does
1062:  * the same thing partially for the request and the response property only.
1063:  *
1064:  * @param boolean $full If set to false only HttpSocket::response and HttpSocket::request are reseted
1065:  * @return boolean True on success
1066:  * @access public
1067:  */
1068:     function reset($full = true) {
1069:         static $initalState = array();
1070:         if (empty($initalState)) {
1071:             $initalState = get_class_vars(__CLASS__);
1072:         }
1073:         if ($full == false) {
1074:             $this->request = $initalState['request'];
1075:             $this->response = $initalState['response'];
1076:             return true;
1077:         }
1078:         parent::reset($initalState);
1079:         return true;
1080:     }
1081: }
1082: 
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