RequestHandlerComponent.php 25 KB

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