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

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