Controller::loadComponent()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
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
17
use Cake\Controller\Exception\MissingActionException;
18
use Cake\Datasource\ModelAwareTrait;
19
use Cake\Event\Event;
20
use Cake\Event\EventDispatcherInterface;
21
use Cake\Event\EventDispatcherTrait;
22
use Cake\Event\EventListenerInterface;
23
use Cake\Http\Response;
24
use Cake\Http\ServerRequest;
25
use Cake\Log\LogTrait;
26
use Cake\ORM\Locator\LocatorAwareTrait;
27
use Cake\Routing\RequestActionTrait;
28
use Cake\Routing\Router;
29
use Cake\Utility\MergeVariablesTrait;
30
use Cake\View\ViewVarsTrait;
31
use LogicException;
32
use ReflectionClass;
33
use ReflectionException;
34
use ReflectionMethod;
35
use RuntimeException;
36
37
/**
38
 * Application controller class for organization of business logic.
39
 * Provides basic functionality, such as rendering views inside layouts,
40
 * automatic model availability, redirection, callbacks, and more.
41
 *
42
 * Controllers should provide a number of 'action' methods. These are public
43
 * methods on a controller that are not inherited from `Controller`.
44
 * Each action serves as an endpoint for performing a specific action on a
45
 * resource or collection of resources. For example adding or editing a new
46
 * object, or listing a set of objects.
47
 *
48
 * You can access request parameters, using `$this->request`. The request object
49
 * contains all the POST, GET and FILES that were part of the request.
50
 *
51
 * After performing the required action, controllers are responsible for
52
 * creating a response. This usually takes the form of a generated `View`, or
53
 * possibly a redirection to another URL. In either case `$this->response`
54
 * allows you to manipulate all aspects of the response.
55
 *
56
 * Controllers are created by `Dispatcher` based on request parameters and
57
 * routing. By default controllers and actions use conventional names.
58
 * For example `/posts/index` maps to `PostsController::index()`. You can re-map
59
 * URLs using Router::connect() or RouterBuilder::connect().
60
 *
61
 * ### Life cycle callbacks
62
 *
63
 * CakePHP fires a number of life cycle callbacks during each request.
64
 * By implementing a method you can receive the related events. The available
65
 * callbacks are:
66
 *
67
 * - `beforeFilter(Event $event)`
68
 *   Called before each action. This is a good place to do general logic that
69
 *   applies to all actions.
70
 * - `beforeRender(Event $event)`
71
 *   Called before the view is rendered.
72
 * - `beforeRedirect(Event $event, $url, Response $response)`
73
 *    Called before a redirect is done.
74
 * - `afterFilter(Event $event)`
75
 *   Called after each action is complete and after the view is rendered.
76
 *
77
 * @property \Cake\Controller\Component\AuthComponent $Auth
78
 * @property \Cake\Controller\Component\CookieComponent $Cookie
79
 * @property \Cake\Controller\Component\CsrfComponent $Csrf
80
 * @property \Cake\Controller\Component\FlashComponent $Flash
81
 * @property \Cake\Controller\Component\PaginatorComponent $Paginator
82
 * @property \Cake\Controller\Component\RequestHandlerComponent $RequestHandler
83
 * @property \Cake\Controller\Component\SecurityComponent $Security
84
 * @method bool isAuthorized($user)
85
 * @link https://book.cakephp.org/3/en/controllers.html
86
 */
87
class Controller implements EventListenerInterface, EventDispatcherInterface
88
{
89
    use EventDispatcherTrait;
90
    use LocatorAwareTrait;
91
    use LogTrait;
92
    use MergeVariablesTrait;
93
    use ModelAwareTrait;
94
    use RequestActionTrait;
0 ignored issues
show
Deprecated Code introduced by
The trait Cake\Routing\RequestActionTrait has been deprecated with message: 3.3.0 Use view cells instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
95
    use ViewVarsTrait;
96
97
    /**
98
     * The name of this controller. Controller names are plural, named after the model they manipulate.
99
     *
100
     * Set automatically using conventions in Controller::__construct().
101
     *
102
     * @var string
103
     */
104
    protected $name;
105
106
    /**
107
     * An array containing the names of helpers this controller uses. The array elements should
108
     * not contain the "Helper" part of the class name.
109
     *
110
     * Example:
111
     * ```
112
     * public $helpers = ['Form', 'Html', 'Time'];
113
     * ```
114
     *
115
     * @var array
116
     * @link https://book.cakephp.org/3/en/controllers.html#configuring-helpers-to-load
117
     *
118
     * @deprecated 3.0.0 You should configure helpers in your AppView::initialize() method.
119
     */
120
    public $helpers = [];
121
122
    /**
123
     * An instance of a \Cake\Http\ServerRequest object that contains information about the current request.
124
     * This object contains all the information about a request and several methods for reading
125
     * additional information about the request.
126
     *
127
     * Deprecated 3.6.0: The property will become protected in 4.0.0. Use getRequest()/setRequest instead.
128
     *
129
     * @var \Cake\Http\ServerRequest
130
     * @link https://book.cakephp.org/3/en/controllers/request-response.html#request
131
     */
132
    public $request;
133
134
    /**
135
     * An instance of a Response object that contains information about the impending response
136
     *
137
     * Deprecated 3.6.0: The property will become protected in 4.0.0. Use getResponse()/setResponse instead.
138
139
     * @var \Cake\Http\Response
140
     * @link https://book.cakephp.org/3/en/controllers/request-response.html#response
141
     */
142
    public $response;
143
144
    /**
145
     * The class name to use for creating the response object.
146
     *
147
     * @var string
148
     */
149
    protected $_responseClass = Response::class;
150
151
    /**
152
     * Settings for pagination.
153
     *
154
     * Used to pre-configure pagination preferences for the various
155
     * tables your controller will be paginating.
156
     *
157
     * @var array
158
     * @see \Cake\Controller\Component\PaginatorComponent
159
     */
160
    public $paginate = [];
161
162
    /**
163
     * Set to true to automatically render the view
164
     * after action logic.
165
     *
166
     * @var bool
167
     */
168
    protected $autoRender = true;
169
170
    /**
171
     * Instance of ComponentRegistry used to create Components
172
     *
173
     * @var \Cake\Controller\ComponentRegistry
174
     */
175
    protected $_components;
176
177
    /**
178
     * Array containing the names of components this controller uses. Component names
179
     * should not contain the "Component" portion of the class name.
180
     *
181
     * Example:
182
     * ```
183
     * public $components = ['RequestHandler', 'Acl'];
184
     * ```
185
     *
186
     * @var array
187
     * @link https://book.cakephp.org/3/en/controllers/components.html
188
     *
189
     * @deprecated 3.0.0 You should configure components in your Controller::initialize() method.
190
     */
191
    public $components = [];
192
193
    /**
194
     * Instance of the View created during rendering. Won't be set until after
195
     * Controller::render() is called.
196
     *
197
     * @var \Cake\View\View
198
     * @deprecated 3.1.0 Use viewBuilder() instead.
199
     */
200
    public $View;
201
202
    /**
203
     * These Controller properties will be passed from the Controller to the View as options.
204
     *
205
     * @var array
206
     * @see \Cake\View\View
207
     * @deprecated 3.7.0 Use ViewBuilder::setOptions() or any one of it's setter methods instead.
208
     */
209
    protected $_validViewOptions = [
210
        'passedArgs',
211
    ];
212
213
    /**
214
     * Automatically set to the name of a plugin.
215
     *
216
     * @var string|null
217
     */
218
    protected $plugin;
219
220
    /**
221
     * Holds all passed params.
222
     *
223
     * @var array
224
     * @deprecated 3.1.0 Use `$this->request->getParam('pass')` instead.
225
     */
226
    public $passedArgs = [];
227
228
    /**
229
     * Constructor.
230
     *
231
     * Sets a number of properties based on conventions if they are empty. To override the
232
     * conventions CakePHP uses you can define properties in your class declaration.
233
     *
234
     * @param \Cake\Http\ServerRequest|null $request Request object for this controller. Can be null for testing,
235
     *   but expect that features that use the request parameters will not work.
236
     * @param \Cake\Http\Response|null $response Response object for this controller.
237
     * @param string|null $name Override the name useful in testing when using mocks.
238
     * @param \Cake\Event\EventManager|null $eventManager The event manager. Defaults to a new instance.
239
     * @param \Cake\Controller\ComponentRegistry|null $components The component registry. Defaults to a new instance.
240
     */
241
    public function __construct(ServerRequest $request = null, Response $response = null, $name = null, $eventManager = null, $components = null)
242
    {
243
        if ($name !== null) {
244
            $this->name = $name;
245
        }
246
247
        if ($this->name === null && $request && $request->getParam('controller')) {
248
            $this->name = $request->getParam('controller');
249
        }
250
251
        if ($this->name === null) {
252
            list(, $name) = namespaceSplit(get_class($this));
253
            $this->name = substr($name, 0, -10);
254
        }
255
256
        $this->setRequest($request ?: new ServerRequest());
257
        $this->response = $response ?: new Response();
258
259
        if ($eventManager !== null) {
260
            $this->setEventManager($eventManager);
261
        }
262
263
        $this->modelFactory('Table', [$this->getTableLocator(), 'get']);
264
        $plugin = $this->request->getParam('plugin');
265
        $modelClass = ($plugin ? $plugin . '.' : '') . $this->name;
266
        $this->_setModelClass($modelClass);
267
268
        if ($components !== null) {
269
            $this->components($components);
270
        }
271
272
        $this->initialize();
273
274
        $this->_mergeControllerVars();
275
        $this->_loadComponents();
276
        $this->getEventManager()->on($this);
277
    }
278
279
    /**
280
     * Initialization hook method.
281
     *
282
     * Implement this method to avoid having to overwrite
283
     * the constructor and call parent.
284
     *
285
     * @return void
286
     */
287
    public function initialize()
288
    {
289
    }
290
291
    /**
292
     * Get the component registry for this controller.
293
     *
294
     * If called with the first parameter, it will be set as the controller $this->_components property
295
     *
296
     * @param \Cake\Controller\ComponentRegistry|null $components Component registry.
297
     *
298
     * @return \Cake\Controller\ComponentRegistry
299
     */
300
    public function components($components = null)
301
    {
302
        if ($components === null && $this->_components === null) {
303
            $this->_components = new ComponentRegistry($this);
304
        }
305
        if ($components !== null) {
306
            $components->setController($this);
307
            $this->_components = $components;
308
        }
309
310
        return $this->_components;
311
    }
312
313
    /**
314
     * Add a component to the controller's registry.
315
     *
316
     * This method will also set the component to a property.
317
     * For example:
318
     *
319
     * ```
320
     * $this->loadComponent('Acl.Acl');
321
     * ```
322
     *
323
     * Will result in a `Toolbar` property being set.
324
     *
325
     * @param string $name The name of the component to load.
326
     * @param array $config The config for the component.
327
     * @return \Cake\Controller\Component
328
     * @throws \Exception
329
     */
330
    public function loadComponent($name, array $config = [])
331
    {
332
        list(, $prop) = pluginSplit($name);
333
334
        return $this->{$prop} = $this->components()->load($name, $config);
335
    }
336
337
    /**
338
     * Magic accessor for model autoloading.
339
     *
340
     * @param string $name Property name
341
     * @return bool|object The model instance or false
342
     */
343
    public function __get($name)
344
    {
345
        $deprecated = [
346
            'name' => 'getName',
347
            'plugin' => 'getPlugin',
348
            'autoRender' => 'isAutoRenderEnabled',
349
        ];
350
        if (isset($deprecated[$name])) {
351
            $method = $deprecated[$name];
352
            deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s() instead.', $name, $method));
353
354
            return $this->{$method}();
355
        }
356
357
        $deprecated = [
358
            'layout' => 'getLayout',
359
            'view' => 'getTemplate',
360
            'theme' => 'getTheme',
361
            'autoLayout' => 'isAutoLayoutEnabled',
362
            'viewPath' => 'getTemplatePath',
363
            'layoutPath' => 'getLayoutPath',
364
        ];
365
        if (isset($deprecated[$name])) {
366
            $method = $deprecated[$name];
367
            deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
368
369
            return $this->viewBuilder()->{$method}();
370
        }
371
372
        list($plugin, $class) = pluginSplit($this->modelClass, true);
0 ignored issues
show
Bug introduced by
It seems like $this->modelClass can also be of type false or null; however, pluginSplit() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
373
        if ($class === $name) {
374
            return $this->loadModel($plugin . $class);
375
        }
376
377
        $trace = debug_backtrace();
378
        $parts = explode('\\', get_class($this));
379
        trigger_error(
380
            sprintf(
381
                'Undefined property: %s::$%s in %s on line %s',
382
                array_pop($parts),
383
                $name,
384
                $trace[0]['file'],
385
                $trace[0]['line']
386
            ),
387
            E_USER_NOTICE
388
        );
389
390
        return false;
391
    }
392
393
    /**
394
     * Magic setter for removed properties.
395
     *
396
     * @param string $name Property name.
397
     * @param mixed $value Value to set.
398
     * @return void
399
     */
400
    public function __set($name, $value)
401
    {
402
        $deprecated = [
403
            'name' => 'setName',
404
            'plugin' => 'setPlugin',
405
        ];
406
        if (isset($deprecated[$name])) {
407
            $method = $deprecated[$name];
408
            deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s() instead.', $name, $method));
409
            $this->{$method}($value);
410
411
            return;
412
        }
413
        if ($name === 'autoRender') {
414
            $value ? $this->enableAutoRender() : $this->disableAutoRender();
415
            deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->enableAutoRender/disableAutoRender() instead.', $name));
416
417
            return;
418
        }
419
        $deprecated = [
420
            'layout' => 'setLayout',
421
            'view' => 'setTemplate',
422
            'theme' => 'setTheme',
423
            'autoLayout' => 'enableAutoLayout',
424
            'viewPath' => 'setTemplatePath',
425
            'layoutPath' => 'setLayoutPath',
426
        ];
427
        if (isset($deprecated[$name])) {
428
            $method = $deprecated[$name];
429
            deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->viewBuilder()->%s() instead.', $name, $method));
430
431
            $this->viewBuilder()->{$method}($value);
432
433
            return;
434
        }
435
436
        $this->{$name} = $value;
437
    }
438
439
    /**
440
     * Returns the controller name.
441
     *
442
     * @return string
443
     * @since 3.6.0
444
     */
445
    public function getName()
446
    {
447
        return $this->name;
448
    }
449
450
    /**
451
     * Sets the controller name.
452
     *
453
     * @param string $name Controller name.
454
     * @return $this
455
     * @since 3.6.0
456
     */
457
    public function setName($name)
458
    {
459
        $this->name = $name;
460
461
        return $this;
462
    }
463
464
    /**
465
     * Returns the plugin name.
466
     *
467
     * @return string|null
468
     * @since 3.6.0
469
     */
470
    public function getPlugin()
471
    {
472
        return $this->plugin;
473
    }
474
475
    /**
476
     * Sets the plugin name.
477
     *
478
     * @param string $name Plugin name.
479
     * @return $this
480
     * @since 3.6.0
481
     */
482
    public function setPlugin($name)
483
    {
484
        $this->plugin = $name;
485
486
        return $this;
487
    }
488
489
    /**
490
     * Returns true if an action should be rendered automatically.
491
     *
492
     * @return bool
493
     * @since 3.6.0
494
     */
495
    public function isAutoRenderEnabled()
496
    {
497
        return $this->autoRender;
498
    }
499
500
    /**
501
     * Enable automatic action rendering.
502
     *
503
     * @return $this
504
     * @since 3.6.0
505
     */
506
    public function enableAutoRender()
507
    {
508
        $this->autoRender = true;
509
510
        return $this;
511
    }
512
513
    /**
514
     * Disable automatic action rendering.
515
     *
516
     * @return $this
517
     * @since 3.6.0
518
     */
519
    public function disableAutoRender()
520
    {
521
        $this->autoRender = false;
522
523
        return $this;
524
    }
525
526
    /**
527
     * Gets the request instance.
528
     *
529
     * @return \Cake\Http\ServerRequest
530
     * @since 3.6.0
531
     */
532
    public function getRequest()
533
    {
534
        return $this->request;
535
    }
536
537
    /**
538
     * Sets the request objects and configures a number of controller properties
539
     * based on the contents of the request. Controller acts as a proxy for certain View variables
540
     * which must also be updated here. The properties that get set are:
541
     *
542
     * - $this->request - To the $request parameter
543
     * - $this->passedArgs - Same as $request->params['pass]
544
     *
545
     * @param \Cake\Http\ServerRequest $request Request instance.
546
     * @return $this
547
     */
548 View Code Duplication
    public function setRequest(ServerRequest $request)
549
    {
550
        $this->request = $request;
551
        $this->plugin = $request->getParam('plugin') ?: null;
552
553
        if ($request->getParam('pass')) {
554
            $this->passedArgs = $request->getParam('pass');
0 ignored issues
show
Documentation Bug introduced by
It seems like $request->getParam('pass') of type * is incompatible with the declared type array of property $passedArgs.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Deprecated Code introduced by
The property Cake\Controller\Controller::$passedArgs has been deprecated with message: 3.1.0 Use `$this->request->getParam('pass')` instead.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
555
        }
556
557
        return $this;
558
    }
559
560
    /**
561
     * Gets the response instance.
562
     *
563
     * @return \Cake\Http\Response
564
     * @since 3.6.0
565
     */
566
    public function getResponse()
567
    {
568
        return $this->response;
569
    }
570
571
    /**
572
     * Sets the response instance.
573
     *
574
     * @param \Cake\Http\Response $response Response instance.
575
     * @return $this
576
     * @since 3.6.0
577
     */
578
    public function setResponse(Response $response)
579
    {
580
        $this->response = $response;
581
582
        return $this;
583
    }
584
585
    /**
586
     * Dispatches the controller action. Checks that the action
587
     * exists and isn't private.
588
     *
589
     * @return mixed The resulting response.
590
     * @throws \ReflectionException
591
     */
592
    public function invokeAction()
593
    {
594
        $request = $this->request;
595
        if (!$request) {
596
            throw new LogicException('No Request object configured. Cannot invoke action');
597
        }
598
        if (!$this->isAction($request->getParam('action'))) {
599
            throw new MissingActionException([
600
                'controller' => $this->name . 'Controller',
601
                'action' => $request->getParam('action'),
602
                'prefix' => $request->getParam('prefix') ?: '',
603
                'plugin' => $request->getParam('plugin'),
604
            ]);
605
        }
606
        /** @var callable $callable */
607
        $callable = [$this, $request->getParam('action')];
608
609
        $result = $callable(...array_values($request->getParam('pass')));
610
        if ($result instanceof Response) {
611
            $this->response = $result;
612
        }
613
614
        return $result;
615
    }
616
617
    /**
618
     * Merge components, helpers vars from
619
     * parent classes.
620
     *
621
     * @return void
622
     */
623
    protected function _mergeControllerVars()
624
    {
625
        $this->_mergeVars(
626
            ['components', 'helpers'],
627
            ['associative' => ['components', 'helpers']]
628
        );
629
    }
630
631
    /**
632
     * Returns a list of all events that will fire in the controller during its lifecycle.
633
     * You can override this function to add your own listener callbacks
634
     *
635
     * @return array
636
     */
637
    public function implementedEvents()
638
    {
639
        return [
640
            'Controller.initialize' => 'beforeFilter',
641
            'Controller.beforeRender' => 'beforeRender',
642
            'Controller.beforeRedirect' => 'beforeRedirect',
643
            'Controller.shutdown' => 'afterFilter',
644
        ];
645
    }
646
647
    /**
648
     * Loads the defined components using the Component factory.
649
     *
650
     * @return void
651
     */
652
    protected function _loadComponents()
653
    {
654
        if (empty($this->components)) {
0 ignored issues
show
Deprecated Code introduced by
The property Cake\Controller\Controller::$components has been deprecated with message: 3.0.0 You should configure components in your Controller::initialize() method.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
655
            return;
656
        }
657
        $registry = $this->components();
658
        $components = $registry->normalizeArray($this->components);
0 ignored issues
show
Deprecated Code introduced by
The property Cake\Controller\Controller::$components has been deprecated with message: 3.0.0 You should configure components in your Controller::initialize() method.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
659
        foreach ($components as $properties) {
660
            $this->loadComponent($properties['class'], $properties['config']);
661
        }
662
    }
663
664
    /**
665
     * Perform the startup process for this controller.
666
     * Fire the Components and Controller callbacks in the correct order.
667
     *
668
     * - Initializes components, which fires their `initialize` callback
669
     * - Calls the controller `beforeFilter`.
670
     * - triggers Component `startup` methods.
671
     *
672
     * @return \Cake\Http\Response|null
673
     */
674
    public function startupProcess()
675
    {
676
        $event = $this->dispatchEvent('Controller.initialize');
677
        if ($event->getResult() instanceof Response) {
678
            return $event->getResult();
679
        }
680
        $event = $this->dispatchEvent('Controller.startup');
681
        if ($event->getResult() instanceof Response) {
682
            return $event->getResult();
683
        }
684
685
        return null;
686
    }
687
688
    /**
689
     * Perform the various shutdown processes for this controller.
690
     * Fire the Components and Controller callbacks in the correct order.
691
     *
692
     * - triggers the component `shutdown` callback.
693
     * - calls the Controller's `afterFilter` method.
694
     *
695
     * @return \Cake\Http\Response|null
696
     */
697
    public function shutdownProcess()
698
    {
699
        $event = $this->dispatchEvent('Controller.shutdown');
700
        if ($event->getResult() instanceof Response) {
701
            return $event->getResult();
702
        }
703
704
        return null;
705
    }
706
707
    /**
708
     * Redirects to given $url, after turning off $this->autoRender.
709
     *
710
     * @param string|array $url A string or array-based URL pointing to another location within the app,
711
     *     or an absolute URL
712
     * @param int $status HTTP status code (eg: 301)
713
     * @return \Cake\Http\Response|null
714
     * @link https://book.cakephp.org/3/en/controllers.html#Controller::redirect
715
     */
716
    public function redirect($url, $status = 302)
717
    {
718
        $this->autoRender = false;
719
720
        if ($status) {
721
            $this->response = $this->response->withStatus($status);
722
        }
723
724
        $event = $this->dispatchEvent('Controller.beforeRedirect', [$url, $this->response]);
725
        if ($event->getResult() instanceof Response) {
726
            return $this->response = $event->getResult();
727
        }
728
        if ($event->isStopped()) {
729
            return null;
730
        }
731
        $response = $this->response;
732
733
        if (!$response->getHeaderLine('Location')) {
734
            $response = $response->withLocation(Router::url($url, true));
735
        }
736
737
        return $this->response = $response;
738
    }
739
740
    /**
741
     * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
742
     *
743
     * Examples:
744
     *
745
     * ```
746
     * setAction('another_action');
747
     * setAction('action_with_parameters', $parameter1);
748
     * ```
749
     *
750
     * @param string $action The new action to be 'redirected' to.
751
     *   Any other parameters passed to this method will be passed as parameters to the new action.
752
     * @param array ...$args Arguments passed to the action
753
     * @return mixed Returns the return value of the called action
754
     */
755
    public function setAction($action, ...$args)
756
    {
757
        $this->setRequest($this->request->withParam('action', $action));
758
759
        return $this->$action(...$args);
760
    }
761
762
    /**
763
     * Instantiates the correct view class, hands it its data, and uses it to render the view output.
764
     *
765
     * @param string|null $view View to use for rendering
766
     * @param string|null $layout Layout to use
767
     * @return \Cake\Http\Response A response object containing the rendered view.
768
     * @link https://book.cakephp.org/3/en/controllers.html#rendering-a-view
769
     */
770
    public function render($view = null, $layout = null)
771
    {
772
        $builder = $this->viewBuilder();
773
        if (!$builder->getTemplatePath()) {
774
            $builder->setTemplatePath($this->_viewPath());
775
        }
776
777
        if ($this->request->getParam('bare')) {
778
            $builder->disableAutoLayout();
779
        }
780
        $this->autoRender = false;
781
782
        $event = $this->dispatchEvent('Controller.beforeRender');
783
        if ($event->getResult() instanceof Response) {
784
            return $event->getResult();
785
        }
786
        if ($event->isStopped()) {
787
            return $this->response;
788
        }
789
790
        if ($builder->getTemplate() === null && $this->request->getParam('action')) {
791
            $builder->setTemplate($this->request->getParam('action'));
792
        }
793
794
        $this->View = $this->createView();
0 ignored issues
show
Deprecated Code introduced by
The property Cake\Controller\Controller::$View has been deprecated with message: 3.1.0 Use viewBuilder() instead.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
795
        $contents = $this->View->render($view, $layout);
0 ignored issues
show
Deprecated Code introduced by
The property Cake\Controller\Controller::$View has been deprecated with message: 3.1.0 Use viewBuilder() instead.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
796
        $this->setResponse($this->View->getResponse()->withStringBody($contents));
0 ignored issues
show
Deprecated Code introduced by
The property Cake\Controller\Controller::$View has been deprecated with message: 3.1.0 Use viewBuilder() instead.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
797
798
        return $this->response;
799
    }
800
801
    /**
802
     * Get the viewPath based on controller name and request prefix.
803
     *
804
     * @return string
805
     */
806
    protected function _viewPath()
807
    {
808
        $viewPath = $this->name;
809
        if ($this->request->getParam('prefix')) {
810
            $prefixes = array_map(
811
                'Cake\Utility\Inflector::camelize',
812
                explode('/', $this->request->getParam('prefix'))
813
            );
814
            $viewPath = implode(DIRECTORY_SEPARATOR, $prefixes) . DIRECTORY_SEPARATOR . $viewPath;
815
        }
816
817
        return $viewPath;
818
    }
819
820
    /**
821
     * Returns the referring URL for this request.
822
     *
823
     * @param string|array|null $default Default URL to use if HTTP_REFERER cannot be read from headers
824
     * @param bool $local If true, restrict referring URLs to local server
825
     * @return string Referring URL
826
     */
827
    public function referer($default = null, $local = false)
828
    {
829
        if (!$this->request) {
830
            return Router::url($default, !$local);
831
        }
832
833
        $referer = $this->request->referer($local);
834
        if ($referer === '/' && $default && $default !== $referer) {
835
            $url = Router::url($default, !$local);
836
            $base = $this->request->getAttribute('base');
837
            if ($local && $base && strpos($url, $base) === 0) {
838
                $url = substr($url, strlen($base));
839
                if ($url[0] !== '/') {
840
                    $url = '/' . $url;
841
                }
842
843
                return $url;
844
            }
845
846
            return $url;
847
        }
848
849
        return $referer;
850
    }
851
852
    /**
853
     * Handles pagination of records in Table objects.
854
     *
855
     * Will load the referenced Table object, and have the PaginatorComponent
856
     * paginate the query using the request date and settings defined in `$this->paginate`.
857
     *
858
     * This method will also make the PaginatorHelper available in the view.
859
     *
860
     * @param \Cake\ORM\Table|string|\Cake\ORM\Query|null $object Table to paginate
861
     * (e.g: Table instance, 'TableName' or a Query object)
862
     * @param array $settings The settings/configuration used for pagination.
863
     * @return \Cake\ORM\ResultSet|\Cake\Datasource\ResultSetInterface Query results
864
     * @link https://book.cakephp.org/3/en/controllers.html#paginating-a-model
865
     * @throws \RuntimeException When no compatible table object can be found.
866
     */
867
    public function paginate($object = null, array $settings = [])
868
    {
869
        if (is_object($object)) {
870
            $table = $object;
871
        }
872
873
        if (is_string($object) || $object === null) {
874
            $try = [$object, $this->modelClass];
875
            foreach ($try as $tableName) {
876
                if (empty($tableName)) {
877
                    continue;
878
                }
879
                $table = $this->loadModel($tableName);
880
                break;
881
            }
882
        }
883
884
        $this->loadComponent('Paginator');
885
        if (empty($table)) {
886
            throw new RuntimeException('Unable to locate an object compatible with paginate.');
887
        }
888
        $settings += $this->paginate;
889
890
        return $this->Paginator->paginate($table, $settings);
891
    }
892
893
    /**
894
     * Method to check that an action is accessible from a URL.
895
     *
896
     * Override this method to change which controller methods can be reached.
897
     * The default implementation disallows access to all methods defined on Cake\Controller\Controller,
898
     * and allows all public methods on all subclasses of this class.
899
     *
900
     * @param string $action The action to check.
901
     * @return bool Whether or not the method is accessible from a URL.
902
     * @throws \ReflectionException
903
     */
904
    public function isAction($action)
905
    {
906
        $baseClass = new ReflectionClass('Cake\Controller\Controller');
907
        if ($baseClass->hasMethod($action)) {
908
            return false;
909
        }
910
        try {
911
            $method = new ReflectionMethod($this, $action);
912
        } catch (ReflectionException $e) {
913
            return false;
914
        }
915
916
        return $method->isPublic();
917
    }
918
919
    /**
920
     * Called before the controller action. You can use this method to configure and customize components
921
     * or perform logic that needs to happen before each controller action.
922
     *
923
     * @param \Cake\Event\Event $event An Event instance
924
     * @return \Cake\Http\Response|null
925
     * @link https://book.cakephp.org/3/en/controllers.html#request-life-cycle-callbacks
926
     */
927
    public function beforeFilter(Event $event)
928
    {
929
        return null;
930
    }
931
932
    /**
933
     * Called after the controller action is run, but before the view is rendered. You can use this method
934
     * to perform logic or set view variables that are required on every request.
935
     *
936
     * @param \Cake\Event\Event $event An Event instance
937
     * @return \Cake\Http\Response|null
938
     * @link https://book.cakephp.org/3/en/controllers.html#request-life-cycle-callbacks
939
     */
940
    public function beforeRender(Event $event)
941
    {
942
        return null;
943
    }
944
945
    /**
946
     * The beforeRedirect method is invoked when the controller's redirect method is called but before any
947
     * further action.
948
     *
949
     * If the event is stopped the controller will not continue on to redirect the request.
950
     * The $url and $status variables have same meaning as for the controller's method.
951
     * You can set the event result to response instance or modify the redirect location
952
     * using controller's response instance.
953
     *
954
     * @param \Cake\Event\Event $event An Event instance
955
     * @param string|array $url A string or array-based URL pointing to another location within the app,
956
     *     or an absolute URL
957
     * @param \Cake\Http\Response $response The response object.
958
     * @return \Cake\Http\Response|null
959
     * @link https://book.cakephp.org/3/en/controllers.html#request-life-cycle-callbacks
960
     */
961
    public function beforeRedirect(Event $event, $url, Response $response)
962
    {
963
        return null;
964
    }
965
966
    /**
967
     * Called after the controller action is run and rendered.
968
     *
969
     * @param \Cake\Event\Event $event An Event instance
970
     * @return \Cake\Http\Response|null
971
     * @link https://book.cakephp.org/3/en/controllers.html#request-life-cycle-callbacks
972
     */
973
    public function afterFilter(Event $event)
974
    {
975
        return null;
976
    }
977
}
978