1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 0.10.4
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Controller\Component;
16:
17: use Cake\Controller\Component;
18: use Cake\Controller\ComponentRegistry;
19: use Cake\Controller\Controller;
20: use Cake\Core\App;
21: use Cake\Core\Configure;
22: use Cake\Core\Exception\Exception;
23: use Cake\Event\Event;
24: use Cake\Http\Exception\NotFoundException;
25: use Cake\Http\Response;
26: use Cake\Routing\Router;
27: use Cake\Utility\Exception\XmlException;
28: use Cake\Utility\Inflector;
29: use Cake\Utility\Xml;
30: use RuntimeException;
31:
32: /**
33: * Request object for handling alternative HTTP requests
34: *
35: * Alternative HTTP requests can come from wireless units like mobile phones, palmtop computers,
36: * and the like. These units have no use for AJAX requests, and this Component can tell how Cake
37: * should respond to the different needs of a handheld computer and a desktop machine.
38: *
39: * @link https://book.cakephp.org/3.0/en/controllers/components/request-handling.html
40: */
41: class RequestHandlerComponent extends Component
42: {
43: /**
44: * @var bool
45: * @deprecated 3.4.0 Unused. Will be removed in 4.0.0
46: */
47: public $enabled = true;
48:
49: /**
50: * Contains the file extension parsed out by the Router
51: *
52: * Deprecated as of 3.7.0. This property will be made protected in 4.0.0.
53: *
54: * @var string|null
55: * @see \Cake\Routing\Router::extensions()
56: */
57: public $ext;
58:
59: /**
60: * The template to use when rendering the given content type.
61: *
62: * @var string|null
63: */
64: protected $_renderType;
65:
66: /**
67: * Default config
68: *
69: * These are merged with user-provided config when the component is used.
70: *
71: * - `checkHttpCache` - Whether to check for HTTP cache.
72: * - `viewClassMap` - Mapping between type and view classes. If undefined
73: * json, xml, and ajax will be mapped. Defining any types will omit the defaults.
74: * - `inputTypeMap` - A mapping between types and deserializers for request bodies.
75: * If undefined json & xml will be mapped. Defining any types will omit the defaults.
76: * - `enableBeforeRedirect` - Set to false to disable the `beforeRedirect` callback. The
77: * `beforeRedirect` functionality has been deprecated.
78: *
79: * @var array
80: */
81: protected $_defaultConfig = [
82: 'checkHttpCache' => true,
83: 'viewClassMap' => [],
84: 'inputTypeMap' => [],
85: 'enableBeforeRedirect' => true
86: ];
87:
88: /**
89: * Set the layout to be used when rendering the AuthComponent's ajaxLogin element.
90: *
91: * @var string
92: * @deprecated 3.3.11 This feature property is not supported and will
93: * be removed in 4.0.0
94: */
95: public $ajaxLayout;
96:
97: /**
98: * Constructor. Parses the accepted content types accepted by the client using HTTP_ACCEPT
99: *
100: * @param \Cake\Controller\ComponentRegistry $registry ComponentRegistry object.
101: * @param array $config Array of config.
102: */
103: public function __construct(ComponentRegistry $registry, array $config = [])
104: {
105: $config += [
106: 'viewClassMap' => [
107: 'json' => 'Json',
108: 'xml' => 'Xml',
109: 'ajax' => 'Ajax'
110: ],
111: 'inputTypeMap' => [
112: 'json' => ['json_decode', true],
113: 'xml' => [[$this, 'convertXml']],
114: ]
115: ];
116: parent::__construct($registry, $config);
117: }
118:
119: /**
120: * Events supported by this component.
121: *
122: * @return array
123: */
124: public function implementedEvents()
125: {
126: return [
127: 'Controller.startup' => 'startup',
128: 'Controller.beforeRender' => 'beforeRender',
129: 'Controller.beforeRedirect' => 'beforeRedirect',
130: ];
131: }
132:
133: /**
134: * @param array $config The config data.
135: * @return void
136: * @deprecated 3.4.0 Unused. To be removed in 4.0.0
137: */
138: public function initialize(array $config)
139: {
140: }
141:
142: /**
143: * Set the extension based on the accept headers.
144: * Compares the accepted types and configured extensions.
145: * If there is one common type, that is assigned as the ext/content type for the response.
146: * The type with the highest weight will be set. If the highest weight has more
147: * than one type matching the extensions, the order in which extensions are specified
148: * determines which type will be set.
149: *
150: * If html is one of the preferred types, no content type will be set, this
151: * is to avoid issues with browsers that prefer HTML and several other content types.
152: *
153: * @param \Cake\Http\ServerRequest $request The request instance.
154: * @param \Cake\Http\Response $response The response instance.
155: * @return void
156: */
157: protected function _setExtension($request, $response)
158: {
159: $accept = $request->parseAccept();
160: if (empty($accept) || current($accept)[0] === 'text/html') {
161: return;
162: }
163:
164: $accepts = $response->mapType($accept);
165: $preferredTypes = current($accepts);
166: if (array_intersect($preferredTypes, ['html', 'xhtml'])) {
167: return;
168: }
169:
170: $extensions = array_unique(
171: array_merge(Router::extensions(), array_keys($this->getConfig('viewClassMap')))
172: );
173: foreach ($accepts as $types) {
174: $ext = array_intersect($extensions, $types);
175: if ($ext) {
176: $this->ext = current($ext);
177: break;
178: }
179: }
180: }
181:
182: /**
183: * The startup method of the RequestHandler enables several automatic behaviors
184: * related to the detection of certain properties of the HTTP request, including:
185: *
186: * If the XML data is POSTed, the data is parsed into an XML object, which is assigned
187: * to the $data property of the controller, which can then be saved to a model object.
188: *
189: * @param \Cake\Event\Event $event The startup event that was fired.
190: * @return void
191: */
192: public function startup(Event $event)
193: {
194: /** @var \Cake\Controller\Controller $controller */
195: $controller = $event->getSubject();
196: $request = $controller->getRequest();
197: $response = $controller->getResponse();
198:
199: if ($request->getParam('_ext')) {
200: $this->ext = $request->getParam('_ext');
201: }
202: if (!$this->ext || in_array($this->ext, ['html', 'htm'])) {
203: $this->_setExtension($request, $response);
204: }
205:
206: $isAjax = $request->is('ajax');
207: $controller->setRequest($request->withParam('isAjax', $isAjax));
208:
209: if (!$this->ext && $isAjax) {
210: $this->ext = 'ajax';
211: }
212:
213: if ($request->is(['get', 'head', 'options'])) {
214: return;
215: }
216:
217: if ($request->getParsedBody() !== []) {
218: return;
219: }
220: foreach ($this->getConfig('inputTypeMap') as $type => $handler) {
221: if (!is_callable($handler[0])) {
222: throw new RuntimeException(sprintf("Invalid callable for '%s' type.", $type));
223: }
224: if ($this->requestedWith($type)) {
225: $input = $request->input(...$handler);
226: $controller->setRequest($request->withParsedBody((array)$input));
227: }
228: }
229: }
230:
231: /**
232: * Helper method to parse xml input data, due to lack of anonymous functions
233: * this lives here.
234: *
235: * @param string $xml XML string.
236: * @return array Xml array data
237: */
238: public function convertXml($xml)
239: {
240: try {
241: $xml = Xml::build($xml, ['return' => 'domdocument', 'readFile' => false]);
242: // We might not get child nodes if there are nested inline entities.
243: if ((int)$xml->childNodes->length > 0) {
244: return Xml::toArray($xml);
245: }
246:
247: return [];
248: } catch (XmlException $e) {
249: return [];
250: }
251: }
252:
253: /**
254: * Handles (fakes) redirects for AJAX requests using requestAction()
255: *
256: * @param \Cake\Event\Event $event The Controller.beforeRedirect event.
257: * @param string|array $url A string or array containing the redirect location
258: * @param \Cake\Http\Response $response The response object.
259: * @return \Cake\Http\Response|null The response object if the redirect is caught.
260: * @deprecated 3.3.5 This functionality will be removed in 4.0.0. You can disable this function
261: * now by setting the `enableBeforeRedirect` config option to false.
262: */
263: public function beforeRedirect(Event $event, $url, Response $response)
264: {
265: if (!$this->getConfig('enableBeforeRedirect')) {
266: return null;
267: }
268: deprecationWarning(
269: 'RequestHandlerComponent::beforeRedirect() is deprecated. ' .
270: 'This functionality will be removed in 4.0.0. Set the `enableBeforeRedirect` ' .
271: 'option to `false` to disable this warning.'
272: );
273: $request = $this->getController()->getRequest();
274: if (!$request->is('ajax')) {
275: return null;
276: }
277: if (empty($url)) {
278: return null;
279: }
280: if (is_array($url)) {
281: $url = Router::url($url + ['_base' => false]);
282: }
283: $query = [];
284: if (strpos($url, '?') !== false) {
285: list($url, $querystr) = explode('?', $url, 2);
286: parse_str($querystr, $query);
287: }
288: /** @var \Cake\Controller\Controller $controller */
289: $controller = $event->getSubject();
290: $response->body($controller->requestAction($url, [
291: 'return',
292: 'bare' => false,
293: 'environment' => [
294: 'REQUEST_METHOD' => 'GET'
295: ],
296: 'query' => $query,
297: 'cookies' => $request->getCookieParams()
298: ]));
299:
300: return $response->withStatus(200);
301: }
302:
303: /**
304: * Checks if the response can be considered different according to the request
305: * headers, and the caching response headers. If it was not modified, then the
306: * render process is skipped. And the client will get a blank response with a
307: * "304 Not Modified" header.
308: *
309: * - If Router::extensions() is enabled, the layout and template type are
310: * switched based on the parsed extension or `Accept` header. For example,
311: * if `controller/action.xml` is requested, the view path becomes
312: * `app/View/Controller/xml/action.ctp`. Also if `controller/action` is
313: * requested with `Accept: application/xml` in the headers the view
314: * path will become `app/View/Controller/xml/action.ctp`. Layout and template
315: * types will only switch to mime-types recognized by Cake\Http\Response.
316: * If you need to declare additional mime-types, you can do so using
317: * Cake\Http\Response::type() in your controller's beforeFilter() method.
318: * - If a helper with the same name as the extension exists, it is added to
319: * the controller.
320: * - If the extension is of a type that RequestHandler understands, it will
321: * set that Content-type in the response header.
322: *
323: * @param \Cake\Event\Event $event The Controller.beforeRender event.
324: * @return bool false if the render process should be aborted
325: * @throws \Cake\Http\Exception\NotFoundException If invoked extension is not configured.
326: */
327: public function beforeRender(Event $event)
328: {
329: /** @var \Cake\Controller\Controller $controller */
330: $controller = $event->getSubject();
331: $response = $controller->getResponse();
332: $request = $controller->getRequest();
333:
334: if ($this->ext && !in_array($this->ext, ['html', 'htm'])) {
335: if (!$response->getMimeType($this->ext)) {
336: throw new NotFoundException('Invoked extension not recognized/configured: ' . $this->ext);
337: }
338:
339: $this->renderAs($controller, $this->ext);
340: $response = $controller->response;
341: } else {
342: $response = $response->withCharset(Configure::read('App.encoding'));
343: }
344:
345: if ($this->_config['checkHttpCache'] &&
346: $response->checkNotModified($request)
347: ) {
348: $controller->setResponse($response);
349:
350: return false;
351: }
352: $controller->setResponse($response);
353: }
354:
355: /**
356: * Returns true if the current call accepts an XML response, false otherwise
357: *
358: * @return bool True if client accepts an XML response
359: * @deprecated 3.7.0 Use RequestHandler::prefers('xml') instead.
360: */
361: public function isXml()
362: {
363: deprecationWarning(
364: 'RequestHandlerComponent::isXml() is deprecated. Use RequestHandlerComponent::prefers(\'xml\') instead.'
365: );
366:
367: return $this->prefers('xml');
368: }
369:
370: /**
371: * Returns true if the current call accepts an RSS response, false otherwise
372: *
373: * @return bool True if client accepts an RSS response
374: * @deprecated 3.7.0 Use RequestHandler::prefers('rss') instead.
375: */
376: public function isRss()
377: {
378: deprecationWarning(
379: 'RequestHandlerComponent::isRss() is deprecated. Use RequestHandlerComponent::prefers(\'rss\') instead.'
380: );
381:
382: return $this->prefers('rss');
383: }
384:
385: /**
386: * Returns true if the current call accepts an Atom response, false otherwise
387: *
388: * @return bool True if client accepts an Atom response
389: * @deprecated 3.7.0 Use RequestHandler::prefers('atom') instead.
390: */
391: public function isAtom()
392: {
393: deprecationWarning(
394: 'RequestHandlerComponent::isAtom() is deprecated. Use RequestHandlerComponent::prefers(\'atom\') instead.'
395: );
396:
397: return $this->prefers('atom');
398: }
399:
400: /**
401: * Returns true if user agent string matches a mobile web browser, or if the
402: * client accepts WAP content.
403: *
404: * @return bool True if user agent is a mobile web browser
405: * @deprecated 3.7.0 Use ServerRequest::is('mobile') instead.
406: */
407: public function isMobile()
408: {
409: deprecationWarning(
410: 'RequestHandlerComponent::isMobile() is deprecated. Use ServerRequest::is(\'mobile\') instead.'
411: );
412:
413: $request = $this->getController()->getRequest();
414:
415: return $request->is('mobile') || $this->accepts('wap');
416: }
417:
418: /**
419: * Returns true if the client accepts WAP content
420: *
421: * @return bool
422: * @deprecated 3.7.0 Use RequestHandler::prefers('wap') instead.
423: */
424: public function isWap()
425: {
426: deprecationWarning(
427: 'RequestHandlerComponent::isWap() is deprecated. Use RequestHandlerComponent::prefers(\'wap\') instead.'
428: );
429:
430: return $this->prefers('wap');
431: }
432:
433: /**
434: * Determines which content types the client accepts. Acceptance is based on
435: * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
436: * header. Unlike Cake\Http\ServerRequest::accepts() this method deals entirely with mapped content types.
437: *
438: * Usage:
439: *
440: * ```
441: * $this->RequestHandler->accepts(['xml', 'html', 'json']);
442: * ```
443: *
444: * Returns true if the client accepts any of the supplied types.
445: *
446: * ```
447: * $this->RequestHandler->accepts('xml');
448: * ```
449: *
450: * Returns true if the client accepts xml.
451: *
452: * @param string|array|null $type Can be null (or no parameter), a string type name, or an
453: * array of types
454: * @return mixed If null or no parameter is passed, returns an array of content
455: * types the client accepts. If a string is passed, returns true
456: * if the client accepts it. If an array is passed, returns true
457: * if the client accepts one or more elements in the array.
458: */
459: public function accepts($type = null)
460: {
461: $controller = $this->getController();
462: $request = $controller->getRequest();
463: $response = $controller->getResponse();
464: $accepted = $request->accepts();
465:
466: if (!$type) {
467: return $response->mapType($accepted);
468: }
469: if (is_array($type)) {
470: foreach ($type as $t) {
471: $t = $this->mapAlias($t);
472: if (in_array($t, $accepted)) {
473: return true;
474: }
475: }
476:
477: return false;
478: }
479: if (is_string($type)) {
480: return in_array($this->mapAlias($type), $accepted);
481: }
482:
483: return false;
484: }
485:
486: /**
487: * Determines the content type of the data the client has sent (i.e. in a POST request)
488: *
489: * @param string|array|null $type Can be null (or no parameter), a string type name, or an array of types
490: * @return mixed If a single type is supplied a boolean will be returned. If no type is provided
491: * The mapped value of CONTENT_TYPE will be returned. If an array is supplied the first type
492: * in the request content type will be returned.
493: */
494: public function requestedWith($type = null)
495: {
496: $controller = $this->getController();
497: $request = $controller->getRequest();
498: $response = $controller->getResponse();
499:
500: if (!$request->is('post') &&
501: !$request->is('put') &&
502: !$request->is('patch') &&
503: !$request->is('delete')
504: ) {
505: return null;
506: }
507: if (is_array($type)) {
508: foreach ($type as $t) {
509: if ($this->requestedWith($t)) {
510: return $t;
511: }
512: }
513:
514: return false;
515: }
516:
517: list($contentType) = explode(';', $request->contentType());
518: if ($type === null) {
519: return $response->mapType($contentType);
520: }
521: if (is_string($type)) {
522: return ($type === $response->mapType($contentType));
523: }
524: }
525:
526: /**
527: * Determines which content-types the client prefers. If no parameters are given,
528: * the single content-type that the client most likely prefers is returned. If $type is
529: * an array, the first item in the array that the client accepts is returned.
530: * Preference is determined primarily by the file extension parsed by the Router
531: * if provided, and secondarily by the list of content-types provided in
532: * HTTP_ACCEPT.
533: *
534: * @param string|array|null $type An optional array of 'friendly' content-type names, i.e.
535: * 'html', 'xml', 'js', etc.
536: * @return mixed If $type is null or not provided, the first content-type in the
537: * list, based on preference, is returned. If a single type is provided
538: * a boolean will be returned if that type is preferred.
539: * If an array of types are provided then the first preferred type is returned.
540: * If no type is provided the first preferred type is returned.
541: */
542: public function prefers($type = null)
543: {
544: $controller = $this->getController();
545: $request = $controller->getRequest();
546: $response = $controller->getResponse();
547: $acceptRaw = $request->parseAccept();
548:
549: if (empty($acceptRaw)) {
550: return $type ? $type === $this->ext : $this->ext;
551: }
552: $accepts = $response->mapType(array_shift($acceptRaw));
553:
554: if (!$type) {
555: if (empty($this->ext) && !empty($accepts)) {
556: return $accepts[0];
557: }
558:
559: return $this->ext;
560: }
561:
562: $types = (array)$type;
563:
564: if (count($types) === 1) {
565: if ($this->ext) {
566: return in_array($this->ext, $types);
567: }
568:
569: return in_array($types[0], $accepts);
570: }
571:
572: $intersect = array_values(array_intersect($accepts, $types));
573: if (!$intersect) {
574: return false;
575: }
576:
577: return $intersect[0];
578: }
579:
580: /**
581: * Sets either the view class if one exists or the layout and template path of the view.
582: * The names of these are derived from the $type input parameter.
583: *
584: * ### Usage:
585: *
586: * Render the response as an 'ajax' response.
587: *
588: * ```
589: * $this->RequestHandler->renderAs($this, 'ajax');
590: * ```
591: *
592: * Render the response as an xml file and force the result as a file download.
593: *
594: * ```
595: * $this->RequestHandler->renderAs($this, 'xml', ['attachment' => 'myfile.xml'];
596: * ```
597: *
598: * @param \Cake\Controller\Controller $controller A reference to a controller object
599: * @param string $type Type of response to send (e.g: 'ajax')
600: * @param array $options Array of options to use
601: * @return void
602: * @see \Cake\Controller\Component\RequestHandlerComponent::respondAs()
603: */
604: public function renderAs(Controller $controller, $type, array $options = [])
605: {
606: $defaults = ['charset' => 'UTF-8'];
607: $viewClassMap = $this->getConfig('viewClassMap');
608:
609: if (Configure::read('App.encoding') !== null) {
610: $defaults['charset'] = Configure::read('App.encoding');
611: }
612: $options += $defaults;
613:
614: $builder = $controller->viewBuilder();
615: if (array_key_exists($type, $viewClassMap)) {
616: $view = $viewClassMap[$type];
617: } else {
618: $view = Inflector::classify($type);
619: }
620:
621: $viewClass = null;
622: if ($builder->getClassName() === null) {
623: $viewClass = App::className($view, 'View', 'View');
624: }
625:
626: if ($viewClass) {
627: $controller->viewClass = $viewClass;
628: $builder->setClassName($viewClass);
629: } else {
630: if (!$this->_renderType) {
631: $builder->setTemplatePath($builder->getTemplatePath() . DIRECTORY_SEPARATOR . $type);
632: } else {
633: $builder->setTemplatePath(preg_replace(
634: "/([\/\\\\]{$this->_renderType})$/",
635: DIRECTORY_SEPARATOR . $type,
636: $builder->getTemplatePath()
637: ));
638: }
639:
640: $this->_renderType = $type;
641: $builder->setLayoutPath($type);
642: }
643:
644: $response = $controller->response;
645: if ($response->getMimeType($type)) {
646: $this->respondAs($type, $options);
647: }
648: }
649:
650: /**
651: * Sets the response header based on type map index name. This wraps several methods
652: * available on Cake\Http\Response. It also allows you to use Content-Type aliases.
653: *
654: * @param string|array $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
655: * like 'application/x-shockwave'.
656: * @param array $options If $type is a friendly type name that is associated with
657: * more than one type of content, $index is used to select which content-type to use.
658: * @return bool Returns false if the friendly type name given in $type does
659: * not exist in the type map, or if the Content-type header has
660: * already been set by this method.
661: */
662: public function respondAs($type, array $options = [])
663: {
664: $defaults = ['index' => null, 'charset' => null, 'attachment' => false];
665: $options += $defaults;
666:
667: $cType = $type;
668: $controller = $this->getController();
669: $response = $controller->getResponse();
670: $request = $controller->getRequest();
671:
672: if (strpos($type, '/') === false) {
673: $cType = $response->getMimeType($type);
674: }
675: if (is_array($cType)) {
676: if (isset($cType[$options['index']])) {
677: $cType = $cType[$options['index']];
678: }
679:
680: if ($this->prefers($cType)) {
681: $cType = $this->prefers($cType);
682: } else {
683: $cType = $cType[0];
684: }
685: }
686:
687: if (!$type) {
688: return false;
689: }
690: if (!$request->getParam('requested')) {
691: $response = $response->withType($cType);
692: }
693: if (!empty($options['charset'])) {
694: $response = $response->withCharset($options['charset']);
695: }
696: if (!empty($options['attachment'])) {
697: $response = $response->withDownload($options['attachment']);
698: }
699: $controller->setResponse($response);
700:
701: return true;
702: }
703:
704: /**
705: * Returns the current response type (Content-type header), or null if not alias exists
706: *
707: * @return mixed A string content type alias, or raw content type if no alias map exists,
708: * otherwise null
709: * @deprecated 3.7.0 Use $response->mapType($response->getType()) instead.
710: */
711: public function responseType()
712: {
713: deprecationWarning(
714: 'RequestHandlerComponent::responseType() is deprecated. Use $response->mapType($response->getType()) instead.'
715: );
716:
717: $response = $this->getController()->response;
718:
719: return $response->mapType($response->getType());
720: }
721:
722: /**
723: * Maps a content type alias back to its mime-type(s)
724: *
725: * @param string|array $alias String alias to convert back into a content type. Or an array of aliases to map.
726: * @return string|array|null Null on an undefined alias. String value of the mapped alias type. If an
727: * alias maps to more than one content type, the first one will be returned. If an array is provided
728: * for $alias, an array of mapped types will be returned.
729: */
730: public function mapAlias($alias)
731: {
732: if (is_array($alias)) {
733: return array_map([$this, 'mapAlias'], $alias);
734: }
735: $response = $this->getController()->response;
736: $type = $response->getMimeType($alias);
737: if ($type) {
738: if (is_array($type)) {
739: return $type[0];
740: }
741:
742: return $type;
743: }
744:
745: return null;
746: }
747:
748: /**
749: * Add a new mapped input type. Mapped input types are automatically
750: * converted by RequestHandlerComponent during the startup() callback.
751: *
752: * @param string $type The type alias being converted, ie. json
753: * @param array $handler The handler array for the type. The first index should
754: * be the handling callback, all other arguments should be additional parameters
755: * for the handler.
756: * @return void
757: * @throws \Cake\Core\Exception\Exception
758: * @deprecated 3.1.0 Use setConfig('addInputType', ...) instead.
759: */
760: public function addInputType($type, $handler)
761: {
762: deprecationWarning(
763: 'RequestHandlerComponent::addInputType() is deprecated. Use setConfig("inputTypeMap", ...) instead.'
764: );
765: if (!is_array($handler) || !isset($handler[0]) || !is_callable($handler[0])) {
766: throw new Exception('You must give a handler callback.');
767: }
768: $this->setConfig('inputTypeMap.' . $type, $handler);
769: }
770:
771: /**
772: * Getter/setter for viewClassMap
773: *
774: * @param array|string|null $type The type string or array with format `['type' => 'viewClass']` to map one or more
775: * @param array|null $viewClass The viewClass to be used for the type without `View` appended
776: * @return array|string Returns viewClass when only string $type is set, else array with viewClassMap
777: * @deprecated 3.1.0 Use setConfig('viewClassMap', ...) instead.
778: */
779: public function viewClassMap($type = null, $viewClass = null)
780: {
781: deprecationWarning(
782: 'RequestHandlerComponent::viewClassMap() is deprecated. Use setConfig("viewClassMap", ...) instead.'
783: );
784: if (!$viewClass && is_string($type)) {
785: return $this->getConfig('viewClassMap.' . $type);
786: }
787: if (is_string($type)) {
788: $this->setConfig('viewClassMap.' . $type, $viewClass);
789: } elseif (is_array($type)) {
790: $this->setConfig('viewClassMap', $type, true);
791: }
792:
793: return $this->getConfig('viewClassMap');
794: }
795: }
796: