comparison cake/libs/controller/components/request_handler.php @ 0:261e66bd5a0c

hg init
author Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp>
date Sun, 24 Jul 2011 21:08:31 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:261e66bd5a0c
1 <?php
2 /**
3 * Request object for handling alternative HTTP requests
4 *
5 * Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
6 * and the like. These units have no use for Ajax requests, and this Component can tell how Cake
7 * should respond to the different needs of a handheld computer and a desktop machine.
8 *
9 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
11 *
12 * Licensed under The MIT License
13 * Redistributions of files must retain the above copyright notice.
14 *
15 * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
16 * @link http://cakephp.org CakePHP(tm) Project
17 * @package cake
18 * @subpackage cake.cake.libs.controller.components
19 * @since CakePHP(tm) v 0.10.4.1076
20 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
21 */
22
23 /**
24 * Request object for handling HTTP requests
25 *
26 * @package cake
27 * @subpackage cake.cake.libs.controller.components
28 * @link http://book.cakephp.org/view/1291/Request-Handling
29 *
30 */
31 class RequestHandlerComponent extends Object {
32
33 /**
34 * The layout that will be switched to for Ajax requests
35 *
36 * @var string
37 * @access public
38 * @see RequestHandler::setAjax()
39 */
40 var $ajaxLayout = 'ajax';
41
42 /**
43 * Determines whether or not callbacks will be fired on this component
44 *
45 * @var boolean
46 * @access public
47 */
48 var $enabled = true;
49
50 /**
51 * Holds the content-type of the response that is set when using
52 * RequestHandler::respondAs()
53 *
54 * @var string
55 * @access private
56 */
57 var $__responseTypeSet = null;
58
59 /**
60 * Holds the copy of Controller::$params
61 *
62 * @var array
63 * @access public
64 */
65 var $params = array();
66
67 /**
68 * Friendly content-type mappings used to set response types and determine
69 * request types. Can be modified with RequestHandler::setContent()
70 *
71 * @var array
72 * @access private
73 * @see RequestHandlerComponent::setContent
74 */
75 var $__requestContent = array(
76 'javascript' => 'text/javascript',
77 'js' => 'text/javascript',
78 'json' => 'application/json',
79 'css' => 'text/css',
80 'html' => array('text/html', '*/*'),
81 'text' => 'text/plain',
82 'txt' => 'text/plain',
83 'csv' => array('application/vnd.ms-excel', 'text/plain'),
84 'form' => 'application/x-www-form-urlencoded',
85 'file' => 'multipart/form-data',
86 'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
87 'xhtml-mobile' => 'application/vnd.wap.xhtml+xml',
88 'xml' => array('application/xml', 'text/xml'),
89 'rss' => 'application/rss+xml',
90 'atom' => 'application/atom+xml',
91 'amf' => 'application/x-amf',
92 'wap' => array(
93 'text/vnd.wap.wml',
94 'text/vnd.wap.wmlscript',
95 'image/vnd.wap.wbmp'
96 ),
97 'wml' => 'text/vnd.wap.wml',
98 'wmlscript' => 'text/vnd.wap.wmlscript',
99 'wbmp' => 'image/vnd.wap.wbmp',
100 'pdf' => 'application/pdf',
101 'zip' => 'application/x-zip',
102 'tar' => 'application/x-tar'
103 );
104
105 /**
106 * List of regular expressions for matching mobile device's user agent string
107 *
108 * @var array
109 * @access public
110 */
111 var $mobileUA = array(
112 'Android',
113 'AvantGo',
114 'BlackBerry',
115 'DoCoMo',
116 'iPod',
117 'iPhone',
118 'J2ME',
119 'MIDP',
120 'NetFront',
121 'Nokia',
122 'Opera Mini',
123 'PalmOS',
124 'PalmSource',
125 'portalmmm',
126 'Plucker',
127 'ReqwirelessWeb',
128 'SonyEricsson',
129 'Symbian',
130 'UP\.Browser',
131 'webOS',
132 'Windows CE',
133 'Xiino'
134 );
135
136 /**
137 * Content-types accepted by the client. If extension parsing is enabled in the
138 * Router, and an extension is detected, the corresponding content-type will be
139 * used as the overriding primary content-type accepted.
140 *
141 * @var array
142 * @access private
143 * @see Router::parseExtensions()
144 */
145 var $__acceptTypes = array();
146
147 /**
148 * The template to use when rendering the given content type.
149 *
150 * @var string
151 * @access private
152 */
153 var $__renderType = null;
154
155 /**
156 * Contains the file extension parsed out by the Router
157 *
158 * @var string
159 * @access public
160 * @see Router::parseExtensions()
161 */
162 var $ext = null;
163
164 /**
165 * Flag set when MIME types have been initialized
166 *
167 * @var boolean
168 * @access private
169 * @see RequestHandler::__initializeTypes()
170 */
171 var $__typesInitialized = false;
172
173 /**
174 * Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT
175 *
176 */
177 function __construct() {
178 $this->__acceptTypes = explode(',', env('HTTP_ACCEPT'));
179
180 foreach ($this->__acceptTypes as $i => $type) {
181 if (strpos($type, ';')) {
182 $type = explode(';', $type);
183 $this->__acceptTypes[$i] = $type[0];
184 }
185 }
186 parent::__construct();
187 }
188
189 /**
190 * Initializes the component, gets a reference to Controller::$parameters, and
191 * checks to see if a file extension has been parsed by the Router. If yes, the
192 * corresponding content-type is pushed onto the list of accepted content-types
193 * as the first item.
194 *
195 * @param object $controller A reference to the controller
196 * @param array $settings Array of settings to _set().
197 * @return void
198 * @see Router::parseExtensions()
199 * @access public
200 */
201 function initialize(&$controller, $settings = array()) {
202 if (isset($controller->params['url']['ext'])) {
203 $this->ext = $controller->params['url']['ext'];
204 }
205 $this->params = $controller->params;
206 $this->_set($settings);
207 }
208
209 /**
210 * The startup method of the RequestHandler enables several automatic behaviors
211 * related to the detection of certain properties of the HTTP request, including:
212 *
213 * - Disabling layout rendering for Ajax requests (based on the HTTP_X_REQUESTED_WITH header)
214 * - If Router::parseExtensions() is enabled, the layout and template type are
215 * switched based on the parsed extension. For example, if controller/action.xml
216 * is requested, the view path becomes <i>app/views/controller/xml/action.ctp</i>.
217 * - If a helper with the same name as the extension exists, it is added to the controller.
218 * - If the extension is of a type that RequestHandler understands, it will set that
219 * Content-type in the response header.
220 * - If the XML data is POSTed, the data is parsed into an XML object, which is assigned
221 * to the $data property of the controller, which can then be saved to a model object.
222 *
223 * @param object $controller A reference to the controller
224 * @return void
225 * @access public
226 */
227 function startup(&$controller) {
228 if (!$this->enabled) {
229 return;
230 }
231
232 $this->__initializeTypes();
233 $controller->params['isAjax'] = $this->isAjax();
234 $isRecognized = (
235 !in_array($this->ext, array('html', 'htm')) &&
236 in_array($this->ext, array_keys($this->__requestContent))
237 );
238
239 if (!empty($this->ext) && $isRecognized) {
240 $this->renderAs($controller, $this->ext);
241 } elseif ($this->isAjax()) {
242 $this->renderAs($controller, 'ajax');
243 } elseif (empty($this->ext) || in_array($this->ext, array('html', 'htm'))) {
244 $this->respondAs('html', array('charset' => Configure::read('App.encoding')));
245 }
246
247 if ($this->requestedWith('xml')) {
248 if (!class_exists('XmlNode')) {
249 App::import('Core', 'Xml');
250 }
251 $xml = new Xml(trim(file_get_contents('php://input')));
252
253 if (count($xml->children) == 1 && is_object($dataNode = $xml->child('data'))) {
254 $controller->data = $dataNode->toArray();
255 } else {
256 $controller->data = $xml->toArray();
257 }
258 }
259 }
260
261 /**
262 * Handles (fakes) redirects for Ajax requests using requestAction()
263 *
264 * @param object $controller A reference to the controller
265 * @param mixed $url A string or array containing the redirect location
266 * @param mixed HTTP Status for redirect
267 * @access public
268 */
269 function beforeRedirect(&$controller, $url, $status = null) {
270 if (!$this->isAjax()) {
271 return;
272 }
273 foreach ($_POST as $key => $val) {
274 unset($_POST[$key]);
275 }
276 if (is_array($url)) {
277 $url = Router::url($url + array('base' => false));
278 }
279 if (!empty($status)) {
280 $statusCode = $controller->httpCodes($status);
281 $code = key($statusCode);
282 $msg = $statusCode[$code];
283 $controller->header("HTTP/1.1 {$code} {$msg}");
284 }
285 echo $this->requestAction($url, array('return', 'bare' => false));
286 $this->_stop();
287 }
288
289 /**
290 * Returns true if the current HTTP request is Ajax, false otherwise
291 *
292 * @return boolean True if call is Ajax
293 * @access public
294 */
295 function isAjax() {
296 return env('HTTP_X_REQUESTED_WITH') === "XMLHttpRequest";
297 }
298
299 /**
300 * Returns true if the current HTTP request is coming from a Flash-based client
301 *
302 * @return boolean True if call is from Flash
303 * @access public
304 */
305 function isFlash() {
306 return (preg_match('/^(Shockwave|Adobe) Flash/', env('HTTP_USER_AGENT')) == 1);
307 }
308
309 /**
310 * Returns true if the current request is over HTTPS, false otherwise.
311 *
312 * @return bool True if call is over HTTPS
313 * @access public
314 */
315 function isSSL() {
316 return env('HTTPS');
317 }
318
319 /**
320 * Returns true if the current call accepts an XML response, false otherwise
321 *
322 * @return boolean True if client accepts an XML response
323 * @access public
324 */
325 function isXml() {
326 return $this->prefers('xml');
327 }
328
329 /**
330 * Returns true if the current call accepts an RSS response, false otherwise
331 *
332 * @return boolean True if client accepts an RSS response
333 * @access public
334 */
335 function isRss() {
336 return $this->prefers('rss');
337 }
338
339 /**
340 * Returns true if the current call accepts an Atom response, false otherwise
341 *
342 * @return boolean True if client accepts an RSS response
343 * @access public
344 */
345 function isAtom() {
346 return $this->prefers('atom');
347 }
348
349 /**
350 * Returns true if user agent string matches a mobile web browser, or if the
351 * client accepts WAP content.
352 *
353 * @return boolean True if user agent is a mobile web browser
354 * @access public
355 * @deprecated Use of constant REQUEST_MOBILE_UA is deprecated and will be removed in future versions
356 */
357 function isMobile() {
358 if (defined('REQUEST_MOBILE_UA')) {
359 $regex = '/' . REQUEST_MOBILE_UA . '/i';
360 } else {
361 $regex = '/' . implode('|', $this->mobileUA) . '/i';
362 }
363
364 if (preg_match($regex, env('HTTP_USER_AGENT')) || $this->accepts('wap')) {
365 return true;
366 }
367 return false;
368 }
369
370 /**
371 * Returns true if the client accepts WAP content
372 *
373 * @return bool
374 * @access public
375 */
376 function isWap() {
377 return $this->prefers('wap');
378 }
379
380 /**
381 * Returns true if the current call a POST request
382 *
383 * @return boolean True if call is a POST
384 * @access public
385 */
386 function isPost() {
387 return (strtolower(env('REQUEST_METHOD')) == 'post');
388 }
389
390 /**
391 * Returns true if the current call a PUT request
392 *
393 * @return boolean True if call is a PUT
394 * @access public
395 */
396 function isPut() {
397 return (strtolower(env('REQUEST_METHOD')) == 'put');
398 }
399
400 /**
401 * Returns true if the current call a GET request
402 *
403 * @return boolean True if call is a GET
404 * @access public
405 */
406 function isGet() {
407 return (strtolower(env('REQUEST_METHOD')) == 'get');
408 }
409
410 /**
411 * Returns true if the current call a DELETE request
412 *
413 * @return boolean True if call is a DELETE
414 * @access public
415 */
416 function isDelete() {
417 return (strtolower(env('REQUEST_METHOD')) == 'delete');
418 }
419
420 /**
421 * Gets Prototype version if call is Ajax, otherwise empty string.
422 * The Prototype library sets a special "Prototype version" HTTP header.
423 *
424 * @return string Prototype version of component making Ajax call
425 * @access public
426 */
427 function getAjaxVersion() {
428 if (env('HTTP_X_PROTOTYPE_VERSION') != null) {
429 return env('HTTP_X_PROTOTYPE_VERSION');
430 }
431 return false;
432 }
433
434 /**
435 * Adds/sets the Content-type(s) for the given name. This method allows
436 * content-types to be mapped to friendly aliases (or extensions), which allows
437 * RequestHandler to automatically respond to requests of that type in the
438 * startup method.
439 *
440 * @param string $name The name of the Content-type, i.e. "html", "xml", "css"
441 * @param mixed $type The Content-type or array of Content-types assigned to the name,
442 * i.e. "text/html", or "application/xml"
443 * @return void
444 * @access public
445 */
446 function setContent($name, $type = null) {
447 if (is_array($name)) {
448 $this->__requestContent = array_merge($this->__requestContent, $name);
449 return;
450 }
451 $this->__requestContent[$name] = $type;
452 }
453
454 /**
455 * Gets the server name from which this request was referred
456 *
457 * @return string Server address
458 * @access public
459 */
460 function getReferer() {
461 if (env('HTTP_HOST') != null) {
462 $sessHost = env('HTTP_HOST');
463 }
464
465 if (env('HTTP_X_FORWARDED_HOST') != null) {
466 $sessHost = env('HTTP_X_FORWARDED_HOST');
467 }
468 return trim(preg_replace('/(?:\:.*)/', '', $sessHost));
469 }
470
471 /**
472 * Gets remote client IP
473 *
474 * @return string Client IP address
475 * @access public
476 */
477 function getClientIP($safe = true) {
478 if (!$safe && env('HTTP_X_FORWARDED_FOR') != null) {
479 $ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
480 } else {
481 if (env('HTTP_CLIENT_IP') != null) {
482 $ipaddr = env('HTTP_CLIENT_IP');
483 } else {
484 $ipaddr = env('REMOTE_ADDR');
485 }
486 }
487
488 if (env('HTTP_CLIENTADDRESS') != null) {
489 $tmpipaddr = env('HTTP_CLIENTADDRESS');
490
491 if (!empty($tmpipaddr)) {
492 $ipaddr = preg_replace('/(?:,.*)/', '', $tmpipaddr);
493 }
494 }
495 return trim($ipaddr);
496 }
497
498 /**
499 * Determines which content types the client accepts. Acceptance is based on
500 * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
501 * header.
502 *
503 * @param mixed $type Can be null (or no parameter), a string type name, or an
504 * array of types
505 * @return mixed If null or no parameter is passed, returns an array of content
506 * types the client accepts. If a string is passed, returns true
507 * if the client accepts it. If an array is passed, returns true
508 * if the client accepts one or more elements in the array.
509 * @access public
510 * @see RequestHandlerComponent::setContent()
511 */
512 function accepts($type = null) {
513 $this->__initializeTypes();
514
515 if ($type == null) {
516 return $this->mapType($this->__acceptTypes);
517
518 } elseif (is_array($type)) {
519 foreach ($type as $t) {
520 if ($this->accepts($t) == true) {
521 return true;
522 }
523 }
524 return false;
525 } elseif (is_string($type)) {
526
527 if (!isset($this->__requestContent[$type])) {
528 return false;
529 }
530
531 $content = $this->__requestContent[$type];
532
533 if (is_array($content)) {
534 foreach ($content as $c) {
535 if (in_array($c, $this->__acceptTypes)) {
536 return true;
537 }
538 }
539 } else {
540 if (in_array($content, $this->__acceptTypes)) {
541 return true;
542 }
543 }
544 }
545 }
546
547 /**
548 * Determines the content type of the data the client has sent (i.e. in a POST request)
549 *
550 * @param mixed $type Can be null (or no parameter), a string type name, or an array of types
551 * @return mixed
552 * @access public
553 */
554 function requestedWith($type = null) {
555 if (!$this->isPost() && !$this->isPut()) {
556 return null;
557 }
558
559 list($contentType) = explode(';', env('CONTENT_TYPE'));
560 if ($type == null) {
561 return $this->mapType($contentType);
562 } elseif (is_array($type)) {
563 foreach ($type as $t) {
564 if ($this->requestedWith($t)) {
565 return $this->mapType($t);
566 }
567 }
568 return false;
569 } elseif (is_string($type)) {
570 return ($type == $this->mapType($contentType));
571 }
572 }
573
574 /**
575 * Determines which content-types the client prefers. If no parameters are given,
576 * the content-type that the client most likely prefers is returned. If $type is
577 * an array, the first item in the array that the client accepts is returned.
578 * Preference is determined primarily by the file extension parsed by the Router
579 * if provided, and secondarily by the list of content-types provided in
580 * HTTP_ACCEPT.
581 *
582 * @param mixed $type An optional array of 'friendly' content-type names, i.e.
583 * 'html', 'xml', 'js', etc.
584 * @return mixed If $type is null or not provided, the first content-type in the
585 * list, based on preference, is returned.
586 * @access public
587 * @see RequestHandlerComponent::setContent()
588 */
589 function prefers($type = null) {
590 $this->__initializeTypes();
591 $accept = $this->accepts();
592
593 if ($type == null) {
594 if (empty($this->ext)) {
595 if (is_array($accept)) {
596 return $accept[0];
597 }
598 return $accept;
599 }
600 return $this->ext;
601 }
602
603 $types = $type;
604 if (is_string($type)) {
605 $types = array($type);
606 }
607
608 if (count($types) === 1) {
609 if (!empty($this->ext)) {
610 return ($types[0] == $this->ext);
611 }
612 return ($types[0] == $accept[0]);
613 }
614 $accepts = array();
615
616 foreach ($types as $type) {
617 if (in_array($type, $accept)) {
618 $accepts[] = $type;
619 }
620 }
621
622 if (count($accepts) === 0) {
623 return false;
624 } elseif (count($types) === 1) {
625 return ($types[0] === $accepts[0]);
626 } elseif (count($accepts) === 1) {
627 return $accepts[0];
628 }
629
630 $acceptedTypes = array();
631 foreach ($this->__acceptTypes as $type) {
632 $acceptedTypes[] = $this->mapType($type);
633 }
634 $accepts = array_intersect($acceptedTypes, $accepts);
635 return $accepts[0];
636 }
637
638 /**
639 * Sets the layout and template paths for the content type defined by $type.
640 *
641 * @param object $controller A reference to a controller object
642 * @param string $type Type of response to send (e.g: 'ajax')
643 * @return void
644 * @access public
645 * @see RequestHandlerComponent::setContent()
646 * @see RequestHandlerComponent::respondAs()
647 */
648 function renderAs(&$controller, $type) {
649 $this->__initializeTypes();
650 $options = array('charset' => 'UTF-8');
651
652 if (Configure::read('App.encoding') !== null) {
653 $options = array('charset' => Configure::read('App.encoding'));
654 }
655
656 if ($type == 'ajax') {
657 $controller->layout = $this->ajaxLayout;
658 return $this->respondAs('html', $options);
659 }
660 $controller->ext = '.ctp';
661
662 if (empty($this->__renderType)) {
663 $controller->viewPath .= DS . $type;
664 } else {
665 $remove = preg_replace("/([\/\\\\]{$this->__renderType})$/", DS . $type, $controller->viewPath);
666 $controller->viewPath = $remove;
667 }
668 $this->__renderType = $type;
669 $controller->layoutPath = $type;
670
671 if (isset($this->__requestContent[$type])) {
672 $this->respondAs($type, $options);
673 }
674
675 $helper = ucfirst($type);
676 $isAdded = (
677 in_array($helper, $controller->helpers) ||
678 array_key_exists($helper, $controller->helpers)
679 );
680
681 if (!$isAdded) {
682 if (App::import('Helper', $helper)) {
683 $controller->helpers[] = $helper;
684 }
685 }
686 }
687
688 /**
689 * Sets the response header based on type map index name. If DEBUG is greater than 2, the header
690 * is not set.
691 *
692 * @param mixed $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
693 * like 'application/x-shockwave'.
694 * @param array $options If $type is a friendly type name that is associated with
695 * more than one type of content, $index is used to select which content-type to use.
696 * @return boolean Returns false if the friendly type name given in $type does
697 * not exist in the type map, or if the Content-type header has
698 * already been set by this method.
699 * @access public
700 * @see RequestHandlerComponent::setContent()
701 */
702 function respondAs($type, $options = array()) {
703 $this->__initializeTypes();
704 if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
705 return false;
706 }
707 $defaults = array('index' => 0, 'charset' => null, 'attachment' => false);
708 $options = array_merge($defaults, $options);
709
710 if (strpos($type, '/') === false && isset($this->__requestContent[$type])) {
711 $cType = null;
712 if (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][$options['index']])) {
713 $cType = $this->__requestContent[$type][$options['index']];
714 } elseif (is_array($this->__requestContent[$type]) && isset($this->__requestContent[$type][0])) {
715 $cType = $this->__requestContent[$type][0];
716 } elseif (isset($this->__requestContent[$type])) {
717 $cType = $this->__requestContent[$type];
718 } else {
719 return false;
720 }
721
722 if (is_array($cType)) {
723 if ($this->prefers($cType)) {
724 $cType = $this->prefers($cType);
725 } else {
726 $cType = $cType[0];
727 }
728 }
729 } else {
730 $cType = $type;
731 }
732
733 if ($cType != null) {
734 $header = 'Content-type: ' . $cType;
735
736 if (!empty($options['charset'])) {
737 $header .= '; charset=' . $options['charset'];
738 }
739 if (!empty($options['attachment'])) {
740 $this->_header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
741 }
742 if (Configure::read() < 2 && !defined('CAKEPHP_SHELL') && empty($this->params['requested'])) {
743 $this->_header($header);
744 }
745 $this->__responseTypeSet = $cType;
746 return true;
747 }
748 return false;
749 }
750
751 /**
752 * Wrapper for header() so calls can be easily tested.
753 *
754 * @param string $header The header to be sent.
755 * @return void
756 * @access protected
757 */
758 function _header($header) {
759 header($header);
760 }
761
762 /**
763 * Returns the current response type (Content-type header), or null if none has been set
764 *
765 * @return mixed A string content type alias, or raw content type if no alias map exists,
766 * otherwise null
767 * @access public
768 */
769 function responseType() {
770 if ($this->__responseTypeSet == null) {
771 return null;
772 }
773 return $this->mapType($this->__responseTypeSet);
774 }
775
776 /**
777 * Maps a content-type back to an alias
778 *
779 * @param mixed $type Content type
780 * @return mixed Alias
781 * @access public
782 */
783 function mapType($ctype) {
784 if (is_array($ctype)) {
785 $out = array();
786 foreach ($ctype as $t) {
787 $out[] = $this->mapType($t);
788 }
789 return $out;
790 } else {
791 $keys = array_keys($this->__requestContent);
792 $count = count($keys);
793
794 for ($i = 0; $i < $count; $i++) {
795 $name = $keys[$i];
796 $type = $this->__requestContent[$name];
797
798 if (is_array($type) && in_array($ctype, $type)) {
799 return $name;
800 } elseif (!is_array($type) && $type == $ctype) {
801 return $name;
802 }
803 }
804 return $ctype;
805 }
806 }
807
808 /**
809 * Initializes MIME types
810 *
811 * @return void
812 * @access private
813 */
814 function __initializeTypes() {
815 if ($this->__typesInitialized) {
816 return;
817 }
818 if (isset($this->__requestContent[$this->ext])) {
819 $content = $this->__requestContent[$this->ext];
820 if (is_array($content)) {
821 $content = $content[0];
822 }
823 array_unshift($this->__acceptTypes, $content);
824 }
825 $this->__typesInitialized = true;
826 }
827 }