Mercurial > hg > Members > shoshi > webvirt
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 } |