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

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.0
      • 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
        • Auth
    • Core
    • Error
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
    • Network
      • Email
      • Http
    • Routing
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

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