RequestHandlerComponent.php 25 KB

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