RequestHandlerComponent.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  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
  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
  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 (!empty($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 (isset($request->params['_ext'])) {
  188. $this->ext = $request->params['_ext'];
  189. }
  190. if (empty($this->ext) || in_array($this->ext, ['html', 'htm'])) {
  191. $this->_setExtension($request, $this->response);
  192. }
  193. $request->params['isAjax'] = $request->is('ajax');
  194. if (empty($this->ext) && $request->params['isAjax']) {
  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') == false) {
  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. $response->statusCode(200);
  270. return $response;
  271. }
  272. /**
  273. * Checks if the response can be considered different according to the request
  274. * headers, and the caching response headers. If it was not modified, then the
  275. * render process is skipped. And the client will get a blank response with a
  276. * "304 Not Modified" header.
  277. *
  278. * - If Router::extensions() is enabled, the layout and template type are
  279. * switched based on the parsed extension or `Accept` header. For example,
  280. * if `controller/action.xml` is requested, the view path becomes
  281. * `app/View/Controller/xml/action.ctp`. Also if `controller/action` is
  282. * requested with `Accept: application/xml` in the headers the view
  283. * path will become `app/View/Controller/xml/action.ctp`. Layout and template
  284. * types will only switch to mime-types recognized by Cake\Network\Response.
  285. * If you need to declare additional mime-types, you can do so using
  286. * Cake\Network\Response::type() in your controller's beforeFilter() method.
  287. * - If a helper with the same name as the extension exists, it is added to
  288. * the controller.
  289. * - If the extension is of a type that RequestHandler understands, it will
  290. * set that Content-type in the response header.
  291. *
  292. * @param \Cake\Event\Event $event The Controller.beforeRender event.
  293. * @return bool false if the render process should be aborted
  294. */
  295. public function beforeRender(Event $event)
  296. {
  297. $isRecognized = (
  298. !in_array($this->ext, ['html', 'htm']) &&
  299. $this->response->getMimeType($this->ext)
  300. );
  301. if (!empty($this->ext) && $isRecognized) {
  302. $this->renderAs($event->subject(), $this->ext);
  303. } else {
  304. $this->response->charset(Configure::read('App.encoding'));
  305. }
  306. if ($this->_config['checkHttpCache'] &&
  307. $this->response->checkNotModified($this->request)
  308. ) {
  309. return false;
  310. }
  311. }
  312. /**
  313. * Returns true if the current call accepts an XML response, false otherwise
  314. *
  315. * @return bool True if client accepts an XML response
  316. */
  317. public function isXml()
  318. {
  319. return $this->prefers('xml');
  320. }
  321. /**
  322. * Returns true if the current call accepts an RSS response, false otherwise
  323. *
  324. * @return bool True if client accepts an RSS response
  325. */
  326. public function isRss()
  327. {
  328. return $this->prefers('rss');
  329. }
  330. /**
  331. * Returns true if the current call accepts an Atom response, false otherwise
  332. *
  333. * @return bool True if client accepts an RSS response
  334. */
  335. public function isAtom()
  336. {
  337. return $this->prefers('atom');
  338. }
  339. /**
  340. * Returns true if user agent string matches a mobile web browser, or if the
  341. * client accepts WAP content.
  342. *
  343. * @return bool True if user agent is a mobile web browser
  344. */
  345. public function isMobile()
  346. {
  347. $request = $this->request;
  348. return $request->is('mobile') || $this->accepts('wap');
  349. }
  350. /**
  351. * Returns true if the client accepts WAP content
  352. *
  353. * @return bool
  354. */
  355. public function isWap()
  356. {
  357. return $this->prefers('wap');
  358. }
  359. /**
  360. * Determines which content types the client accepts. Acceptance is based on
  361. * the file extension parsed by the Router (if present), and by the HTTP_ACCEPT
  362. * header. Unlike Cake\Network\Request::accepts() this method deals entirely with mapped content types.
  363. *
  364. * Usage:
  365. *
  366. * ```
  367. * $this->RequestHandler->accepts(['xml', 'html', 'json']);
  368. * ```
  369. *
  370. * Returns true if the client accepts any of the supplied types.
  371. *
  372. * ```
  373. * $this->RequestHandler->accepts('xml');
  374. * ```
  375. *
  376. * Returns true if the client accepts xml.
  377. *
  378. * @param string|array|null $type Can be null (or no parameter), a string type name, or an
  379. * array of types
  380. * @return mixed If null or no parameter is passed, returns an array of content
  381. * types the client accepts. If a string is passed, returns true
  382. * if the client accepts it. If an array is passed, returns true
  383. * if the client accepts one or more elements in the array.
  384. */
  385. public function accepts($type = null)
  386. {
  387. $request = $this->request;
  388. $response = $this->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. $request = $this->request;
  418. if (!$request->is('post') &&
  419. !$request->is('put') &&
  420. !$request->is('patch') &&
  421. !$request->is('delete')
  422. ) {
  423. return null;
  424. }
  425. if (is_array($type)) {
  426. foreach ($type as $t) {
  427. if ($this->requestedWith($t)) {
  428. return $t;
  429. }
  430. }
  431. return false;
  432. }
  433. list($contentType) = explode(';', $request->contentType());
  434. $response = $this->response;
  435. if ($type === null) {
  436. return $response->mapType($contentType);
  437. }
  438. if (is_string($type)) {
  439. return ($type === $response->mapType($contentType));
  440. }
  441. }
  442. /**
  443. * Determines which content-types the client prefers. If no parameters are given,
  444. * the single content-type that the client most likely prefers is returned. If $type is
  445. * an array, the first item in the array that the client accepts is returned.
  446. * Preference is determined primarily by the file extension parsed by the Router
  447. * if provided, and secondarily by the list of content-types provided in
  448. * HTTP_ACCEPT.
  449. *
  450. * @param string|array|null $type An optional array of 'friendly' content-type names, i.e.
  451. * 'html', 'xml', 'js', etc.
  452. * @return mixed If $type is null or not provided, the first content-type in the
  453. * list, based on preference, is returned. If a single type is provided
  454. * a boolean will be returned if that type is preferred.
  455. * If an array of types are provided then the first preferred type is returned.
  456. * If no type is provided the first preferred type is returned.
  457. */
  458. public function prefers($type = null)
  459. {
  460. $request = $this->request;
  461. $response = $this->response;
  462. $acceptRaw = $request->parseAccept();
  463. if (empty($acceptRaw)) {
  464. return $this->ext;
  465. }
  466. $accepts = $response->mapType(array_shift($acceptRaw));
  467. if (!$type) {
  468. if (empty($this->ext) && !empty($accepts)) {
  469. return $accepts[0];
  470. }
  471. return $this->ext;
  472. }
  473. $types = (array)$type;
  474. if (count($types) === 1) {
  475. if (!empty($this->ext)) {
  476. return in_array($this->ext, $types);
  477. }
  478. return in_array($types[0], $accepts);
  479. }
  480. $intersect = array_values(array_intersect($accepts, $types));
  481. if (empty($intersect)) {
  482. return false;
  483. }
  484. return $intersect[0];
  485. }
  486. /**
  487. * Sets either the view class if one exists or the layout and template path of the view.
  488. * The names of these are derived from the $type input parameter.
  489. *
  490. * ### Usage:
  491. *
  492. * Render the response as an 'ajax' response.
  493. *
  494. * ```
  495. * $this->RequestHandler->renderAs($this, 'ajax');
  496. * ```
  497. *
  498. * Render the response as an xml file and force the result as a file download.
  499. *
  500. * ```
  501. * $this->RequestHandler->renderAs($this, 'xml', ['attachment' => 'myfile.xml'];
  502. * ```
  503. *
  504. * @param \Cake\Controller\Controller $controller A reference to a controller object
  505. * @param string $type Type of response to send (e.g: 'ajax')
  506. * @param array $options Array of options to use
  507. * @return void
  508. * @see \Cake\Controller\Component\RequestHandlerComponent::respondAs()
  509. */
  510. public function renderAs(Controller $controller, $type, array $options = [])
  511. {
  512. $defaults = ['charset' => 'UTF-8'];
  513. $viewClassMap = $this->config('viewClassMap');
  514. if (Configure::read('App.encoding') !== null) {
  515. $defaults['charset'] = Configure::read('App.encoding');
  516. }
  517. $options += $defaults;
  518. $builder = $controller->viewBuilder();
  519. if (array_key_exists($type, $viewClassMap)) {
  520. $view = $viewClassMap[$type];
  521. } else {
  522. $view = Inflector::classify($type);
  523. }
  524. $viewClass = null;
  525. if ($builder->className() === null) {
  526. $viewClass = App::className($view, 'View', 'View');
  527. }
  528. if ($viewClass) {
  529. $controller->viewClass = $viewClass;
  530. $builder->className($viewClass);
  531. } else {
  532. if (empty($this->_renderType)) {
  533. $builder->templatePath($builder->templatePath() . DIRECTORY_SEPARATOR . $type);
  534. } else {
  535. $builder->templatePath(preg_replace(
  536. "/([\/\\\\]{$this->_renderType})$/",
  537. DIRECTORY_SEPARATOR . $type,
  538. $builder->templatePath()
  539. ));
  540. }
  541. $this->_renderType = $type;
  542. $builder->layoutPath($type);
  543. }
  544. $response = $this->response;
  545. if ($response->getMimeType($type)) {
  546. $this->respondAs($type, $options);
  547. }
  548. $helper = ucfirst($type);
  549. if (!in_array($helper, $controller->helpers) && empty($controller->helpers[$helper])) {
  550. $helperClass = App::className($helper, 'View/Helper', 'Helper');
  551. if ($helperClass !== false) {
  552. $controller->helpers[] = $helper;
  553. }
  554. }
  555. }
  556. /**
  557. * Sets the response header based on type map index name. This wraps several methods
  558. * available on Cake\Network\Response. It also allows you to use Content-Type aliases.
  559. *
  560. * @param string|array $type Friendly type name, i.e. 'html' or 'xml', or a full content-type,
  561. * like 'application/x-shockwave'.
  562. * @param array $options If $type is a friendly type name that is associated with
  563. * more than one type of content, $index is used to select which content-type to use.
  564. * @return bool Returns false if the friendly type name given in $type does
  565. * not exist in the type map, or if the Content-type header has
  566. * already been set by this method.
  567. */
  568. public function respondAs($type, array $options = [])
  569. {
  570. $defaults = ['index' => null, 'charset' => null, 'attachment' => false];
  571. $options += $defaults;
  572. $cType = $type;
  573. $response = $this->response;
  574. if (strpos($type, '/') === false) {
  575. $cType = $response->getMimeType($type);
  576. }
  577. if (is_array($cType)) {
  578. if (isset($cType[$options['index']])) {
  579. $cType = $cType[$options['index']];
  580. }
  581. if ($this->prefers($cType)) {
  582. $cType = $this->prefers($cType);
  583. } else {
  584. $cType = $cType[0];
  585. }
  586. }
  587. if (!$type) {
  588. return false;
  589. }
  590. if (empty($this->request->params['requested'])) {
  591. $response->type($cType);
  592. }
  593. if (!empty($options['charset'])) {
  594. $response->charset($options['charset']);
  595. }
  596. if (!empty($options['attachment'])) {
  597. $response->download($options['attachment']);
  598. }
  599. return true;
  600. }
  601. /**
  602. * Returns the current response type (Content-type header), or null if not alias exists
  603. *
  604. * @return mixed A string content type alias, or raw content type if no alias map exists,
  605. * otherwise null
  606. */
  607. public function responseType()
  608. {
  609. $response = $this->response;
  610. return $response->mapType($response->type());
  611. }
  612. /**
  613. * Maps a content type alias back to its mime-type(s)
  614. *
  615. * @param string|array $alias String alias to convert back into a content type. Or an array of aliases to map.
  616. * @return string|null|array Null on an undefined alias. String value of the mapped alias type. If an
  617. * alias maps to more than one content type, the first one will be returned. If an array is provided
  618. * for $alias, an array of mapped types will be returned.
  619. */
  620. public function mapAlias($alias)
  621. {
  622. if (is_array($alias)) {
  623. return array_map([$this, 'mapAlias'], $alias);
  624. }
  625. $response = $this->response;
  626. $type = $response->getMimeType($alias);
  627. if ($type) {
  628. if (is_array($type)) {
  629. return $type[0];
  630. }
  631. return $type;
  632. }
  633. return null;
  634. }
  635. /**
  636. * Add a new mapped input type. Mapped input types are automatically
  637. * converted by RequestHandlerComponent during the startup() callback.
  638. *
  639. * @param string $type The type alias being converted, ie. json
  640. * @param array $handler The handler array for the type. The first index should
  641. * be the handling callback, all other arguments should be additional parameters
  642. * for the handler.
  643. * @return void
  644. * @throws \Cake\Core\Exception\Exception
  645. * @deprecated 3.1.0 Use config('addInputType', ...) instead.
  646. */
  647. public function addInputType($type, $handler)
  648. {
  649. trigger_error(
  650. 'RequestHandlerComponent::addInputType() is deprecated. Use config("inputTypeMap", ...) instead.',
  651. E_USER_DEPRECATED
  652. );
  653. if (!is_array($handler) || !isset($handler[0]) || !is_callable($handler[0])) {
  654. throw new Exception('You must give a handler callback.');
  655. }
  656. $this->config('inputTypeMap.' . $type, $handler);
  657. }
  658. /**
  659. * Getter/setter for viewClassMap
  660. *
  661. * @param array|string|null $type The type string or array with format `['type' => 'viewClass']` to map one or more
  662. * @param array|null $viewClass The viewClass to be used for the type without `View` appended
  663. * @return array|string Returns viewClass when only string $type is set, else array with viewClassMap
  664. * @deprecated 3.1.0 Use config('viewClassMap', ...) instead.
  665. */
  666. public function viewClassMap($type = null, $viewClass = null)
  667. {
  668. trigger_error(
  669. 'RequestHandlerComponent::viewClassMap() is deprecated. Use config("viewClassMap", ...) instead.',
  670. E_USER_DEPRECATED
  671. );
  672. if (!$viewClass && is_string($type)) {
  673. return $this->config('viewClassMap.' . $type);
  674. }
  675. if (is_string($type)) {
  676. $this->config('viewClassMap.' . $type, $viewClass);
  677. } elseif (is_array($type)) {
  678. $this->config('viewClassMap', $type, true);
  679. }
  680. return $this->config('viewClassMap');
  681. }
  682. }