Controller.php 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  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.2.9
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Controller;
  16. use Cake\Controller\Exception\MissingActionException;
  17. use Cake\Datasource\ModelAwareTrait;
  18. use Cake\Event\Event;
  19. use Cake\Event\EventDispatcherInterface;
  20. use Cake\Event\EventDispatcherTrait;
  21. use Cake\Event\EventListenerInterface;
  22. use Cake\Http\Response;
  23. use Cake\Http\ServerRequest;
  24. use Cake\Log\LogTrait;
  25. use Cake\ORM\Locator\LocatorAwareTrait;
  26. use Cake\Routing\RequestActionTrait;
  27. use Cake\Routing\Router;
  28. use Cake\Utility\MergeVariablesTrait;
  29. use Cake\View\ViewVarsTrait;
  30. use LogicException;
  31. use ReflectionClass;
  32. use ReflectionException;
  33. use ReflectionMethod;
  34. use RuntimeException;
  35. /**
  36. * Application controller class for organization of business logic.
  37. * Provides basic functionality, such as rendering views inside layouts,
  38. * automatic model availability, redirection, callbacks, and more.
  39. *
  40. * Controllers should provide a number of 'action' methods. These are public
  41. * methods on a controller that are not inherited from `Controller`.
  42. * Each action serves as an endpoint for performing a specific action on a
  43. * resource or collection of resources. For example adding or editing a new
  44. * object, or listing a set of objects.
  45. *
  46. * You can access request parameters, using `$this->request`. The request object
  47. * contains all the POST, GET and FILES that were part of the request.
  48. *
  49. * After performing the required action, controllers are responsible for
  50. * creating a response. This usually takes the form of a generated `View`, or
  51. * possibly a redirection to another URL. In either case `$this->response`
  52. * allows you to manipulate all aspects of the response.
  53. *
  54. * Controllers are created by `Dispatcher` based on request parameters and
  55. * routing. By default controllers and actions use conventional names.
  56. * For example `/posts/index` maps to `PostsController::index()`. You can re-map
  57. * URLs using Router::connect() or RouterBuilder::connect().
  58. *
  59. * ### Life cycle callbacks
  60. *
  61. * CakePHP fires a number of life cycle callbacks during each request.
  62. * By implementing a method you can receive the related events. The available
  63. * callbacks are:
  64. *
  65. * - `beforeFilter(Event $event)`
  66. * Called before each action. This is a good place to do general logic that
  67. * applies to all actions.
  68. * - `beforeRender(Event $event)`
  69. * Called before the view is rendered.
  70. * - `beforeRedirect(Event $event, $url, Response $response)`
  71. * Called before a redirect is done.
  72. * - `afterFilter(Event $event)`
  73. * Called after each action is complete and after the view is rendered.
  74. *
  75. * @property \Cake\Controller\Component\AuthComponent $Auth
  76. * @property \Cake\Controller\Component\CookieComponent $Cookie
  77. * @property \Cake\Controller\Component\CsrfComponent $Csrf
  78. * @property \Cake\Controller\Component\FlashComponent $Flash
  79. * @property \Cake\Controller\Component\PaginatorComponent $Paginator
  80. * @property \Cake\Controller\Component\RequestHandlerComponent $RequestHandler
  81. * @property \Cake\Controller\Component\SecurityComponent $Security
  82. * @method bool isAuthorized($user)
  83. * @link https://book.cakephp.org/3.0/en/controllers.html
  84. */
  85. class Controller implements EventListenerInterface, EventDispatcherInterface
  86. {
  87. use EventDispatcherTrait;
  88. use LocatorAwareTrait;
  89. use LogTrait;
  90. use MergeVariablesTrait;
  91. use ModelAwareTrait;
  92. use RequestActionTrait;
  93. use ViewVarsTrait;
  94. /**
  95. * The name of this controller. Controller names are plural, named after the model they manipulate.
  96. *
  97. * Set automatically using conventions in Controller::__construct().
  98. *
  99. * @var string
  100. */
  101. protected $name;
  102. /**
  103. * An array containing the names of helpers this controller uses. The array elements should
  104. * not contain the "Helper" part of the class name.
  105. *
  106. * Example:
  107. * ```
  108. * public $helpers = ['Form', 'Html', 'Time'];
  109. * ```
  110. *
  111. * @var array
  112. * @link https://book.cakephp.org/3.0/en/controllers.html#configuring-helpers-to-load
  113. *
  114. * @deprecated 3.0.0 You should configure helpers in your AppView::initialize() method.
  115. */
  116. public $helpers = [];
  117. /**
  118. * An instance of a \Cake\Http\ServerRequest object that contains information about the current request.
  119. * This object contains all the information about a request and several methods for reading
  120. * additional information about the request.
  121. *
  122. * @var \Cake\Http\ServerRequest
  123. * @link https://book.cakephp.org/3.0/en/controllers/request-response.html#request
  124. * @deprecated 3.6.0 The property will become protected in 4.0.0. Use getRequest()/setRequest instead.
  125. */
  126. public $request;
  127. /**
  128. * An instance of a Response object that contains information about the impending response
  129. *
  130. * @var \Cake\Http\Response
  131. * @link https://book.cakephp.org/3.0/en/controllers/request-response.html#response
  132. * @deprecated 3.6.0 The property will become protected in 4.0.0. Use getResponse()/setResponse instead.
  133. */
  134. public $response;
  135. /**
  136. * The class name to use for creating the response object.
  137. *
  138. * @var string
  139. */
  140. protected $_responseClass = 'Cake\Http\Response';
  141. /**
  142. * Settings for pagination.
  143. *
  144. * Used to pre-configure pagination preferences for the various
  145. * tables your controller will be paginating.
  146. *
  147. * @var array
  148. * @see \Cake\Controller\Component\PaginatorComponent
  149. */
  150. public $paginate = [];
  151. /**
  152. * Set to true to automatically render the view
  153. * after action logic.
  154. *
  155. * @var bool
  156. */
  157. protected $autoRender = true;
  158. /**
  159. * Instance of ComponentRegistry used to create Components
  160. *
  161. * @var \Cake\Controller\ComponentRegistry
  162. */
  163. protected $_components;
  164. /**
  165. * Array containing the names of components this controller uses. Component names
  166. * should not contain the "Component" portion of the class name.
  167. *
  168. * Example:
  169. * ```
  170. * public $components = ['RequestHandler', 'Acl'];
  171. * ```
  172. *
  173. * @var array
  174. * @link https://book.cakephp.org/3.0/en/controllers/components.html
  175. *
  176. * @deprecated 3.0.0 You should configure components in your Controller::initialize() method.
  177. */
  178. public $components = [];
  179. /**
  180. * Instance of the View created during rendering. Won't be set until after
  181. * Controller::render() is called.
  182. *
  183. * @var \Cake\View\View
  184. * @deprecated 3.1.0 Use viewBuilder() instead.
  185. */
  186. public $View;
  187. /**
  188. * These Controller properties will be passed from the Controller to the View as options.
  189. *
  190. * @var array
  191. * @see \Cake\View\View
  192. */
  193. protected $_validViewOptions = [
  194. 'passedArgs'
  195. ];
  196. /**
  197. * Automatically set to the name of a plugin.
  198. *
  199. * @var string|null
  200. */
  201. protected $plugin;
  202. /**
  203. * Holds all passed params.
  204. *
  205. * @var array
  206. * @deprecated 3.1.0 Use `$this->request->getParam('pass')` instead.
  207. */
  208. public $passedArgs = [];
  209. /**
  210. * Constructor.
  211. *
  212. * Sets a number of properties based on conventions if they are empty. To override the
  213. * conventions CakePHP uses you can define properties in your class declaration.
  214. *
  215. * @param \Cake\Http\ServerRequest|null $request Request object for this controller. Can be null for testing,
  216. * but expect that features that use the request parameters will not work.
  217. * @param \Cake\Http\Response|null $response Response object for this controller.
  218. * @param string|null $name Override the name useful in testing when using mocks.
  219. * @param \Cake\Event\EventManager|null $eventManager The event manager. Defaults to a new instance.
  220. * @param \Cake\Controller\ComponentRegistry|null $components The component registry. Defaults to a new instance.
  221. */
  222. public function __construct(ServerRequest $request = null, Response $response = null, $name = null, $eventManager = null, $components = null)
  223. {
  224. if ($name !== null) {
  225. $this->name = $name;
  226. }
  227. if ($this->name === null && $request && $request->getParam('controller')) {
  228. $this->name = $request->getParam('controller');
  229. }
  230. if ($this->name === null) {
  231. list(, $name) = namespaceSplit(get_class($this));
  232. $this->name = substr($name, 0, -10);
  233. }
  234. $this->setRequest($request ?: new ServerRequest());
  235. $this->setResponse($response ?: new Response());
  236. if ($eventManager !== null) {
  237. $this->setEventManager($eventManager);
  238. }
  239. $this->modelFactory('Table', [$this->getTableLocator(), 'get']);
  240. $plugin = $this->request->getParam('plugin');
  241. $modelClass = ($plugin ? $plugin . '.' : '') . $this->name;
  242. $this->_setModelClass($modelClass);
  243. if ($components !== null) {
  244. $this->components($components);
  245. }
  246. $this->initialize();
  247. $this->_mergeControllerVars();
  248. $this->_loadComponents();
  249. $this->getEventManager()->on($this);
  250. }
  251. /**
  252. * Initialization hook method.
  253. *
  254. * Implement this method to avoid having to overwrite
  255. * the constructor and call parent.
  256. *
  257. * @return void
  258. */
  259. public function initialize()
  260. {
  261. }
  262. /**
  263. * Get the component registry for this controller.
  264. *
  265. * If called with the first parameter, it will be set as the controller $this->_components property
  266. *
  267. * @param \Cake\Controller\ComponentRegistry|null $components Component registry.
  268. *
  269. * @return \Cake\Controller\ComponentRegistry
  270. */
  271. public function components($components = null)
  272. {
  273. if ($components === null && $this->_components === null) {
  274. $this->_components = new ComponentRegistry($this);
  275. }
  276. if ($components !== null) {
  277. $components->setController($this);
  278. $this->_components = $components;
  279. }
  280. return $this->_components;
  281. }
  282. /**
  283. * Add a component to the controller's registry.
  284. *
  285. * This method will also set the component to a property.
  286. * For example:
  287. *
  288. * ```
  289. * $this->loadComponent('Acl.Acl');
  290. * ```
  291. *
  292. * Will result in a `Toolbar` property being set.
  293. *
  294. * @param string $name The name of the component to load.
  295. * @param array $config The config for the component.
  296. * @return \Cake\Controller\Component
  297. */
  298. public function loadComponent($name, array $config = [])
  299. {
  300. list(, $prop) = pluginSplit($name);
  301. return $this->{$prop} = $this->components()->load($name, $config);
  302. }
  303. /**
  304. * Magic accessor for model autoloading.
  305. *
  306. * @param string $name Property name
  307. * @return bool|object The model instance or false
  308. */
  309. public function __get($name)
  310. {
  311. $deprecated = [
  312. 'name' => 'getName',
  313. 'plugin' => 'getPlugin',
  314. 'autoRender' => 'isAutoRenderEnabled',
  315. ];
  316. if (isset($deprecated[$name])) {
  317. $method = $deprecated[$name];
  318. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s instead.', $name, $method));
  319. return $this->{$method}();
  320. }
  321. $deprecated = [
  322. 'layout' => 'getLayout',
  323. 'view' => 'getTemplate',
  324. 'theme' => 'getTheme',
  325. 'autoLayout' => 'isAutoLayoutEnabled',
  326. 'viewPath' => 'getTemplatePath',
  327. 'layoutPath' => 'getLayoutPath',
  328. ];
  329. if (isset($deprecated[$name])) {
  330. $method = $deprecated[$name];
  331. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
  332. return $this->viewBuilder()->{$method}();
  333. }
  334. list($plugin, $class) = pluginSplit($this->modelClass, true);
  335. if ($class !== $name) {
  336. return false;
  337. }
  338. return $this->loadModel($plugin . $class);
  339. }
  340. /**
  341. * Magic setter for removed properties.
  342. *
  343. * @param string $name Property name.
  344. * @param mixed $value Value to set.
  345. * @return void
  346. */
  347. public function __set($name, $value)
  348. {
  349. $deprecated = [
  350. 'name' => 'setName',
  351. 'plugin' => 'setPlugin'
  352. ];
  353. if (isset($deprecated[$name])) {
  354. $method = $deprecated[$name];
  355. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s() instead.', $name, $method));
  356. $this->{$method}($value);
  357. return;
  358. }
  359. if ($name === 'autoRender') {
  360. $value ? $this->enableAutoRender() : $this->disableAutoRender();
  361. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->enableAutoRender/disableAutoRender() instead.', $name));
  362. return;
  363. }
  364. $deprecated = [
  365. 'layout' => 'setLayout',
  366. 'view' => 'setTemplate',
  367. 'theme' => 'setTheme',
  368. 'autoLayout' => 'enableAutoLayout',
  369. 'viewPath' => 'setTemplatePath',
  370. 'layoutPath' => 'setLayoutPath',
  371. ];
  372. if (isset($deprecated[$name])) {
  373. $method = $deprecated[$name];
  374. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
  375. $this->viewBuilder()->{$method}($value);
  376. return;
  377. }
  378. $this->{$name} = $value;
  379. }
  380. /**
  381. * Returns the controller name.
  382. *
  383. * @return string
  384. * @since 3.6.0
  385. */
  386. public function getName()
  387. {
  388. return $this->name;
  389. }
  390. /**
  391. * Sets the controller name.
  392. *
  393. * @param string $name Controller name.
  394. * @return $this
  395. * @since 3.6.0
  396. */
  397. public function setName($name)
  398. {
  399. $this->name = $name;
  400. return $this;
  401. }
  402. /**
  403. * Returns the plugin name.
  404. *
  405. * @return string|null
  406. * @since 3.6.0
  407. */
  408. public function getPlugin()
  409. {
  410. return $this->plugin;
  411. }
  412. /**
  413. * Sets the plugin name.
  414. *
  415. * @param string $name Plugin name.
  416. * @return $this
  417. * @since 3.6.0
  418. */
  419. public function setPlugin($name)
  420. {
  421. $this->plugin = $name;
  422. return $this;
  423. }
  424. /**
  425. * Returns true if an action should be rendered automatically.
  426. *
  427. * @return bool
  428. * @since 3.6.0
  429. */
  430. public function isAutoRenderEnabled()
  431. {
  432. return $this->autoRender;
  433. }
  434. /**
  435. * Enable automatic action rendering.
  436. *
  437. * @return $this
  438. * @since 3.6.0
  439. */
  440. public function enableAutoRender()
  441. {
  442. $this->autoRender = true;
  443. return $this;
  444. }
  445. /**
  446. * Disbale automatic action rendering.
  447. *
  448. * @return $this
  449. * @since 3.6.0
  450. */
  451. public function disableAutoRender()
  452. {
  453. $this->autoRender = false;
  454. return $this;
  455. }
  456. /**
  457. * Gets the request instance.
  458. *
  459. * @return \Cake\Http\ServerRequest
  460. * @since 3.6.0
  461. */
  462. public function getRequest()
  463. {
  464. return $this->request;
  465. }
  466. /**
  467. * Sets the request objects and configures a number of controller properties
  468. * based on the contents of the request. Controller acts as a proxy for certain View variables
  469. * which must also be updated here. The properties that get set are:
  470. *
  471. * - $this->request - To the $request parameter
  472. * - $this->passedArgs - Same as $request->params['pass]
  473. *
  474. * @param \Cake\Http\ServerRequest $request Request instance.
  475. * @return $this
  476. */
  477. public function setRequest(ServerRequest $request)
  478. {
  479. $this->request = $request;
  480. $this->plugin = $request->getParam('plugin') ?: null;
  481. if ($request->getParam('pass')) {
  482. $this->passedArgs = $request->getParam('pass');
  483. }
  484. return $this;
  485. }
  486. /**
  487. * Gets the response instance.
  488. *
  489. * @return \Cake\Http\Response
  490. * @since 3.6.0
  491. */
  492. public function getResponse()
  493. {
  494. return $this->response;
  495. }
  496. /**
  497. * Sets the response instance.
  498. *
  499. * @param \Cake\Http\Response $response Response instance.
  500. * @return $this
  501. * @since 3.6.0
  502. */
  503. public function setResponse(Response $response)
  504. {
  505. $this->response = $response;
  506. return $this;
  507. }
  508. /**
  509. * Dispatches the controller action. Checks that the action
  510. * exists and isn't private.
  511. *
  512. * @return mixed The resulting response.
  513. * @throws \LogicException When request is not set.
  514. * @throws \Cake\Controller\Exception\MissingActionException When actions are not defined or inaccessible.
  515. */
  516. public function invokeAction()
  517. {
  518. $request = $this->request;
  519. if (!isset($request)) {
  520. throw new LogicException('No Request object configured. Cannot invoke action');
  521. }
  522. if (!$this->isAction($request->getParam('action'))) {
  523. throw new MissingActionException([
  524. 'controller' => $this->name . 'Controller',
  525. 'action' => $request->getParam('action'),
  526. 'prefix' => $request->getParam('prefix') ?: '',
  527. 'plugin' => $request->getParam('plugin'),
  528. ]);
  529. }
  530. /* @var callable $callable */
  531. $callable = [$this, $request->getParam('action')];
  532. return $callable(...array_values($request->getParam('pass')));
  533. }
  534. /**
  535. * Merge components, helpers vars from
  536. * parent classes.
  537. *
  538. * @return void
  539. */
  540. protected function _mergeControllerVars()
  541. {
  542. $this->_mergeVars(
  543. ['components', 'helpers'],
  544. ['associative' => ['components', 'helpers']]
  545. );
  546. }
  547. /**
  548. * Returns a list of all events that will fire in the controller during its lifecycle.
  549. * You can override this function to add your own listener callbacks
  550. *
  551. * @return array
  552. */
  553. public function implementedEvents()
  554. {
  555. return [
  556. 'Controller.initialize' => 'beforeFilter',
  557. 'Controller.beforeRender' => 'beforeRender',
  558. 'Controller.beforeRedirect' => 'beforeRedirect',
  559. 'Controller.shutdown' => 'afterFilter',
  560. ];
  561. }
  562. /**
  563. * Loads the defined components using the Component factory.
  564. *
  565. * @return void
  566. */
  567. protected function _loadComponents()
  568. {
  569. if (empty($this->components)) {
  570. return;
  571. }
  572. $registry = $this->components();
  573. $components = $registry->normalizeArray($this->components);
  574. foreach ($components as $properties) {
  575. $this->loadComponent($properties['class'], $properties['config']);
  576. }
  577. }
  578. /**
  579. * Perform the startup process for this controller.
  580. * Fire the Components and Controller callbacks in the correct order.
  581. *
  582. * - Initializes components, which fires their `initialize` callback
  583. * - Calls the controller `beforeFilter`.
  584. * - triggers Component `startup` methods.
  585. *
  586. * @return \Cake\Http\Response|null
  587. */
  588. public function startupProcess()
  589. {
  590. $event = $this->dispatchEvent('Controller.initialize');
  591. if ($event->getResult() instanceof Response) {
  592. return $event->getResult();
  593. }
  594. $event = $this->dispatchEvent('Controller.startup');
  595. if ($event->getResult() instanceof Response) {
  596. return $event->getResult();
  597. }
  598. return null;
  599. }
  600. /**
  601. * Perform the various shutdown processes for this controller.
  602. * Fire the Components and Controller callbacks in the correct order.
  603. *
  604. * - triggers the component `shutdown` callback.
  605. * - calls the Controller's `afterFilter` method.
  606. *
  607. * @return \Cake\Http\Response|null
  608. */
  609. public function shutdownProcess()
  610. {
  611. $event = $this->dispatchEvent('Controller.shutdown');
  612. if ($event->getResult() instanceof Response) {
  613. return $event->getResult();
  614. }
  615. return null;
  616. }
  617. /**
  618. * Redirects to given $url, after turning off $this->autoRender.
  619. *
  620. * @param string|array $url A string or array-based URL pointing to another location within the app,
  621. * or an absolute URL
  622. * @param int $status HTTP status code (eg: 301)
  623. * @return \Cake\Http\Response|null
  624. * @link https://book.cakephp.org/3.0/en/controllers.html#Controller::redirect
  625. */
  626. public function redirect($url, $status = 302)
  627. {
  628. $this->autoRender = false;
  629. if ($status) {
  630. $this->response = $this->response->withStatus($status);
  631. }
  632. $event = $this->dispatchEvent('Controller.beforeRedirect', [$url, $this->response]);
  633. if ($event->getResult() instanceof Response) {
  634. return $this->response = $event->getResult();
  635. }
  636. if ($event->isStopped()) {
  637. return null;
  638. }
  639. $response = $this->response;
  640. if (!$response->getHeaderLine('Location')) {
  641. $response = $response->withLocation(Router::url($url, true));
  642. }
  643. return $this->response = $response;
  644. }
  645. /**
  646. * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
  647. *
  648. * Examples:
  649. *
  650. * ```
  651. * setAction('another_action');
  652. * setAction('action_with_parameters', $parameter1);
  653. * ```
  654. *
  655. * @param string $action The new action to be 'redirected' to.
  656. * Any other parameters passed to this method will be passed as parameters to the new action.
  657. * @param array ...$args Arguments passed to the action
  658. * @return mixed Returns the return value of the called action
  659. */
  660. public function setAction($action, ...$args)
  661. {
  662. $this->request = $this->request->withParam('action', $action);
  663. return $this->$action(...$args);
  664. }
  665. /**
  666. * Instantiates the correct view class, hands it its data, and uses it to render the view output.
  667. *
  668. * @param string|null $view View to use for rendering
  669. * @param string|null $layout Layout to use
  670. * @return \Cake\Http\Response A response object containing the rendered view.
  671. * @link https://book.cakephp.org/3.0/en/controllers.html#rendering-a-view
  672. */
  673. public function render($view = null, $layout = null)
  674. {
  675. $builder = $this->viewBuilder();
  676. if (!$builder->getTemplatePath()) {
  677. $builder->setTemplatePath($this->_viewPath());
  678. }
  679. if ($this->request->getParam('bare')) {
  680. $builder->enableAutoLayout(false);
  681. }
  682. $builder->getClassName($this->viewClass);
  683. $this->autoRender = false;
  684. $event = $this->dispatchEvent('Controller.beforeRender');
  685. if ($event->getResult() instanceof Response) {
  686. return $event->getResult();
  687. }
  688. if ($event->isStopped()) {
  689. return $this->response;
  690. }
  691. if ($builder->getTemplate() === null && $this->request->getParam('action')) {
  692. $builder->setTemplate($this->request->getParam('action'));
  693. }
  694. $this->View = $this->createView();
  695. $contents = $this->View->render($view, $layout);
  696. $this->response = $this->View->response->withStringBody($contents);
  697. return $this->response;
  698. }
  699. /**
  700. * Get the viewPath based on controller name and request prefix.
  701. *
  702. * @return string
  703. */
  704. protected function _viewPath()
  705. {
  706. $viewPath = $this->name;
  707. if ($this->request->getParam('prefix')) {
  708. $prefixes = array_map(
  709. 'Cake\Utility\Inflector::camelize',
  710. explode('/', $this->request->getParam('prefix'))
  711. );
  712. $viewPath = implode(DIRECTORY_SEPARATOR, $prefixes) . DIRECTORY_SEPARATOR . $viewPath;
  713. }
  714. return $viewPath;
  715. }
  716. /**
  717. * Returns the referring URL for this request.
  718. *
  719. * @param string|array|null $default Default URL to use if HTTP_REFERER cannot be read from headers
  720. * @param bool $local If true, restrict referring URLs to local server
  721. * @return string Referring URL
  722. */
  723. public function referer($default = null, $local = false)
  724. {
  725. if (!$this->request) {
  726. return Router::url($default, !$local);
  727. }
  728. $referer = $this->request->referer($local);
  729. if ($referer === '/' && $default && $default !== $referer) {
  730. $url = Router::url($default, !$local);
  731. $base = $this->request->getAttribute('base');
  732. if ($local && $base && strpos($url, $base) === 0) {
  733. $url = substr($url, strlen($base));
  734. if ($url[0] !== '/') {
  735. $url = '/' . $url;
  736. }
  737. return $url;
  738. }
  739. return $url;
  740. }
  741. return $referer;
  742. }
  743. /**
  744. * Handles pagination of records in Table objects.
  745. *
  746. * Will load the referenced Table object, and have the PaginatorComponent
  747. * paginate the query using the request date and settings defined in `$this->paginate`.
  748. *
  749. * This method will also make the PaginatorHelper available in the view.
  750. *
  751. * @param \Cake\ORM\Table|string|\Cake\ORM\Query|null $object Table to paginate
  752. * (e.g: Table instance, 'TableName' or a Query object)
  753. * @param array $settings The settings/configuration used for pagination.
  754. * @return \Cake\ORM\ResultSet|\Cake\Datasource\ResultSetInterface Query results
  755. * @link https://book.cakephp.org/3.0/en/controllers.html#paginating-a-model
  756. * @throws \RuntimeException When no compatible table object can be found.
  757. */
  758. public function paginate($object = null, array $settings = [])
  759. {
  760. if (is_object($object)) {
  761. $table = $object;
  762. }
  763. if (is_string($object) || $object === null) {
  764. $try = [$object, $this->modelClass];
  765. foreach ($try as $tableName) {
  766. if (empty($tableName)) {
  767. continue;
  768. }
  769. $table = $this->loadModel($tableName);
  770. break;
  771. }
  772. }
  773. $this->loadComponent('Paginator');
  774. if (empty($table)) {
  775. throw new RuntimeException('Unable to locate an object compatible with paginate.');
  776. }
  777. $settings += $this->paginate;
  778. return $this->Paginator->paginate($table, $settings);
  779. }
  780. /**
  781. * Method to check that an action is accessible from a URL.
  782. *
  783. * Override this method to change which controller methods can be reached.
  784. * The default implementation disallows access to all methods defined on Cake\Controller\Controller,
  785. * and allows all public methods on all subclasses of this class.
  786. *
  787. * @param string $action The action to check.
  788. * @return bool Whether or not the method is accessible from a URL.
  789. */
  790. public function isAction($action)
  791. {
  792. $baseClass = new ReflectionClass('Cake\Controller\Controller');
  793. if ($baseClass->hasMethod($action)) {
  794. return false;
  795. }
  796. try {
  797. $method = new ReflectionMethod($this, $action);
  798. } catch (ReflectionException $e) {
  799. return false;
  800. }
  801. return $method->isPublic();
  802. }
  803. /**
  804. * Called before the controller action. You can use this method to configure and customize components
  805. * or perform logic that needs to happen before each controller action.
  806. *
  807. * @param \Cake\Event\Event $event An Event instance
  808. * @return \Cake\Http\Response|null
  809. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  810. */
  811. public function beforeFilter(Event $event)
  812. {
  813. return null;
  814. }
  815. /**
  816. * Called after the controller action is run, but before the view is rendered. You can use this method
  817. * to perform logic or set view variables that are required on every request.
  818. *
  819. * @param \Cake\Event\Event $event An Event instance
  820. * @return \Cake\Http\Response|null
  821. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  822. */
  823. public function beforeRender(Event $event)
  824. {
  825. return null;
  826. }
  827. /**
  828. * The beforeRedirect method is invoked when the controller's redirect method is called but before any
  829. * further action.
  830. *
  831. * If the event is stopped the controller will not continue on to redirect the request.
  832. * The $url and $status variables have same meaning as for the controller's method.
  833. * You can set the event result to response instance or modify the redirect location
  834. * using controller's response instance.
  835. *
  836. * @param \Cake\Event\Event $event An Event instance
  837. * @param string|array $url A string or array-based URL pointing to another location within the app,
  838. * or an absolute URL
  839. * @param \Cake\Http\Response $response The response object.
  840. * @return \Cake\Http\Response|null
  841. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  842. */
  843. public function beforeRedirect(Event $event, $url, Response $response)
  844. {
  845. return null;
  846. }
  847. /**
  848. * Called after the controller action is run and rendered.
  849. *
  850. * @param \Cake\Event\Event $event An Event instance
  851. * @return \Cake\Http\Response|null
  852. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  853. */
  854. public function afterFilter(Event $event)
  855. {
  856. return null;
  857. }
  858. }