Controller::beforeRender()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 2
rs 10
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @package       Cake.Controller
13
 * @since         CakePHP(tm) v 0.2.9
14
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
15
 */
16
17
App::uses('CakeResponse', 'Network');
18
App::uses('ClassRegistry', 'Utility');
19
App::uses('ComponentCollection', 'Controller');
20
App::uses('View', 'View');
21
App::uses('CakeEvent', 'Event');
22
App::uses('CakeEventListener', 'Event');
23
App::uses('CakeEventManager', 'Event');
24
25
/**
26
 * Application controller class for organization of business logic.
27
 * Provides basic functionality, such as rendering views inside layouts,
28
 * automatic model availability, redirection, callbacks, and more.
29
 *
30
 * Controllers should provide a number of 'action' methods. These are public methods on the controller
31
 * that are not prefixed with a '_' and not part of Controller. Each action serves as an endpoint for
32
 * performing a specific action on a resource or collection of resources. For example: adding or editing a new
33
 * object, or listing a set of objects.
34
 *
35
 * You can access request parameters, using `$this->request`. The request object contains all the POST, GET and FILES
36
 * that were part of the request.
37
 *
38
 * After performing the required actions, controllers are responsible for creating a response. This usually
39
 * takes the form of a generated View, or possibly a redirection to another controller action. In either case
40
 * `$this->response` allows you to manipulate all aspects of the response.
41
 *
42
 * Controllers are created by Dispatcher based on request parameters and routing. By default controllers and actions
43
 * use conventional names. For example `/posts/index` maps to `PostsController::index()`. You can re-map URLs
44
 * using Router::connect().
45
 *
46
 * @package       Cake.Controller
47
 * @property      AclComponent $Acl
48
 * @property      AuthComponent $Auth
49
 * @property      CookieComponent $Cookie
50
 * @property      EmailComponent $Email
51
 * @property      PaginatorComponent $Paginator
52
 * @property      RequestHandlerComponent $RequestHandler
53
 * @property      SecurityComponent $Security
54
 * @property      SessionComponent $Session
55
 * @link          http://book.cakephp.org/2.0/en/controllers.html
56
 */
57
class Controller extends Object implements CakeEventListener {
58
59
/**
60
 * The name of this controller. Controller names are plural, named after the model they manipulate.
61
 *
62
 * @var string
63
 * @link http://book.cakephp.org/2.0/en/controllers.html#controller-attributes
64
 */
65
	public $name = null;
66
67
/**
68
 * An array containing the class names of models this controller uses.
69
 *
70
 * Example: `public $uses = array('Product', 'Post', 'Comment');`
71
 *
72
 * Can be set to several values to express different options:
73
 *
74
 * - `true` Use the default inflected model name.
75
 * - `array()` Use only models defined in the parent class.
76
 * - `false` Use no models at all, do not merge with parent class either.
77
 * - `array('Post', 'Comment')` Use only the Post and Comment models. Models
78
 *   Will also be merged with the parent class.
79
 *
80
 * The default value is `true`.
81
 *
82
 * @var mixed A single name as a string or a list of names as an array.
83
 * @link http://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
84
 */
85
	public $uses = true;
86
87
/**
88
 * An array containing the names of helpers this controller uses. The array elements should
89
 * not contain the "Helper" part of the class name.
90
 *
91
 * Example: `public $helpers = array('Html', 'Js', 'Time', 'Ajax');`
92
 *
93
 * @var mixed A single name as a string or a list of names as an array.
94
 * @link http://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
95
 */
96
	public $helpers = array();
97
98
/**
99
 * An instance of a CakeRequest object that contains information about the current request.
100
 * This object contains all the information about a request and several methods for reading
101
 * additional information about the request.
102
 *
103
 * @var CakeRequest
104
 * @link http://book.cakephp.org/2.0/en/controllers/request-response.html#cakerequest
105
 */
106
	public $request;
107
108
/**
109
 * An instance of a CakeResponse object that contains information about the impending response
110
 *
111
 * @var CakeResponse
112
 * @link http://book.cakephp.org/2.0/en/controllers/request-response.html#cakeresponse
113
 */
114
	public $response;
115
116
/**
117
 * The class name to use for creating the response object.
118
 *
119
 * @var string
120
 */
121
	protected $_responseClass = 'CakeResponse';
122
123
/**
124
 * The name of the views subfolder containing views for this controller.
125
 *
126
 * @var string
127
 */
128
	public $viewPath = null;
129
130
/**
131
 * The name of the layouts subfolder containing layouts for this controller.
132
 *
133
 * @var string
134
 */
135
	public $layoutPath = null;
136
137
/**
138
 * Contains variables to be handed to the view.
139
 *
140
 * @var array
141
 */
142
	public $viewVars = array();
143
144
/**
145
 * The name of the view file to render. The name specified
146
 * is the filename in /app/View/<SubFolder> without the .ctp extension.
147
 *
148
 * @var string
149
 */
150
	public $view = null;
151
152
/**
153
 * The name of the layout file to render the view inside of. The name specified
154
 * is the filename of the layout in /app/View/Layouts without the .ctp
155
 * extension.
156
 *
157
 * @var string
158
 */
159
	public $layout = 'default';
160
161
/**
162
 * Set to true to automatically render the view
163
 * after action logic.
164
 *
165
 * @var boolean
166
 */
167
	public $autoRender = true;
168
169
/**
170
 * Set to true to automatically render the layout around views.
171
 *
172
 * @var boolean
173
 */
174
	public $autoLayout = true;
175
176
/**
177
 * Instance of ComponentCollection used to handle callbacks.
178
 *
179
 * @var ComponentCollection
180
 */
181
	public $Components = null;
182
183
/**
184
 * Array containing the names of components this controller uses. Component names
185
 * should not contain the "Component" portion of the class name.
186
 *
187
 * Example: `public $components = array('Session', 'RequestHandler', 'Acl');`
188
 *
189
 * @var array
190
 * @link http://book.cakephp.org/2.0/en/controllers/components.html
191
 */
192
	public $components = array('Session');
193
194
/**
195
 * The name of the View class this controller sends output to.
196
 *
197
 * @var string
198
 */
199
	public $viewClass = 'View';
200
201
/**
202
 * Instance of the View created during rendering. Won't be set until after
203
 * Controller::render() is called.
204
 *
205
 * @var View
206
 */
207
	public $View;
208
209
/**
210
 * File extension for view templates. Defaults to CakePHP's conventional ".ctp".
211
 *
212
 * @var string
213
 */
214
	public $ext = '.ctp';
215
216
/**
217
 * Automatically set to the name of a plugin.
218
 *
219
 * @var string
220
 */
221
	public $plugin = null;
222
223
/**
224
 * Used to define methods a controller that will be cached. To cache a
225
 * single action, the value is set to an array containing keys that match
226
 * action names and values that denote cache expiration times (in seconds).
227
 *
228
 * Example:
229
 *
230
 * {{{
231
 * public $cacheAction = array(
232
 *		'view/23/' => 21600,
233
 *		'recalled/' => 86400
234
 *	);
235
 * }}}
236
 *
237
 * $cacheAction can also be set to a strtotime() compatible string. This
238
 * marks all the actions in the controller for view caching.
239
 *
240
 * @var mixed
241
 * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/cache.html#additional-configuration-options
242
 */
243
	public $cacheAction = false;
244
245
/**
246
 * Holds all params passed and named.
247
 *
248
 * @var mixed
249
 */
250
	public $passedArgs = array();
251
252
/**
253
 * Triggers Scaffolding
254
 *
255
 * @var mixed
256
 * @link http://book.cakephp.org/2.0/en/controllers/scaffolding.html
257
 */
258
	public $scaffold = false;
259
260
/**
261
 * Holds current methods of the controller. This is a list of all the methods reachable
262
 * via URL. Modifying this array will allow you to change which methods can be reached.
263
 *
264
 * @var array
265
 */
266
	public $methods = array();
267
268
/**
269
 * This controller's primary model class name, the Inflector::singularize()'ed version of
270
 * the controller's $name property.
271
 *
272
 * Example: For a controller named 'Comments', the modelClass would be 'Comment'
273
 *
274
 * @var string
275
 */
276
	public $modelClass = null;
277
278
/**
279
 * This controller's model key name, an underscored version of the controller's $modelClass property.
280
 *
281
 * Example: For a controller named 'ArticleComments', the modelKey would be 'article_comment'
282
 *
283
 * @var string
284
 */
285
	public $modelKey = null;
286
287
/**
288
 * Holds any validation errors produced by the last call of the validateErrors() method.
289
 *
290
 * @var array Validation errors, or false if none
291
 */
292
	public $validationErrors = null;
293
294
/**
295
 * The class name of the parent class you wish to merge with.
296
 * Typically this is AppController, but you may wish to merge vars with a different
297
 * parent class.
298
 *
299
 * @var string
300
 */
301
	protected $_mergeParent = 'AppController';
302
303
/**
304
 * Instance of the CakeEventManager this controller is using
305
 * to dispatch inner events.
306
 *
307
 * @var CakeEventManager
308
 */
309
	protected $_eventManager = null;
310
311
/**
312
 * Constructor.
313
 *
314
 * @param CakeRequest $request Request object for this controller. Can be null for testing,
315
 *  but expect that features that use the request parameters will not work.
316
 * @param CakeResponse $response Response object for this controller.
317
 */
318
	public function __construct($request = null, $response = null) {
319
		if ($this->name === null) {
320
			$this->name = substr(get_class($this), 0, -10);
321
		}
322
323
		if (!$this->viewPath) {
324
			$this->viewPath = $this->name;
325
		}
326
327
		$this->modelClass = Inflector::singularize($this->name);
328
		$this->modelKey = Inflector::underscore($this->modelClass);
329
		$this->Components = new ComponentCollection();
330
331
		$childMethods = get_class_methods($this);
332
		$parentMethods = get_class_methods('Controller');
333
334
		$this->methods = array_diff($childMethods, $parentMethods);
335
336
		if ($request instanceof CakeRequest) {
337
			$this->setRequest($request);
338
		}
339
		if ($response instanceof CakeResponse) {
340
			$this->response = $response;
341
		}
342
		parent::__construct();
343
	}
344
345
/**
346
 * Provides backwards compatibility to avoid problems with empty and isset to alias properties.
347
 * Lazy loads models using the loadModel() method if declared in $uses
348
 *
349
 * @param string $name
350
 * @return boolean
351
 */
352
	public function __isset($name) {
353
		switch ($name) {
354
			case 'base':
355
			case 'here':
356
			case 'webroot':
357
			case 'data':
358
			case 'action':
359
			case 'params':
360
				return true;
361
		}
362
363 View Code Duplication
		if (is_array($this->uses)) {
364
			foreach ($this->uses as $modelClass) {
365
				list($plugin, $class) = pluginSplit($modelClass, true);
366
				if ($name === $class) {
367
					return $this->loadModel($modelClass);
368
				}
369
			}
370
		}
371
372
		if ($name === $this->modelClass) {
373
			list($plugin, $class) = pluginSplit($name, true);
0 ignored issues
show
Unused Code introduced by
The assignment to $class is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
374
			if (!$plugin) {
375
				$plugin = $this->plugin ? $this->plugin . '.' : null;
376
			}
377
			return $this->loadModel($plugin . $this->modelClass);
378
		}
379
380
		return false;
381
	}
382
383
/**
384
 * Provides backwards compatibility access to the request object properties.
385
 * Also provides the params alias.
386
 *
387
 * @param string $name The name of the requested value
388
 * @return mixed The requested value for valid variables/aliases else null
389
 */
390
	public function __get($name) {
391
		switch ($name) {
392
			case 'base':
393
			case 'here':
394
			case 'webroot':
395
			case 'data':
396
				return $this->request->{$name};
397
			case 'action':
398
				return isset($this->request->params['action']) ? $this->request->params['action'] : '';
399
			case 'params':
400
				return $this->request;
401
			case 'paginate':
402
				return $this->Components->load('Paginator')->settings;
403
		}
404
405
		if (isset($this->{$name})) {
406
			return $this->{$name};
407
		}
408
409
		return null;
410
	}
411
412
/**
413
 * Provides backwards compatibility access for setting values to the request object.
414
 *
415
 * @param string $name
416
 * @param mixed $value
417
 * @return void
418
 */
419
	public function __set($name, $value) {
420
		switch ($name) {
421
			case 'base':
422
			case 'here':
423
			case 'webroot':
424
			case 'data':
425
				$this->request->{$name} = $value;
426
				return;
427
			case 'action':
428
				$this->request->params['action'] = $value;
429
				return;
430
			case 'params':
431
				$this->request->params = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value of type * is incompatible with the declared type array of property $params.

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...
432
				return;
433
			case 'paginate':
434
				$this->Components->load('Paginator')->settings = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value of type * is incompatible with the declared type array of property $settings.

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...
435
				return;
436
		}
437
		$this->{$name} = $value;
438
	}
439
440
/**
441
 * Sets the request objects and configures a number of controller properties
442
 * based on the contents of the request. The properties that get set are
443
 *
444
 * - $this->request - To the $request parameter
445
 * - $this->plugin - To the $request->params['plugin']
446
 * - $this->view - To the $request->params['action']
447
 * - $this->autoLayout - To the false if $request->params['bare']; is set.
448
 * - $this->autoRender - To false if $request->params['return'] == 1
449
 * - $this->passedArgs - The the combined results of params['named'] and params['pass]
450
 *
451
 * @param CakeRequest $request
452
 * @return void
453
 */
454
	public function setRequest(CakeRequest $request) {
455
		$this->request = $request;
456
		$this->plugin = isset($request->params['plugin']) ? Inflector::camelize($request->params['plugin']) : null;
457
		$this->view = isset($request->params['action']) ? $request->params['action'] : null;
458
		if (isset($request->params['pass']) && isset($request->params['named'])) {
459
			$this->passedArgs = array_merge($request->params['pass'], $request->params['named']);
460
		}
461
462
		if (!empty($request->params['return']) && $request->params['return'] == 1) {
463
			$this->autoRender = false;
464
		}
465
		if (!empty($request->params['bare'])) {
466
			$this->autoLayout = false;
467
		}
468
	}
469
470
/**
471
 * Dispatches the controller action. Checks that the action
472
 * exists and isn't private.
473
 *
474
 * @param CakeRequest $request
475
 * @return mixed The resulting response.
476
 * @throws PrivateActionException When actions are not public or prefixed by _
477
 * @throws MissingActionException When actions are not defined and scaffolding is
478
 *    not enabled.
479
 */
480
	public function invokeAction(CakeRequest $request) {
481
		try {
482
			$method = new ReflectionMethod($this, $request->params['action']);
483
484
			if ($this->_isPrivateAction($method, $request)) {
485
				throw new PrivateActionException(array(
486
					'controller' => $this->name . "Controller",
487
					'action' => $request->params['action']
488
				));
489
			}
490
			return $method->invokeArgs($this, $request->params['pass']);
491
492
		} catch (ReflectionException $e) {
493
			if ($this->scaffold !== false) {
494
				return $this->_getScaffold($request);
495
			}
496
			throw new MissingActionException(array(
497
				'controller' => $this->name . "Controller",
498
				'action' => $request->params['action']
499
			));
500
		}
501
	}
502
503
/**
504
 * Check if the request's action is marked as private, with an underscore,
505
 * or if the request is attempting to directly accessing a prefixed action.
506
 *
507
 * @param ReflectionMethod $method The method to be invoked.
508
 * @param CakeRequest $request The request to check.
509
 * @return boolean
510
 */
511
	protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request) {
512
		$privateAction = (
513
			$method->name[0] === '_' ||
514
			!$method->isPublic() ||
515
			!in_array($method->name, $this->methods)
516
		);
517
		$prefixes = Router::prefixes();
518
519
		if (!$privateAction && !empty($prefixes)) {
520
			if (empty($request->params['prefix']) && strpos($request->params['action'], '_') > 0) {
521
				list($prefix) = explode('_', $request->params['action']);
522
				$privateAction = in_array($prefix, $prefixes);
523
			}
524
		}
525
		return $privateAction;
526
	}
527
528
/**
529
 * Returns a scaffold object to use for dynamically scaffolded controllers.
530
 *
531
 * @param CakeRequest $request
532
 * @return Scaffold
533
 */
534
	protected function _getScaffold(CakeRequest $request) {
535
		return new Scaffold($this, $request);
536
	}
537
538
/**
539
 * Merge components, helpers, and uses vars from
540
 * Controller::$_mergeParent and PluginAppController.
541
 *
542
 * @return void
543
 */
544
	protected function _mergeControllerVars() {
545
		$pluginController = $pluginDot = null;
546
		$mergeParent = is_subclass_of($this, $this->_mergeParent);
547
		$pluginVars = array();
548
		$appVars = array();
549
550
		if (!empty($this->plugin)) {
551
			$pluginController = $this->plugin . 'AppController';
552
			if (!is_subclass_of($this, $pluginController)) {
553
				$pluginController = null;
554
			}
555
			$pluginDot = $this->plugin . '.';
556
		}
557
558
		if ($pluginController) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pluginController of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
559
			$merge = array('components', 'helpers');
560
			$this->_mergeVars($merge, $pluginController);
561
		}
562
563
		if ($mergeParent || !empty($pluginController)) {
564
			$appVars = get_class_vars($this->_mergeParent);
565
			$merge = array('components', 'helpers');
566
			$this->_mergeVars($merge, $this->_mergeParent, true);
567
		}
568
569
		if ($this->uses === null) {
570
			$this->uses = false;
571
		}
572
		if ($this->uses === true) {
573
			$this->uses = array($pluginDot . $this->modelClass);
574
		}
575
		if (isset($appVars['uses']) && $appVars['uses'] === $this->uses) {
576
			array_unshift($this->uses, $pluginDot . $this->modelClass);
577
		}
578
		if ($pluginController) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pluginController of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
579
			$pluginVars = get_class_vars($pluginController);
580
		}
581
		if ($this->uses !== false) {
582
			$this->_mergeUses($pluginVars);
583
			$this->_mergeUses($appVars);
584
		} else {
585
			$this->uses = array();
586
			$this->modelClass = '';
587
		}
588
	}
589
590
/**
591
 * Helper method for merging the $uses property together.
592
 *
593
 * Merges the elements not already in $this->uses into
594
 * $this->uses.
595
 *
596
 * @param array $merge The data to merge in.
597
 * @return void
598
 */
599
	protected function _mergeUses($merge) {
600
		if (!isset($merge['uses'])) {
601
			return;
602
		}
603
		if ($merge['uses'] === true) {
604
			return;
605
		}
606
		$this->uses = array_merge(
607
			$this->uses,
608
			array_diff($merge['uses'], $this->uses)
609
		);
610
	}
611
612
/**
613
 * Returns a list of all events that will fire in the controller during its lifecycle.
614
 * You can override this function to add you own listener callbacks
615
 *
616
 * @return array
617
 */
618
	public function implementedEvents() {
619
		return array(
620
			'Controller.initialize' => 'beforeFilter',
621
			'Controller.beforeRender' => 'beforeRender',
622
			'Controller.beforeRedirect' => array('callable' => 'beforeRedirect', 'passParams' => true),
623
			'Controller.shutdown' => 'afterFilter'
624
		);
625
	}
626
627
/**
628
 * Loads Model classes based on the uses property
629
 * see Controller::loadModel(); for more info.
630
 * Loads Components and prepares them for initialization.
631
 *
632
 * @return mixed true if models found and instance created.
633
 * @see Controller::loadModel()
634
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::constructClasses
635
 * @throws MissingModelException
636
 */
637
	public function constructClasses() {
638
		$this->_mergeControllerVars();
639
		if ($this->uses) {
640
			$this->uses = (array)$this->uses;
641
			list(, $this->modelClass) = pluginSplit(reset($this->uses));
642
		}
643
		$this->Components->init($this);
644
		return true;
645
	}
646
647
/**
648
 * Returns the CakeEventManager manager instance that is handling any callbacks.
649
 * You can use this instance to register any new listeners or callbacks to the
650
 * controller events, or create your own events and trigger them at will.
651
 *
652
 * @return CakeEventManager
653
 */
654 View Code Duplication
	public function getEventManager() {
655
		if (empty($this->_eventManager)) {
656
			$this->_eventManager = new CakeEventManager();
657
			$this->_eventManager->attach($this->Components);
658
			$this->_eventManager->attach($this);
659
		}
660
		return $this->_eventManager;
661
	}
662
663
/**
664
 * Perform the startup process for this controller.
665
 * Fire the Components and Controller callbacks in the correct order.
666
 *
667
 * - Initializes components, which fires their `initialize` callback
668
 * - Calls the controller `beforeFilter`.
669
 * - triggers Component `startup` methods.
670
 *
671
 * @return void
672
 */
673
	public function startupProcess() {
674
		$this->getEventManager()->dispatch(new CakeEvent('Controller.initialize', $this));
675
		$this->getEventManager()->dispatch(new CakeEvent('Controller.startup', $this));
676
	}
677
678
/**
679
 * Perform the various shutdown processes for this controller.
680
 * Fire the Components and Controller callbacks in the correct order.
681
 *
682
 * - triggers the component `shutdown` callback.
683
 * - calls the Controller's `afterFilter` method.
684
 *
685
 * @return void
686
 */
687
	public function shutdownProcess() {
688
		$this->getEventManager()->dispatch(new CakeEvent('Controller.shutdown', $this));
689
	}
690
691
/**
692
 * Queries & sets valid HTTP response codes & messages.
693
 *
694
 * @param integer|array $code If $code is an integer, then the corresponding code/message is
695
 *        returned if it exists, null if it does not exist. If $code is an array,
696
 *        then the 'code' and 'message' keys of each nested array are added to the default
697
 *        HTTP codes. Example:
698
 *
699
 *        httpCodes(404); // returns array(404 => 'Not Found')
700
 *
701
 *        httpCodes(array(
702
 *            701 => 'Unicorn Moved',
703
 *            800 => 'Unexpected Minotaur'
704
 *        )); // sets these new values, and returns true
705
 *
706
 * @return array Associative array of the HTTP codes as keys, and the message
707
 *    strings as values, or null of the given $code does not exist.
708
 * @deprecated Since 2.4. Will be removed in 3.0. Use CakeResponse::httpCodes().
709
 */
710
	public function httpCodes($code = null) {
711
		return $this->response->httpCodes($code);
712
	}
713
714
/**
715
 * Loads and instantiates models required by this controller.
716
 * If the model is non existent, it will throw a missing database table error, as CakePHP generates
717
 * dynamic models for the time being.
718
 *
719
 * @param string $modelClass Name of model class to load
720
 * @param integer|string $id Initial ID the instanced model class should have
721
 * @return boolean True if the model was found
722
 * @throws MissingModelException if the model class cannot be found.
723
 */
724
	public function loadModel($modelClass = null, $id = null) {
725
		if ($modelClass === null) {
726
			$modelClass = $this->modelClass;
727
		}
728
729
		$this->uses = ($this->uses) ? (array)$this->uses : array();
730
		if (!in_array($modelClass, $this->uses, true)) {
731
			$this->uses[] = $modelClass;
732
		}
733
734
		list($plugin, $modelClass) = pluginSplit($modelClass, true);
735
736
		$this->{$modelClass} = ClassRegistry::init(array(
737
			'class' => $plugin . $modelClass, 'alias' => $modelClass, 'id' => $id
738
		));
739
		if (!$this->{$modelClass}) {
740
			throw new MissingModelException($modelClass);
741
		}
742
		return true;
743
	}
744
745
/**
746
 * Redirects to given $url, after turning off $this->autoRender.
747
 * Script execution is halted after the redirect.
748
 *
749
 * @param string|array $url A string or array-based URL pointing to another location within the app,
750
 *     or an absolute URL
751
 * @param integer $status Optional HTTP status code (eg: 404)
752
 * @param boolean $exit If true, exit() will be called after the redirect
753
 * @return void
754
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::redirect
755
 */
756
	public function redirect($url, $status = null, $exit = true) {
757
		$this->autoRender = false;
758
759
		if (is_array($status)) {
760
			extract($status, EXTR_OVERWRITE);
761
		}
762
		$event = new CakeEvent('Controller.beforeRedirect', $this, array($url, $status, $exit));
763
764
		list($event->break, $event->breakOn, $event->collectReturn) = array(true, false, true);
765
		$this->getEventManager()->dispatch($event);
766
767
		if ($event->isStopped()) {
768
			return;
769
		}
770
		$response = $event->result;
771
		extract($this->_parseBeforeRedirect($response, $url, $status, $exit), EXTR_OVERWRITE);
0 ignored issues
show
Bug introduced by
It seems like $status defined by parameter $status on line 756 can also be of type array or null; however, Controller::_parseBeforeRedirect() does only seem to accept integer, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
772
773
		if ($url !== null) {
774
			$this->response->header('Location', Router::url($url, true));
775
		}
776
777
		if (is_string($status)) {
778
			$codes = array_flip($this->response->httpCodes());
779
			if (isset($codes[$status])) {
780
				$status = $codes[$status];
781
			}
782
		}
783
784
		if ($status) {
785
			$this->response->statusCode($status);
786
		}
787
788
		if ($exit) {
789
			$this->response->send();
790
			$this->_stop();
791
		}
792
	}
793
794
/**
795
 * Parse beforeRedirect Response
796
 *
797
 * @param mixed $response Response from beforeRedirect callback
798
 * @param string|array $url The same value of beforeRedirect
799
 * @param integer $status The same value of beforeRedirect
800
 * @param boolean $exit The same value of beforeRedirect
801
 * @return array Array with keys url, status and exit
802
 */
803
	protected function _parseBeforeRedirect($response, $url, $status, $exit) {
804
		if (is_array($response) && array_key_exists(0, $response)) {
805
			foreach ($response as $resp) {
806
				if (is_array($resp) && isset($resp['url'])) {
807
					extract($resp, EXTR_OVERWRITE);
808
				} elseif ($resp !== null) {
809
					$url = $resp;
810
				}
811
			}
812
		} elseif (is_array($response)) {
813
			extract($response, EXTR_OVERWRITE);
814
		}
815
		return compact('url', 'status', 'exit');
816
	}
817
818
/**
819
 * Convenience and object wrapper method for CakeResponse::header().
820
 *
821
 * @param string $status The header message that is being set.
822
 * @return void
823
 * @deprecated Will be removed in 3.0. Use CakeResponse::header().
824
 */
825
	public function header($status) {
826
		$this->response->header($status);
827
	}
828
829
/**
830
 * Saves a variable for use inside a view template.
831
 *
832
 * @param string|array $one A string or an array of data.
833
 * @param string|array $two Value in case $one is a string (which then works as the key).
834
 *   Unused if $one is an associative array, otherwise serves as the values to $one's keys.
835
 * @return void
836
 * @link http://book.cakephp.org/2.0/en/controllers.html#interacting-with-views
837
 */
838 View Code Duplication
	public function set($one, $two = null) {
839
		if (is_array($one)) {
840
			if (is_array($two)) {
841
				$data = array_combine($one, $two);
842
			} else {
843
				$data = $one;
844
			}
845
		} else {
846
			$data = array($one => $two);
847
		}
848
		$this->viewVars = $data + $this->viewVars;
849
	}
850
851
/**
852
 * Internally redirects one action to another. Does not perform another HTTP request unlike Controller::redirect()
853
 *
854
 * Examples:
855
 *
856
 * {{{
857
 * setAction('another_action');
858
 * setAction('action_with_parameters', $parameter1);
859
 * }}}
860
 *
861
 * @param string $action The new action to be 'redirected' to.
862
 *   Any other parameters passed to this method will be passed as parameters to the new action.
863
 * @return mixed Returns the return value of the called action
864
 */
865
	public function setAction($action) {
866
		$this->request->params['action'] = $action;
867
		$this->view = $action;
868
		$args = func_get_args();
869
		unset($args[0]);
870
		return call_user_func_array(array(&$this, $action), $args);
871
	}
872
873
/**
874
 * Returns number of errors in a submitted FORM.
875
 *
876
 * @return integer Number of errors
877
 * @deprecated This method will be removed in 3.0
878
 */
879
	public function validate() {
880
		$args = func_get_args();
881
		$errors = call_user_func_array(array(&$this, 'validateErrors'), $args);
882
883
		if ($errors === false) {
884
			return 0;
885
		}
886
		return count($errors);
887
	}
888
889
/**
890
 * Validates models passed by parameters. Example:
891
 *
892
 * `$errors = $this->validateErrors($this->Article, $this->User);`
893
 *
894
 * @param mixed A list of models as a variable argument
895
 * @return array Validation errors, or false if none
896
 * @deprecated This method will be removed in 3.0
897
 */
898
	public function validateErrors() {
899
		$objects = func_get_args();
900
901
		if (empty($objects)) {
902
			return false;
903
		}
904
905
		$errors = array();
906
		foreach ($objects as $object) {
907
			if (isset($this->{$object->alias})) {
908
				$object = $this->{$object->alias};
909
			}
910
			$object->set($object->data);
911
			$errors = array_merge($errors, $object->invalidFields());
912
		}
913
914
		return $this->validationErrors = (!empty($errors) ? $errors : false);
0 ignored issues
show
Documentation Bug introduced by
It seems like !empty($errors) ? $errors : false can also be of type false. However, the property $validationErrors is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
915
	}
916
917
/**
918
 * Instantiates the correct view class, hands it its data, and uses it to render the view output.
919
 *
920
 * @param string $view View to use for rendering
921
 * @param string $layout Layout to use
922
 * @return CakeResponse A response object containing the rendered view.
923
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::render
924
 */
925
	public function render($view = null, $layout = null) {
926
		$event = new CakeEvent('Controller.beforeRender', $this);
927
		$this->getEventManager()->dispatch($event);
928
		if ($event->isStopped()) {
929
			$this->autoRender = false;
930
			return $this->response;
931
		}
932
933
		if (!empty($this->uses) && is_array($this->uses)) {
934
			foreach ($this->uses as $model) {
935
				list($plugin, $className) = pluginSplit($model);
936
				$this->request->params['models'][$className] = compact('plugin', 'className');
937
			}
938
		}
939
940
		$this->View = $this->_getViewObject();
941
942
		$models = ClassRegistry::keys();
943
		foreach ($models as $currentModel) {
944
			$currentObject = ClassRegistry::getObject($currentModel);
945
			if ($currentObject instanceof Model) {
946
				$className = get_class($currentObject);
947
				list($plugin) = pluginSplit(App::location($className));
948
				$this->request->params['models'][$currentObject->alias] = compact('plugin', 'className');
949
				$this->View->validationErrors[$currentObject->alias] =& $currentObject->validationErrors;
950
			}
951
		}
952
953
		$this->autoRender = false;
954
		$this->response->body($this->View->render($view, $layout));
955
		return $this->response;
956
	}
957
958
/**
959
 * Returns the referring URL for this request.
960
 *
961
 * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers
962
 * @param boolean $local If true, restrict referring URLs to local server
963
 * @return string Referring URL
964
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::referer
965
 */
966
	public function referer($default = null, $local = false) {
967
		if (!$this->request) {
968
			return '/';
969
		}
970
971
		$referer = $this->request->referer($local);
972
		if ($referer === '/' && $default) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $default of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
973
			return Router::url($default, true);
974
		}
975
		return $referer;
976
	}
977
978
/**
979
 * Forces the user's browser not to cache the results of the current request.
980
 *
981
 * @return void
982
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::disableCache
983
 * @deprecated Will be removed in 3.0. Use CakeResponse::disableCache().
984
 */
985
	public function disableCache() {
986
		$this->response->disableCache();
987
	}
988
989
/**
990
 * Shows a message to the user for $pause seconds, then redirects to $url.
991
 * Uses flash.ctp as the default layout for the message.
992
 * Does not work if the current debug level is higher than 0.
993
 *
994
 * @param string $message Message to display to the user
995
 * @param string|array $url Relative string or array-based URL to redirect to after the time expires
996
 * @param integer $pause Time to show the message
997
 * @param string $layout Layout you want to use, defaults to 'flash'
998
 * @return void
999
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::flash
1000
 * @deprecated Will be removed in 3.0. Use Session::setFlash().
1001
 */
1002
	public function flash($message, $url, $pause = 1, $layout = 'flash') {
1003
		$this->autoRender = false;
1004
		$this->set('url', Router::url($url));
1005
		$this->set('message', $message);
1006
		$this->set('pause', $pause);
1007
		$this->set('page_title', $message);
1008
		$this->render(false, $layout);
1009
	}
1010
1011
/**
1012
 * Converts POST'ed form data to a model conditions array, suitable for use in a Model::find() call.
1013
 *
1014
 * @param array $data POST'ed data organized by model and field
1015
 * @param string|array $op A string containing an SQL comparison operator, or an array matching operators
1016
 *        to fields
1017
 * @param string $bool SQL boolean operator: AND, OR, XOR, etc.
1018
 * @param boolean $exclusive If true, and $op is an array, fields not included in $op will not be
1019
 *        included in the returned conditions
1020
 * @return array An array of model conditions
1021
 * @deprecated Will be removed in 3.0.
1022
 */
1023
	public function postConditions($data = array(), $op = null, $bool = 'AND', $exclusive = false) {
1024
		if (!is_array($data) || empty($data)) {
1025
			if (!empty($this->request->data)) {
1026
				$data = $this->request->data;
1027
			} else {
1028
				return null;
1029
			}
1030
		}
1031
		$cond = array();
1032
1033
		if ($op === null) {
1034
			$op = '';
1035
		}
1036
1037
		$arrayOp = is_array($op);
1038
		foreach ($data as $model => $fields) {
1039
			foreach ($fields as $field => $value) {
1040
				$key = $model . '.' . $field;
1041
				$fieldOp = $op;
1042
				if ($arrayOp) {
1043
					if (array_key_exists($key, $op)) {
1044
						$fieldOp = $op[$key];
1045
					} elseif (array_key_exists($field, $op)) {
1046
						$fieldOp = $op[$field];
1047
					} else {
1048
						$fieldOp = false;
1049
					}
1050
				}
1051
				if ($exclusive && $fieldOp === false) {
1052
					continue;
1053
				}
1054
				$fieldOp = strtoupper(trim($fieldOp));
1055
				if ($fieldOp === 'LIKE') {
1056
					$key = $key . ' LIKE';
1057
					$value = '%' . $value . '%';
1058
				} elseif ($fieldOp && $fieldOp !== '=') {
1059
					$key = $key . ' ' . $fieldOp;
1060
				}
1061
				$cond[$key] = $value;
1062
			}
1063
		}
1064
		if ($bool && strtoupper($bool) !== 'AND') {
1065
			$cond = array($bool => $cond);
1066
		}
1067
		return $cond;
1068
	}
1069
1070
/**
1071
 * Handles automatic pagination of model records.
1072
 *
1073
 * @param Model|string $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
1074
 * @param string|array $scope Conditions to use while paginating
1075
 * @param array $whitelist List of allowed options for paging
1076
 * @return array Model query results
1077
 * @link http://book.cakephp.org/2.0/en/controllers.html#Controller::paginate
1078
 */
1079
	public function paginate($object = null, $scope = array(), $whitelist = array()) {
1080
		return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Component as the method paginate() does only exist in the following sub-classes of Component: PaginatorComponent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1081
	}
1082
1083
/**
1084
 * Called before the controller action. You can use this method to configure and customize components
1085
 * or perform logic that needs to happen before each controller action.
1086
 *
1087
 * @return void
1088
 * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1089
 */
1090
	public function beforeFilter() {
1091
	}
1092
1093
/**
1094
 * Called after the controller action is run, but before the view is rendered. You can use this method
1095
 * to perform logic or set view variables that are required on every request.
1096
 *
1097
 * @return void
1098
 * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1099
 */
1100
	public function beforeRender() {
1101
	}
1102
1103
/**
1104
 * The beforeRedirect method is invoked when the controller's redirect method is called but before any
1105
 * further action.
1106
 *
1107
 * If this method returns false the controller will not continue on to redirect the request.
1108
 * The $url, $status and $exit variables have same meaning as for the controller's method. You can also
1109
 * return a string which will be interpreted as the URL to redirect to or return associative array with
1110
 * key 'url' and optionally 'status' and 'exit'.
1111
 *
1112
 * @param string|array $url A string or array-based URL pointing to another location within the app,
1113
 *     or an absolute URL
1114
 * @param integer $status Optional HTTP status code (eg: 404)
1115
 * @param boolean $exit If true, exit() will be called after the redirect
1116
 * @return mixed
1117
 *   false to stop redirection event,
1118
 *   string controllers a new redirection URL or
1119
 *   array with the keys url, status and exit to be used by the redirect method.
1120
 * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1121
 */
1122
	public function beforeRedirect($url, $status = null, $exit = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $url is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $status is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $exit is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1123
	}
1124
1125
/**
1126
 * Called after the controller action is run and rendered.
1127
 *
1128
 * @return void
1129
 * @link http://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
1130
 */
1131
	public function afterFilter() {
1132
	}
1133
1134
/**
1135
 * This method should be overridden in child classes.
1136
 *
1137
 * @param string $method name of method called example index, edit, etc.
1138
 * @return boolean Success
1139
 * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1140
 */
1141
	public function beforeScaffold($method) {
0 ignored issues
show
Unused Code introduced by
The parameter $method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1142
		return true;
1143
	}
1144
1145
/**
1146
 * Alias to beforeScaffold()
1147
 *
1148
 * @param string $method
1149
 * @return boolean
1150
 * @see Controller::beforeScaffold()
1151
 * @deprecated Will be removed in 3.0.
1152
 */
1153
	protected function _beforeScaffold($method) {
1154
		return $this->beforeScaffold($method);
1155
	}
1156
1157
/**
1158
 * This method should be overridden in child classes.
1159
 *
1160
 * @param string $method name of method called either edit or update.
1161
 * @return boolean Success
1162
 * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1163
 */
1164
	public function afterScaffoldSave($method) {
0 ignored issues
show
Unused Code introduced by
The parameter $method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1165
		return true;
1166
	}
1167
1168
/**
1169
 * Alias to afterScaffoldSave()
1170
 *
1171
 * @param string $method
1172
 * @return boolean
1173
 * @see Controller::afterScaffoldSave()
1174
 * @deprecated Will be removed in 3.0.
1175
 */
1176
	protected function _afterScaffoldSave($method) {
1177
		return $this->afterScaffoldSave($method);
1178
	}
1179
1180
/**
1181
 * This method should be overridden in child classes.
1182
 *
1183
 * @param string $method name of method called either edit or update.
1184
 * @return boolean Success
1185
 * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1186
 */
1187
	public function afterScaffoldSaveError($method) {
0 ignored issues
show
Unused Code introduced by
The parameter $method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1188
		return true;
1189
	}
1190
1191
/**
1192
 * Alias to afterScaffoldSaveError()
1193
 *
1194
 * @param string $method
1195
 * @return boolean
1196
 * @see Controller::afterScaffoldSaveError()
1197
 * @deprecated Will be removed in 3.0.
1198
 */
1199
	protected function _afterScaffoldSaveError($method) {
1200
		return $this->afterScaffoldSaveError($method);
1201
	}
1202
1203
/**
1204
 * This method should be overridden in child classes.
1205
 * If not it will render a scaffold error.
1206
 * Method MUST return true in child classes
1207
 *
1208
 * @param string $method name of method called example index, edit, etc.
1209
 * @return boolean Success
1210
 * @link http://book.cakephp.org/2.0/en/controllers.html#callbacks
1211
 */
1212
	public function scaffoldError($method) {
0 ignored issues
show
Unused Code introduced by
The parameter $method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1213
		return false;
1214
	}
1215
1216
/**
1217
 * Alias to scaffoldError()
1218
 *
1219
 * @param string $method
1220
 * @return boolean
1221
 * @see Controller::scaffoldError()
1222
 * @deprecated Will be removed in 3.0.
1223
 */
1224
	protected function _scaffoldError($method) {
1225
		return $this->scaffoldError($method);
1226
	}
1227
1228
/**
1229
 * Constructs the view class instance based on the controller property
1230
 *
1231
 * @return View
1232
 */
1233
	protected function _getViewObject() {
1234
		$viewClass = $this->viewClass;
1235 View Code Duplication
		if ($this->viewClass !== 'View') {
1236
			list($plugin, $viewClass) = pluginSplit($viewClass, true);
1237
			$viewClass = $viewClass . 'View';
1238
			App::uses($viewClass, $plugin . 'View');
1239
		}
1240
1241
		return new $viewClass($this);
1242
	}
1243
1244
1245
}
1246