Controller.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926
  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.5.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.5.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. * @deprecated 3.5.0 Use enableAutoRender()/disableAutoRender() and isAutoRenderEnabled() instead.
  157. */
  158. protected $autoRender = true;
  159. /**
  160. * Instance of ComponentRegistry used to create Components
  161. *
  162. * @var \Cake\Controller\ComponentRegistry
  163. */
  164. protected $_components;
  165. /**
  166. * Array containing the names of components this controller uses. Component names
  167. * should not contain the "Component" portion of the class name.
  168. *
  169. * Example:
  170. * ```
  171. * public $components = ['RequestHandler', 'Acl'];
  172. * ```
  173. *
  174. * @var array
  175. * @link https://book.cakephp.org/3.0/en/controllers/components.html
  176. *
  177. * @deprecated 3.0.0 You should configure components in your Controller::initialize() method.
  178. */
  179. public $components = [];
  180. /**
  181. * Instance of the View created during rendering. Won't be set until after
  182. * Controller::render() is called.
  183. *
  184. * @var \Cake\View\View
  185. * @deprecated 3.1.0 Use viewBuilder() instead.
  186. */
  187. public $View;
  188. /**
  189. * These Controller properties will be passed from the Controller to the View as options.
  190. *
  191. * @var array
  192. * @see \Cake\View\View
  193. */
  194. protected $_validViewOptions = [
  195. 'passedArgs'
  196. ];
  197. /**
  198. * Automatically set to the name of a plugin.
  199. *
  200. * @var string
  201. * @deprecated 3.5.0 Use `$this->request->getParam('plugin')` instead.
  202. */
  203. public $plugin;
  204. /**
  205. * Holds all passed params.
  206. *
  207. * @var array
  208. * @deprecated 3.1.0 Use `$this->request->getParam('pass')` instead.
  209. */
  210. public $passedArgs = [];
  211. /**
  212. * Constructor.
  213. *
  214. * Sets a number of properties based on conventions if they are empty. To override the
  215. * conventions CakePHP uses you can define properties in your class declaration.
  216. *
  217. * @param \Cake\Http\ServerRequest|null $request Request object for this controller. Can be null for testing,
  218. * but expect that features that use the request parameters will not work.
  219. * @param \Cake\Http\Response|null $response Response object for this controller.
  220. * @param string|null $name Override the name useful in testing when using mocks.
  221. * @param \Cake\Event\EventManager|null $eventManager The event manager. Defaults to a new instance.
  222. * @param \Cake\Controller\ComponentRegistry|null $components The component registry. Defaults to a new instance.
  223. */
  224. public function __construct(ServerRequest $request = null, Response $response = null, $name = null, $eventManager = null, $components = null)
  225. {
  226. if ($name !== null) {
  227. $this->name = $name;
  228. }
  229. if ($this->name === null && $request && $request->getParam('controller')) {
  230. $this->name = $request->getParam('controller');
  231. }
  232. if ($this->name === null) {
  233. list(, $name) = namespaceSplit(get_class($this));
  234. $this->name = substr($name, 0, -10);
  235. }
  236. $this->setRequest($request !== null ? $request : new ServerRequest());
  237. $this->setResponse($response !== null ? $response : new Response());
  238. if ($eventManager !== null) {
  239. $this->setEventManager($eventManager);
  240. }
  241. $this->modelFactory('Table', [$this->getTableLocator(), 'get']);
  242. $plugin = $this->request->getParam('plugin');
  243. $modelClass = ($plugin ? $plugin . '.' : '') . $this->name;
  244. $this->_setModelClass($modelClass);
  245. if ($components !== null) {
  246. $this->components($components);
  247. }
  248. $this->initialize();
  249. $this->_mergeControllerVars();
  250. $this->_loadComponents();
  251. $this->getEventManager()->on($this);
  252. }
  253. /**
  254. * Initialization hook method.
  255. *
  256. * Implement this method to avoid having to overwrite
  257. * the constructor and call parent.
  258. *
  259. * @return void
  260. */
  261. public function initialize()
  262. {
  263. }
  264. /**
  265. * Get the component registry for this controller.
  266. *
  267. * If called with the first parameter, it will be set as the controller $this->_components property
  268. *
  269. * @param \Cake\Controller\ComponentRegistry|null $components Component registry.
  270. *
  271. * @return \Cake\Controller\ComponentRegistry
  272. */
  273. public function components($components = null)
  274. {
  275. if ($components === null && $this->_components === null) {
  276. $this->_components = new ComponentRegistry($this);
  277. }
  278. if ($components !== null) {
  279. $components->setController($this);
  280. $this->_components = $components;
  281. }
  282. return $this->_components;
  283. }
  284. /**
  285. * Add a component to the controller's registry.
  286. *
  287. * This method will also set the component to a property.
  288. * For example:
  289. *
  290. * ```
  291. * $this->loadComponent('Acl.Acl');
  292. * ```
  293. *
  294. * Will result in a `Toolbar` property being set.
  295. *
  296. * @param string $name The name of the component to load.
  297. * @param array $config The config for the component.
  298. * @return \Cake\Controller\Component
  299. */
  300. public function loadComponent($name, array $config = [])
  301. {
  302. list(, $prop) = pluginSplit($name);
  303. return $this->{$prop} = $this->components()->load($name, $config);
  304. }
  305. /**
  306. * Magic accessor for model autoloading.
  307. *
  308. * @param string $name Property name
  309. * @return bool|object The model instance or false
  310. */
  311. public function __get($name)
  312. {
  313. $deprecated = [
  314. 'name' => 'getName',
  315. 'plugin' => 'getPlugin',
  316. 'autoRender' => 'isAutoRenderEnabled',
  317. ];
  318. if (isset($deprecated[$name])) {
  319. $method = $deprecated[$name];
  320. return $this->{$method}();
  321. }
  322. $deprecated = [
  323. 'layout' => 'getLayout',
  324. 'view' => 'getTemplate',
  325. 'theme' => 'getTheme',
  326. 'autoLayout' => 'isAutoLayoutEnabled',
  327. 'viewPath' => 'getTemplatePath',
  328. 'layoutPath' => 'getLayoutPath',
  329. ];
  330. if (isset($deprecated[$name])) {
  331. $method = $deprecated[$name];
  332. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
  333. return $this->viewBuilder()->{$method}();
  334. }
  335. list($plugin, $class) = pluginSplit($this->modelClass, true);
  336. if ($class !== $name) {
  337. return false;
  338. }
  339. return $this->loadModel($plugin . $class);
  340. }
  341. /**
  342. * Magic setter for removed properties.
  343. *
  344. * @param string $name Property name.
  345. * @param mixed $value Value to set.
  346. * @return void
  347. */
  348. public function __set($name, $value)
  349. {
  350. $deprecated = [
  351. 'name' => 'setName',
  352. 'plugin' => 'setPlugin'
  353. ];
  354. if (isset($deprecated[$name])) {
  355. $method = $deprecated[$name];
  356. $this->{$method}($value);
  357. return;
  358. }
  359. if ($name === 'autoRender') {
  360. $value ? $this->enableAutoRender() : $this->disableAutoRender();
  361. return;
  362. }
  363. $deprecated = [
  364. 'layout' => 'setLayout',
  365. 'view' => 'setTemplate',
  366. 'theme' => 'setTheme',
  367. 'autoLayout' => 'enableAutoLayout',
  368. 'viewPath' => 'setTemplatePath',
  369. 'layoutPath' => 'setLayoutPath',
  370. ];
  371. if (isset($deprecated[$name])) {
  372. $method = $deprecated[$name];
  373. deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
  374. $this->viewBuilder()->{$method}($value);
  375. return;
  376. }
  377. $this->{$name} = $value;
  378. }
  379. /**
  380. * Returns the controller name.
  381. *
  382. * @return string
  383. */
  384. public function getName()
  385. {
  386. return $this->name;
  387. }
  388. /**
  389. * Sets the controller name.
  390. *
  391. * @param string $name Controller name.
  392. * @return $this
  393. */
  394. public function setName($name)
  395. {
  396. $this->name = $name;
  397. return $this;
  398. }
  399. /**
  400. * Returns true if an action should be rendered automatically.
  401. *
  402. * @return bool
  403. */
  404. public function isAutoRenderEnabled()
  405. {
  406. return $this->autoRender;
  407. }
  408. /**
  409. * Enable automatic action rendering.
  410. *
  411. * @return $this
  412. */
  413. public function enableAutoRender()
  414. {
  415. $this->autoRender = true;
  416. return $this;
  417. }
  418. /**
  419. * Disbale automatic action rendering.
  420. *
  421. * @return $this
  422. */
  423. public function disableAutoRender()
  424. {
  425. $this->autoRender = false;
  426. return $this;
  427. }
  428. /**
  429. * Gets the request instance.
  430. *
  431. * @return \Cake\Http\ServerRequest
  432. */
  433. public function getRequest()
  434. {
  435. return $this->request;
  436. }
  437. /**
  438. * Sets the request objects and configures a number of controller properties
  439. * based on the contents of the request. Controller acts as a proxy for certain View variables
  440. * which must also be updated here. The properties that get set are:
  441. *
  442. * - $this->request - To the $request parameter
  443. * - $this->plugin - To the $request->params['plugin']
  444. * - $this->passedArgs - Same as $request->params['pass]
  445. * - View::$plugin - $this->plugin
  446. *
  447. * @param \Cake\Http\ServerRequest $request Request instance.
  448. * @return $this
  449. */
  450. public function setRequest(ServerRequest $request)
  451. {
  452. $this->request = $request;
  453. $this->plugin = $request->getParam('plugin') ?: null;
  454. if ($request->getParam('pass')) {
  455. $this->passedArgs = $request->getParam('pass');
  456. }
  457. return $this;
  458. }
  459. /**
  460. * Gets the response instance.
  461. *
  462. * @return \Cake\Http\Response
  463. */
  464. public function getResponse()
  465. {
  466. return $this->response;
  467. }
  468. /**
  469. * Sets the response instance.
  470. *
  471. * @param \Cake\Http\Response $response Response instance.
  472. * @return $this
  473. */
  474. public function setResponse(Response $response)
  475. {
  476. $this->response = $response;
  477. return $this;
  478. }
  479. /**
  480. * Dispatches the controller action. Checks that the action
  481. * exists and isn't private.
  482. *
  483. * @return mixed The resulting response.
  484. * @throws \LogicException When request is not set.
  485. * @throws \Cake\Controller\Exception\MissingActionException When actions are not defined or inaccessible.
  486. */
  487. public function invokeAction()
  488. {
  489. $request = $this->request;
  490. if (!isset($request)) {
  491. throw new LogicException('No Request object configured. Cannot invoke action');
  492. }
  493. if (!$this->isAction($request->getParam('action'))) {
  494. throw new MissingActionException([
  495. 'controller' => $this->name . 'Controller',
  496. 'action' => $request->getParam('action'),
  497. 'prefix' => $request->getParam('prefix') ?: '',
  498. 'plugin' => $request->getParam('plugin'),
  499. ]);
  500. }
  501. /* @var callable $callable */
  502. $callable = [$this, $request->getParam('action')];
  503. return $callable(...array_values($request->getParam('pass')));
  504. }
  505. /**
  506. * Merge components, helpers vars from
  507. * parent classes.
  508. *
  509. * @return void
  510. */
  511. protected function _mergeControllerVars()
  512. {
  513. $this->_mergeVars(
  514. ['components', 'helpers'],
  515. ['associative' => ['components', 'helpers']]
  516. );
  517. }
  518. /**
  519. * Returns a list of all events that will fire in the controller during its lifecycle.
  520. * You can override this function to add your own listener callbacks
  521. *
  522. * @return array
  523. */
  524. public function implementedEvents()
  525. {
  526. return [
  527. 'Controller.initialize' => 'beforeFilter',
  528. 'Controller.beforeRender' => 'beforeRender',
  529. 'Controller.beforeRedirect' => 'beforeRedirect',
  530. 'Controller.shutdown' => 'afterFilter',
  531. ];
  532. }
  533. /**
  534. * Loads the defined components using the Component factory.
  535. *
  536. * @return void
  537. */
  538. protected function _loadComponents()
  539. {
  540. if (empty($this->components)) {
  541. return;
  542. }
  543. $registry = $this->components();
  544. $components = $registry->normalizeArray($this->components);
  545. foreach ($components as $properties) {
  546. $this->loadComponent($properties['class'], $properties['config']);
  547. }
  548. }
  549. /**
  550. * Perform the startup process for this controller.
  551. * Fire the Components and Controller callbacks in the correct order.
  552. *
  553. * - Initializes components, which fires their `initialize` callback
  554. * - Calls the controller `beforeFilter`.
  555. * - triggers Component `startup` methods.
  556. *
  557. * @return \Cake\Http\Response|null
  558. */
  559. public function startupProcess()
  560. {
  561. $event = $this->dispatchEvent('Controller.initialize');
  562. if ($event->getResult() instanceof Response) {
  563. return $event->getResult();
  564. }
  565. $event = $this->dispatchEvent('Controller.startup');
  566. if ($event->getResult() instanceof Response) {
  567. return $event->getResult();
  568. }
  569. return null;
  570. }
  571. /**
  572. * Perform the various shutdown processes for this controller.
  573. * Fire the Components and Controller callbacks in the correct order.
  574. *
  575. * - triggers the component `shutdown` callback.
  576. * - calls the Controller's `afterFilter` method.
  577. *
  578. * @return \Cake\Http\Response|null
  579. */
  580. public function shutdownProcess()
  581. {
  582. $event = $this->dispatchEvent('Controller.shutdown');
  583. if ($event->getResult() instanceof Response) {
  584. return $event->getResult();
  585. }
  586. return null;
  587. }
  588. /**
  589. * Redirects to given $url, after turning off $this->autoRender.
  590. *
  591. * @param string|array $url A string or array-based URL pointing to another location within the app,
  592. * or an absolute URL
  593. * @param int $status HTTP status code (eg: 301)
  594. * @return \Cake\Http\Response|null
  595. * @link https://book.cakephp.org/3.0/en/controllers.html#Controller::redirect
  596. */
  597. public function redirect($url, $status = 302)
  598. {
  599. $this->autoRender = false;
  600. if ($status) {
  601. $this->response = $this->response->withStatus($status);
  602. }
  603. $event = $this->dispatchEvent('Controller.beforeRedirect', [$url, $this->response]);
  604. if ($event->getResult() instanceof Response) {
  605. return $this->response = $event->getResult();
  606. }
  607. if ($event->isStopped()) {
  608. return null;
  609. }
  610. $response = $this->response;
  611. if (!$response->getHeaderLine('Location')) {
  612. $response = $response->withLocation(Router::url($url, true));
  613. }
  614. return $this->response = $response;
  615. }
  616. /**
  617. * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
  618. *
  619. * Examples:
  620. *
  621. * ```
  622. * setAction('another_action');
  623. * setAction('action_with_parameters', $parameter1);
  624. * ```
  625. *
  626. * @param string $action The new action to be 'redirected' to.
  627. * Any other parameters passed to this method will be passed as parameters to the new action.
  628. * @param array ...$args Arguments passed to the action
  629. * @return mixed Returns the return value of the called action
  630. */
  631. public function setAction($action, ...$args)
  632. {
  633. $this->request = $this->request->withParam('action', $action);
  634. return $this->$action(...$args);
  635. }
  636. /**
  637. * Instantiates the correct view class, hands it its data, and uses it to render the view output.
  638. *
  639. * @param string|null $view View to use for rendering
  640. * @param string|null $layout Layout to use
  641. * @return \Cake\Http\Response A response object containing the rendered view.
  642. * @link https://book.cakephp.org/3.0/en/controllers.html#rendering-a-view
  643. */
  644. public function render($view = null, $layout = null)
  645. {
  646. $builder = $this->viewBuilder();
  647. if (!$builder->getTemplatePath()) {
  648. $builder->setTemplatePath($this->_viewPath());
  649. }
  650. if ($this->request->getParam('bare')) {
  651. $builder->enableAutoLayout(false);
  652. }
  653. $builder->getClassName($this->viewClass);
  654. $this->autoRender = false;
  655. $event = $this->dispatchEvent('Controller.beforeRender');
  656. if ($event->getResult() instanceof Response) {
  657. return $event->getResult();
  658. }
  659. if ($event->isStopped()) {
  660. return $this->response;
  661. }
  662. if ($builder->getTemplate() === null && $this->request->getParam('action')) {
  663. $builder->setTemplate($this->request->getParam('action'));
  664. }
  665. $this->View = $this->createView();
  666. $contents = $this->View->render($view, $layout);
  667. $this->response = $this->View->response->withStringBody($contents);
  668. return $this->response;
  669. }
  670. /**
  671. * Get the viewPath based on controller name and request prefix.
  672. *
  673. * @return string
  674. */
  675. protected function _viewPath()
  676. {
  677. $viewPath = $this->name;
  678. if ($this->request->getParam('prefix')) {
  679. $prefixes = array_map(
  680. 'Cake\Utility\Inflector::camelize',
  681. explode('/', $this->request->getParam('prefix'))
  682. );
  683. $viewPath = implode(DIRECTORY_SEPARATOR, $prefixes) . DIRECTORY_SEPARATOR . $viewPath;
  684. }
  685. return $viewPath;
  686. }
  687. /**
  688. * Returns the referring URL for this request.
  689. *
  690. * @param string|array|null $default Default URL to use if HTTP_REFERER cannot be read from headers
  691. * @param bool $local If true, restrict referring URLs to local server
  692. * @return string Referring URL
  693. */
  694. public function referer($default = null, $local = false)
  695. {
  696. if (!$this->request) {
  697. return Router::url($default, !$local);
  698. }
  699. $referer = $this->request->referer($local);
  700. if ($referer === '/' && $default && $default !== $referer) {
  701. $url = Router::url($default, !$local);
  702. $base = $this->request->getAttribute('base');
  703. if ($local && $base && strpos($url, $base) === 0) {
  704. $url = substr($url, strlen($base));
  705. if ($url[0] !== '/') {
  706. $url = '/' . $url;
  707. }
  708. return $url;
  709. }
  710. return $url;
  711. }
  712. return $referer;
  713. }
  714. /**
  715. * Handles pagination of records in Table objects.
  716. *
  717. * Will load the referenced Table object, and have the PaginatorComponent
  718. * paginate the query using the request date and settings defined in `$this->paginate`.
  719. *
  720. * This method will also make the PaginatorHelper available in the view.
  721. *
  722. * @param \Cake\ORM\Table|string|\Cake\ORM\Query|null $object Table to paginate
  723. * (e.g: Table instance, 'TableName' or a Query object)
  724. * @param array $settings The settings/configuration used for pagination.
  725. * @return \Cake\ORM\ResultSet|\Cake\Datasource\ResultSetInterface Query results
  726. * @link https://book.cakephp.org/3.0/en/controllers.html#paginating-a-model
  727. * @throws \RuntimeException When no compatible table object can be found.
  728. */
  729. public function paginate($object = null, array $settings = [])
  730. {
  731. if (is_object($object)) {
  732. $table = $object;
  733. }
  734. if (is_string($object) || $object === null) {
  735. $try = [$object, $this->modelClass];
  736. foreach ($try as $tableName) {
  737. if (empty($tableName)) {
  738. continue;
  739. }
  740. $table = $this->loadModel($tableName);
  741. break;
  742. }
  743. }
  744. $this->loadComponent('Paginator');
  745. if (empty($table)) {
  746. throw new RuntimeException('Unable to locate an object compatible with paginate.');
  747. }
  748. $settings += $this->paginate;
  749. return $this->Paginator->paginate($table, $settings);
  750. }
  751. /**
  752. * Method to check that an action is accessible from a URL.
  753. *
  754. * Override this method to change which controller methods can be reached.
  755. * The default implementation disallows access to all methods defined on Cake\Controller\Controller,
  756. * and allows all public methods on all subclasses of this class.
  757. *
  758. * @param string $action The action to check.
  759. * @return bool Whether or not the method is accessible from a URL.
  760. */
  761. public function isAction($action)
  762. {
  763. $baseClass = new ReflectionClass('Cake\Controller\Controller');
  764. if ($baseClass->hasMethod($action)) {
  765. return false;
  766. }
  767. try {
  768. $method = new ReflectionMethod($this, $action);
  769. } catch (ReflectionException $e) {
  770. return false;
  771. }
  772. return $method->isPublic();
  773. }
  774. /**
  775. * Called before the controller action. You can use this method to configure and customize components
  776. * or perform logic that needs to happen before each controller action.
  777. *
  778. * @param \Cake\Event\Event $event An Event instance
  779. * @return \Cake\Http\Response|null
  780. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  781. */
  782. public function beforeFilter(Event $event)
  783. {
  784. return null;
  785. }
  786. /**
  787. * Called after the controller action is run, but before the view is rendered. You can use this method
  788. * to perform logic or set view variables that are required on every request.
  789. *
  790. * @param \Cake\Event\Event $event An Event instance
  791. * @return \Cake\Http\Response|null
  792. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  793. */
  794. public function beforeRender(Event $event)
  795. {
  796. return null;
  797. }
  798. /**
  799. * The beforeRedirect method is invoked when the controller's redirect method is called but before any
  800. * further action.
  801. *
  802. * If the event is stopped the controller will not continue on to redirect the request.
  803. * The $url and $status variables have same meaning as for the controller's method.
  804. * You can set the event result to response instance or modify the redirect location
  805. * using controller's response instance.
  806. *
  807. * @param \Cake\Event\Event $event An Event instance
  808. * @param string|array $url A string or array-based URL pointing to another location within the app,
  809. * or an absolute URL
  810. * @param \Cake\Http\Response $response The response object.
  811. * @return \Cake\Http\Response|null
  812. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  813. */
  814. public function beforeRedirect(Event $event, $url, Response $response)
  815. {
  816. return null;
  817. }
  818. /**
  819. * Called after the controller action is run and rendered.
  820. *
  821. * @param \Cake\Event\Event $event An Event instance
  822. * @return \Cake\Http\Response|null
  823. * @link https://book.cakephp.org/3.0/en/controllers.html#request-life-cycle-callbacks
  824. */
  825. public function afterFilter(Event $event)
  826. {
  827. return null;
  828. }
  829. }