Completed
Pull Request — master (#55)
by Reginaldo
65:36 queued 30:55
created

Helper::beforeLayout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 2
rs 10
cc 1
eloc 1
nc 1
nop 1
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.View
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('Router', 'Routing');
18
App::uses('Hash', 'Utility');
19
App::uses('Inflector', 'Utility');
20
21
/**
22
 * Abstract base class for all other Helpers in CakePHP.
23
 * Provides common methods and features.
24
 *
25
 * @package       Cake.View
26
 */
27
class Helper extends Object {
28
29
/**
30
 * Settings for this helper.
31
 *
32
 * @var array
33
 */
34
	public $settings = array();
35
36
/**
37
 * List of helpers used by this helper
38
 *
39
 * @var array
40
 */
41
	public $helpers = array();
42
43
/**
44
 * A helper lookup table used to lazy load helper objects.
45
 *
46
 * @var array
47
 */
48
	protected $_helperMap = array();
49
50
/**
51
 * The current theme name if any.
52
 *
53
 * @var string
54
 */
55
	public $theme = null;
56
57
/**
58
 * Request object
59
 *
60
 * @var CakeRequest
61
 */
62
	public $request = null;
63
64
/**
65
 * Plugin path
66
 *
67
 * @var string
68
 */
69
	public $plugin = null;
70
71
/**
72
 * Holds the fields array('field_name' => array('type' => 'string', 'length' => 100),
73
 * primaryKey and validates array('field_name')
74
 *
75
 * @var array
76
 */
77
	public $fieldset = array();
78
79
/**
80
 * Holds tag templates.
81
 *
82
 * @var array
83
 */
84
	public $tags = array();
85
86
/**
87
 * Holds the content to be cleaned.
88
 *
89
 * @var mixed
90
 */
91
	protected $_tainted = null;
92
93
/**
94
 * Holds the cleaned content.
95
 *
96
 * @var mixed
97
 */
98
	protected $_cleaned = null;
99
100
/**
101
 * The View instance this helper is attached to
102
 *
103
 * @var View
104
 */
105
	protected $_View;
106
107
/**
108
 * A list of strings that should be treated as suffixes, or
109
 * sub inputs for a parent input. This is used for date/time
110
 * inputs primarily.
111
 *
112
 * @var array
113
 */
114
	protected $_fieldSuffixes = array(
115
		'year', 'month', 'day', 'hour', 'min', 'second', 'meridian'
116
	);
117
118
/**
119
 * The name of the current model entities are in scope of.
120
 *
121
 * @see Helper::setEntity()
122
 * @var string
123
 */
124
	protected $_modelScope;
125
126
/**
127
 * The name of the current model association entities are in scope of.
128
 *
129
 * @see Helper::setEntity()
130
 * @var string
131
 */
132
	protected $_association;
133
134
/**
135
 * The dot separated list of elements the current field entity is for.
136
 *
137
 * @see Helper::setEntity()
138
 * @var string
139
 */
140
	protected $_entityPath;
141
142
/**
143
 * Minimized attributes
144
 *
145
 * @var array
146
 */
147
	protected $_minimizedAttributes = array(
148
		'compact', 'checked', 'declare', 'readonly', 'disabled', 'selected',
149
		'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize',
150
		'autoplay', 'controls', 'loop', 'muted', 'required', 'novalidate', 'formnovalidate'
151
	);
152
153
/**
154
 * Format to attribute
155
 *
156
 * @var string
157
 */
158
	protected $_attributeFormat = '%s="%s"';
159
160
/**
161
 * Format to attribute
162
 *
163
 * @var string
164
 */
165
	protected $_minimizedAttributeFormat = '%s="%s"';
166
167
/**
168
 * Default Constructor
169
 *
170
 * @param View $View The View this helper is being attached to.
171
 * @param array $settings Configuration settings for the helper.
172
 */
173
	public function __construct(View $View, $settings = array()) {
174
		$this->_View = $View;
175
		$this->request = $View->request;
176
		if ($settings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $settings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
177
			$this->settings = Hash::merge($this->settings, $settings);
178
		}
179
		if (!empty($this->helpers)) {
180
			$this->_helperMap = ObjectCollection::normalizeObjectArray($this->helpers);
181
		}
182
	}
183
184
/**
185
 * Provide non fatal errors on missing method calls.
186
 *
187
 * @param string $method Method to invoke
188
 * @param array $params Array of params for the method.
189
 * @return void
190
 */
191
	public function __call($method, $params) {
192
		trigger_error(__d('cake_dev', 'Method %1$s::%2$s does not exist', get_class($this), $method), E_USER_WARNING);
193
	}
194
195
/**
196
 * Lazy loads helpers. Provides access to deprecated request properties as well.
197
 *
198
 * @param string $name Name of the property being accessed.
199
 * @return mixed Helper or property found at $name
200
 * @deprecated Accessing request properties through this method is deprecated and will be removed in 3.0.
201
 */
202
	public function __get($name) {
203
		if (isset($this->_helperMap[$name]) && !isset($this->{$name})) {
204
			$settings = array_merge((array)$this->_helperMap[$name]['settings'], array('enabled' => false));
205
			$this->{$name} = $this->_View->loadHelper($this->_helperMap[$name]['class'], $settings);
206
		}
207
		if (isset($this->{$name})) {
208
			return $this->{$name};
209
		}
210 View Code Duplication
		switch ($name) {
211
			case 'base':
212
			case 'here':
213
			case 'webroot':
214
			case 'data':
215
				return $this->request->{$name};
216
			case 'action':
217
				return isset($this->request->params['action']) ? $this->request->params['action'] : '';
218
			case 'params':
219
				return $this->request;
220
		}
221
	}
222
223
/**
224
 * Provides backwards compatibility access for setting values to the request object.
225
 *
226
 * @param string $name Name of the property being accessed.
227
 * @param mixed $value
228
 * @return void
229
 * @deprecated This method will be removed in 3.0
230
 */
231
	public function __set($name, $value) {
232
		switch ($name) {
233
			case 'base':
234
			case 'here':
235
			case 'webroot':
236
			case 'data':
237
				$this->request->{$name} = $value;
238
				return;
239
			case 'action':
240
				$this->request->params['action'] = $value;
241
				return;
242
		}
243
		$this->{$name} = $value;
244
	}
245
246
/**
247
 * Finds URL for specified action.
248
 *
249
 * Returns a URL pointing at the provided parameters.
250
 *
251
 * @param string|array $url Either a relative string url like `/products/view/23` or
252
 *    an array of URL parameters. Using an array for URLs will allow you to leverage
253
 *    the reverse routing features of CakePHP.
254
 * @param boolean $full If true, the full base URL will be prepended to the result
255
 * @return string Full translated URL with base path.
256
 * @link http://book.cakephp.org/2.0/en/views/helpers.html
257
 */
258
	public function url($url = null, $full = false) {
259
		return h(Router::url($url, $full));
260
	}
261
262
/**
263
 * Checks if a file exists when theme is used, if no file is found default location is returned
264
 *
265
 * @param string $file The file to create a webroot path to.
266
 * @return string Web accessible path to file.
267
 */
268
	public function webroot($file) {
269
		$asset = explode('?', $file);
270
		$asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
271
		$webPath = "{$this->request->webroot}" . $asset[0];
272
		$file = $asset[0];
273
274
		if (!empty($this->theme)) {
275
			$file = trim($file, '/');
276
			$theme = $this->theme . '/';
277
278
			if (DS === '\\') {
279
				$file = str_replace('/', '\\', $file);
280
			}
281
282
			if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS . $file)) {
283
				$webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
284
			} else {
285
				$themePath = App::themePath($this->theme);
286
				$path = $themePath . 'webroot' . DS . $file;
287
				if (file_exists($path)) {
288
					$webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
289
				}
290
			}
291
		}
292
		if (strpos($webPath, '//') !== false) {
293
			return str_replace('//', '/', $webPath . $asset[1]);
294
		}
295
		return $webPath . $asset[1];
296
	}
297
298
/**
299
 * Generate URL for given asset file. Depending on options passed provides full URL with domain name.
300
 * Also calls Helper::assetTimestamp() to add timestamp to local files
301
 *
302
 * @param string|array Path string or URL array
303
 * @param array $options Options array. Possible keys:
304
 *   `fullBase` Return full URL with domain name
305
 *   `pathPrefix` Path prefix for relative URLs
306
 *   `ext` Asset extension to append
307
 *   `plugin` False value will prevent parsing path as a plugin
308
 * @return string Generated URL
309
 */
310
	public function assetUrl($path, $options = array()) {
311
		if (is_array($path)) {
312
			return $this->url($path, !empty($options['fullBase']));
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->url($path,...$options['fullBase'])); (array|boolean|string) is incompatible with the return type documented by Helper::assetUrl of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
313
		}
314
		if (strpos($path, '://') !== false) {
315
			return $path;
316
		}
317
		if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
318
			list($plugin, $path) = $this->_View->pluginSplit($path, false);
319
		}
320
		if (!empty($options['pathPrefix']) && $path[0] !== '/') {
321
			$path = $options['pathPrefix'] . $path;
322
		}
323 View Code Duplication
		if (
324
			!empty($options['ext']) &&
325
			strpos($path, '?') === false &&
326
			substr($path, -strlen($options['ext'])) !== $options['ext']
327
		) {
328
			$path .= $options['ext'];
329
		}
330
		if (preg_match('|^([a-z0-9]+:)?//|', $path)) {
331
			return $path;
332
		}
333
		if (isset($plugin)) {
334
			$path = Inflector::underscore($plugin) . '/' . $path;
335
		}
336
		$path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path)));
337
338
		if (!empty($options['fullBase'])) {
339
			$path = rtrim(Router::fullBaseUrl(), '/') . '/' . ltrim($path, '/');
340
		}
341
		return $path;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $path; (array|boolean|string) is incompatible with the return type documented by Helper::assetUrl of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
342
	}
343
344
/**
345
 * Encodes a URL for use in HTML attributes.
346
 *
347
 * @param string $url The URL to encode.
348
 * @return string The URL encoded for both URL & HTML contexts.
349
 */
350
	protected function _encodeUrl($url) {
351
		$path = parse_url($url, PHP_URL_PATH);
352
		$parts = array_map('rawurldecode', explode('/', $path));
353
		$parts = array_map('rawurlencode', $parts);
354
		$encoded = implode('/', $parts);
355
		return h(str_replace($path, $encoded, $url));
356
	}
357
358
/**
359
 * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
360
 * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp === 'force'
361
 * a timestamp will be added.
362
 *
363
 * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
364
 * @return string Path with a timestamp added, or not.
365
 */
366
	public function assetTimestamp($path) {
367
		$stamp = Configure::read('Asset.timestamp');
368
		$timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug') > 0);
369
		if ($timestampEnabled && strpos($path, '?') === false) {
370
			$filepath = preg_replace(
371
				'/^' . preg_quote($this->request->webroot, '/') . '/',
372
				'',
373
				urldecode($path)
374
			);
375
			$webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
376
			if (file_exists($webrootPath)) {
377
				//@codingStandardsIgnoreStart
378
				return $path . '?' . @filemtime($webrootPath);
379
				//@codingStandardsIgnoreEnd
380
			}
381
			$segments = explode('/', ltrim($filepath, '/'));
382
			if ($segments[0] === 'theme') {
383
				$theme = $segments[1];
384
				unset($segments[0], $segments[1]);
385
				$themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
386
				//@codingStandardsIgnoreStart
387
				return $path . '?' . @filemtime($themePath);
388
				//@codingStandardsIgnoreEnd
389
			} else {
390
				$plugin = Inflector::camelize($segments[0]);
391
				if (CakePlugin::loaded($plugin)) {
392
					unset($segments[0]);
393
					$pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
394
					//@codingStandardsIgnoreStart
395
					return $path . '?' . @filemtime($pluginPath);
396
					//@codingStandardsIgnoreEnd
397
				}
398
			}
399
		}
400
		return $path;
401
	}
402
403
/**
404
 * Used to remove harmful tags from content. Removes a number of well known XSS attacks
405
 * from content. However, is not guaranteed to remove all possibilities. Escaping
406
 * content is the best way to prevent all possible attacks.
407
 *
408
 * @param string|array $output Either an array of strings to clean or a single string to clean.
409
 * @return string|array cleaned content for output
410
 * @deprecated This method will be removed in 3.0
411
 */
412
	public function clean($output) {
413
		$this->_reset();
414
		if (empty($output)) {
415
			return null;
416
		}
417
		if (is_array($output)) {
418
			foreach ($output as $key => $value) {
419
				$return[$key] = $this->clean($value);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$return was never initialized. Although not strictly required by PHP, it is generally a good practice to add $return = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
Deprecated Code introduced by
The method Helper::clean() has been deprecated with message: This method will be removed in 3.0

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
420
			}
421
			return $return;
0 ignored issues
show
Bug introduced by
The variable $return does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
422
		}
423
		$this->_tainted = $output;
424
		$this->_clean();
425
		return $this->_cleaned;
426
	}
427
428
/**
429
 * Returns a space-delimited string with items of the $options array. If a key
430
 * of $options array happens to be one of those listed in `Helper::$_minimizedAttributes`
431
 *
432
 * And its value is one of:
433
 *
434
 * - '1' (string)
435
 * - 1 (integer)
436
 * - true (boolean)
437
 * - 'true' (string)
438
 *
439
 * Then the value will be reset to be identical with key's name.
440
 * If the value is not one of these 3, the parameter is not output.
441
 *
442
 * 'escape' is a special option in that it controls the conversion of
443
 *  attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
444
 *
445
 * If value for any option key is set to `null` or `false`, that option will be excluded from output.
446
 *
447
 * @param array $options Array of options.
448
 * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
449
 * @param string $insertBefore String to be inserted before options.
450
 * @param string $insertAfter String to be inserted after options.
451
 * @return string Composed attributes.
452
 * @deprecated This method will be moved to HtmlHelper in 3.0
453
 */
454
	protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
455
		if (!is_string($options)) {
456
			$options = (array)$options + array('escape' => true);
457
458
			if (!is_array($exclude)) {
459
				$exclude = array();
460
			}
461
462
			$exclude = array('escape' => true) + array_flip($exclude);
463
			$escape = $options['escape'];
464
			$attributes = array();
465
466
			foreach ($options as $key => $value) {
467
				if (!isset($exclude[$key]) && $value !== false && $value !== null) {
468
					$attributes[] = $this->_formatAttribute($key, $value, $escape);
0 ignored issues
show
Deprecated Code introduced by
The method Helper::_formatAttribute() has been deprecated with message: This method will be moved to HtmlHelper in 3.0

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
469
				}
470
			}
471
			$out = implode(' ', $attributes);
472
		} else {
473
			$out = $options;
474
		}
475
		return $out ? $insertBefore . $out . $insertAfter : '';
476
	}
477
478
/**
479
 * Formats an individual attribute, and returns the string value of the composed attribute.
480
 * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
481
 *
482
 * @param string $key The name of the attribute to create
483
 * @param string $value The value of the attribute to create.
484
 * @param boolean $escape Define if the value must be escaped
485
 * @return string The composed attribute.
486
 * @deprecated This method will be moved to HtmlHelper in 3.0
487
 */
488
	protected function _formatAttribute($key, $value, $escape = true) {
489
		if (is_array($value)) {
490
			$value = implode(' ', $value);
491
		}
492
		if (is_numeric($key)) {
493
			return sprintf($this->_minimizedAttributeFormat, $value, $value);
494
		}
495
		$truthy = array(1, '1', true, 'true', $key);
496
		$isMinimized = in_array($key, $this->_minimizedAttributes);
497
		if ($isMinimized && in_array($value, $truthy, true)) {
498
			return sprintf($this->_minimizedAttributeFormat, $key, $key);
499
		}
500
		if ($isMinimized) {
501
			return '';
502
		}
503
		return sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
504
	}
505
506
/**
507
 * Returns a string to be used as onclick handler for confirm dialogs.
508
 *
509
 * @param string $message Message to be displayed
510
 * @param string $okCode Code to be executed after user chose 'OK'
511
 * @param string $cancelCode Code to be executed after user chose 'Cancel'
512
 * @param array $options Array of options
513
 * @return string onclick JS code
514
 */
515
	protected function _confirm($message, $okCode, $cancelCode = '', $options = array()) {
516
		$message = json_encode($message);
517
		$confirm = "if (confirm({$message})) { {$okCode} } {$cancelCode}";
518 View Code Duplication
		if (isset($options['escape']) && $options['escape'] === false) {
519
			$confirm = h($confirm);
520
		}
521
		return $confirm;
522
	}
523
524
/**
525
 * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
526
 *
527
 * @param string $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
528
 * @param boolean $setScope Sets the view scope to the model specified in $tagValue
529
 * @return void
530
 */
531
	public function setEntity($entity, $setScope = false) {
532
		if ($entity === null) {
533
			$this->_modelScope = false;
0 ignored issues
show
Documentation Bug introduced by
The property $_modelScope was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
534
		}
535
		if ($setScope === true) {
536
			$this->_modelScope = $entity;
537
		}
538
		$parts = array_values(Hash::filter(explode('.', $entity)));
539
		if (empty($parts)) {
540
			return;
541
		}
542
		$count = count($parts);
543
		$lastPart = isset($parts[$count - 1]) ? $parts[$count - 1] : null;
544
545
		// Either 'body' or 'date.month' type inputs.
546
		if (
547
			($count === 1 && $this->_modelScope && !$setScope) ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_modelScope of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
548
			(
549
				$count === 2 &&
550
				in_array($lastPart, $this->_fieldSuffixes) &&
551
				$this->_modelScope &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_modelScope of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
552
				$parts[0] !== $this->_modelScope
553
			)
554
		) {
555
			$entity = $this->_modelScope . '.' . $entity;
556
		}
557
558
		// 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them.
559
		if (
560
			$count >= 2 &&
561
			is_numeric($parts[0]) &&
562
			!is_numeric($parts[1]) &&
563
			$this->_modelScope &&
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_modelScope of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
564
			strpos($entity, $this->_modelScope) === false
565
		) {
566
			$entity = $this->_modelScope . '.' . $entity;
567
		}
568
569
		$this->_association = null;
570
571
		$isHabtm = (
572
			isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) &&
573
			$this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple'
574
		);
575
576
		// habtm models are special
577
		if ($count === 1 && $isHabtm) {
578
			$this->_association = $parts[0];
579
			$entity = $parts[0] . '.' . $parts[0];
580
		} else {
581
			// check for associated model.
582
			$reversed = array_reverse($parts);
583
			foreach ($reversed as $i => $part) {
584
				if ($i > 0 && preg_match('/^[A-Z]/', $part)) {
585
					$this->_association = $part;
586
					break;
587
				}
588
			}
589
		}
590
		$this->_entityPath = $entity;
591
	}
592
593
/**
594
 * Returns the entity reference of the current context as an array of identity parts
595
 *
596
 * @return array An array containing the identity elements of an entity
597
 */
598
	public function entity() {
599
		return explode('.', $this->_entityPath);
600
	}
601
602
/**
603
 * Gets the currently-used model of the rendering context.
604
 *
605
 * @return string
606
 */
607
	public function model() {
608
		if ($this->_association) {
609
			return $this->_association;
610
		}
611
		return $this->_modelScope;
612
	}
613
614
/**
615
 * Gets the currently-used model field of the rendering context.
616
 * Strips off field suffixes such as year, month, day, hour, min, meridian
617
 * when the current entity is longer than 2 elements.
618
 *
619
 * @return string
620
 */
621
	public function field() {
622
		$entity = $this->entity();
623
		$count = count($entity);
624
		$last = $entity[$count - 1];
625
		if ($count > 2 && in_array($last, $this->_fieldSuffixes)) {
626
			$last = isset($entity[$count - 2]) ? $entity[$count - 2] : null;
627
		}
628
		return $last;
629
	}
630
631
/**
632
 * Generates a DOM ID for the selected element, if one is not set.
633
 * Uses the current View::entity() settings to generate a CamelCased id attribute.
634
 *
635
 * @param array|string $options Either an array of html attributes to add $id into, or a string
636
 *   with a view entity path to get a domId for.
637
 * @param string $id The name of the 'id' attribute.
638
 * @return mixed If $options was an array, an array will be returned with $id set. If a string
639
 *   was supplied, a string will be returned.
640
 */
641
	public function domId($options = null, $id = 'id') {
642
		if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
643
			unset($options[$id]);
644
			return $options;
645
		} elseif (!is_array($options) && $options !== null) {
646
			$this->setEntity($options);
647
			return $this->domId();
648
		}
649
650
		$entity = $this->entity();
651
		$model = array_shift($entity);
652
		$dom = $model . implode('', array_map(array('Inflector', 'camelize'), $entity));
653
654
		if (is_array($options) && !array_key_exists($id, $options)) {
655
			$options[$id] = $dom;
656
		} elseif ($options === null) {
657
			return $dom;
658
		}
659
		return $options;
660
	}
661
662
/**
663
 * Gets the input field name for the current tag. Creates input name attributes
664
 * using CakePHP's data[Model][field] formatting.
665
 *
666
 * @param array|string $options If an array, should be an array of attributes that $key needs to be added to.
667
 *   If a string or null, will be used as the View entity.
668
 * @param string $field
669
 * @param string $key The name of the attribute to be set, defaults to 'name'
670
 * @return mixed If an array was given for $options, an array with $key set will be returned.
671
 *   If a string was supplied a string will be returned.
672
 */
673
	protected function _name($options = array(), $field = null, $key = 'name') {
674 View Code Duplication
		if ($options === null) {
675
			$options = array();
676
		} elseif (is_string($options)) {
677
			$field = $options;
678
			$options = 0;
679
		}
680
681
		if (!empty($field)) {
682
			$this->setEntity($field);
683
		}
684
685
		if (is_array($options) && array_key_exists($key, $options)) {
686
			return $options;
687
		}
688
689
		switch ($field) {
690
			case '_method':
691
				$name = $field;
692
				break;
693
			default:
694
				$name = 'data[' . implode('][', $this->entity()) . ']';
695
		}
696
697
		if (is_array($options)) {
698
			$options[$key] = $name;
699
			return $options;
700
		}
701
		return $name;
702
	}
703
704
/**
705
 * Gets the data for the current tag
706
 *
707
 * @param array|string $options If an array, should be an array of attributes that $key needs to be added to.
708
 *   If a string or null, will be used as the View entity.
709
 * @param string $field
710
 * @param string $key The name of the attribute to be set, defaults to 'value'
711
 * @return mixed If an array was given for $options, an array with $key set will be returned.
712
 *   If a string was supplied a string will be returned.
713
 */
714
	public function value($options = array(), $field = null, $key = 'value') {
715 View Code Duplication
		if ($options === null) {
716
			$options = array();
717
		} elseif (is_string($options)) {
718
			$field = $options;
719
			$options = 0;
720
		}
721
722
		if (is_array($options) && isset($options[$key])) {
723
			return $options;
724
		}
725
726
		if (!empty($field)) {
727
			$this->setEntity($field);
728
		}
729
		$result = null;
730
		$data = $this->request->data;
731
732
		$entity = $this->entity();
733
		if (!empty($data) && is_array($data) && !empty($entity)) {
734
			$result = Hash::get($data, implode('.', $entity));
735
		}
736
737
		$habtmKey = $this->field();
738
		if (empty($result) && isset($data[$habtmKey][$habtmKey]) && is_array($data[$habtmKey])) {
739
			$result = $data[$habtmKey][$habtmKey];
740
		} elseif (empty($result) && isset($data[$habtmKey]) && is_array($data[$habtmKey])) {
741
			if (ClassRegistry::isKeySet($habtmKey)) {
742
				$model = ClassRegistry::getObject($habtmKey);
743
				$result = $this->_selectedArray($data[$habtmKey], $model->primaryKey);
744
			}
745
		}
746
747
		if (is_array($options)) {
748
			if ($result === null && isset($options['default'])) {
749
				$result = $options['default'];
750
			}
751
			unset($options['default']);
752
		}
753
754
		if (is_array($options)) {
755
			$options[$key] = $result;
756
			return $options;
757
		}
758
		return $result;
759
	}
760
761
/**
762
 * Sets the defaults for an input tag. Will set the
763
 * name, value, and id attributes for an array of html attributes.
764
 *
765
 * @param string $field The field name to initialize.
766
 * @param array $options Array of options to use while initializing an input field.
767
 * @return array Array options for the form input.
768
 */
769
	protected function _initInputField($field, $options = array()) {
770
		if ($field !== null) {
771
			$this->setEntity($field);
772
		}
773
		$options = (array)$options;
774
		$options = $this->_name($options);
775
		$options = $this->value($options);
0 ignored issues
show
Bug introduced by
It seems like $options defined by $this->value($options) on line 775 can also be of type null; however, Helper::value() does only seem to accept array|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...
776
		$options = $this->domId($options);
777
		return $options;
778
	}
779
780
/**
781
 * Adds the given class to the element options
782
 *
783
 * @param array $options Array options/attributes to add a class to
784
 * @param string $class The class name being added.
785
 * @param string $key the key to use for class.
786
 * @return array Array of options with $key set.
787
 */
788
	public function addClass($options = array(), $class = null, $key = 'class') {
789
		if (isset($options[$key]) && trim($options[$key])) {
790
			$options[$key] .= ' ' . $class;
791
		} else {
792
			$options[$key] = $class;
793
		}
794
		return $options;
795
	}
796
797
/**
798
 * Returns a string generated by a helper method
799
 *
800
 * This method can be overridden in subclasses to do generalized output post-processing
801
 *
802
 * @param string $str String to be output.
803
 * @return string
804
 * @deprecated This method will be removed in future versions.
805
 */
806
	public function output($str) {
807
		return $str;
808
	}
809
810
/**
811
 * Before render callback. beforeRender is called before the view file is rendered.
812
 *
813
 * Overridden in subclasses.
814
 *
815
 * @param string $viewFile The view file that is going to be rendered
816
 * @return void
817
 */
818
	public function beforeRender($viewFile) {
819
	}
820
821
/**
822
 * After render callback. afterRender is called after the view file is rendered
823
 * but before the layout has been rendered.
824
 *
825
 * Overridden in subclasses.
826
 *
827
 * @param string $viewFile The view file that was rendered.
828
 * @return void
829
 */
830
	public function afterRender($viewFile) {
0 ignored issues
show
Unused Code introduced by
The parameter $viewFile 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...
831
	}
832
833
/**
834
 * Before layout callback. beforeLayout is called before the layout is rendered.
835
 *
836
 * Overridden in subclasses.
837
 *
838
 * @param string $layoutFile The layout about to be rendered.
839
 * @return void
840
 */
841
	public function beforeLayout($layoutFile) {
0 ignored issues
show
Unused Code introduced by
The parameter $layoutFile 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...
842
	}
843
844
/**
845
 * After layout callback. afterLayout is called after the layout has rendered.
846
 *
847
 * Overridden in subclasses.
848
 *
849
 * @param string $layoutFile The layout file that was rendered.
850
 * @return void
851
 */
852
	public function afterLayout($layoutFile) {
853
	}
854
855
/**
856
 * Before render file callback.
857
 * Called before any view fragment is rendered.
858
 *
859
 * Overridden in subclasses.
860
 *
861
 * @param string $viewFile The file about to be rendered.
862
 * @return void
863
 */
864
	public function beforeRenderFile($viewFile) {
865
	}
866
867
/**
868
 * After render file callback.
869
 * Called after any view fragment is rendered.
870
 *
871
 * Overridden in subclasses.
872
 *
873
 * @param string $viewFile The file just be rendered.
874
 * @param string $content The content that was rendered.
875
 * @return void
876
 */
877
	public function afterRenderFile($viewFile, $content) {
878
	}
879
880
/**
881
 * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
882
 * options for a multiple select element
883
 *
884
 * @param string|array $data
885
 * @param string $key
886
 * @return array
887
 */
888
	protected function _selectedArray($data, $key = 'id') {
889
		if (!is_array($data)) {
890
			$model = $data;
891
			if (!empty($this->request->data[$model][$model])) {
892
				return $this->request->data[$model][$model];
893
			}
894
			if (!empty($this->request->data[$model])) {
895
				$data = $this->request->data[$model];
896
			}
897
		}
898
		$array = array();
899 View Code Duplication
		if (!empty($data)) {
900
			foreach ($data as $row) {
901
				if (isset($row[$key])) {
902
					$array[$row[$key]] = $row[$key];
903
				}
904
			}
905
		}
906
		return empty($array) ? null : $array;
907
	}
908
909
/**
910
 * Resets the vars used by Helper::clean() to null
911
 *
912
 * @return void
913
 */
914
	protected function _reset() {
915
		$this->_tainted = null;
916
		$this->_cleaned = null;
917
	}
918
919
/**
920
 * Removes harmful content from output
921
 *
922
 * @return void
923
 */
924
	protected function _clean() {
925
		if (get_magic_quotes_gpc()) {
926
			$this->_cleaned = stripslashes($this->_tainted);
927
		} else {
928
			$this->_cleaned = $this->_tainted;
929
		}
930
931
		$this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
932
		$this->_cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->_cleaned);
933
		$this->_cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->_cleaned);
934
		$this->_cleaned = html_entity_decode($this->_cleaned, ENT_COMPAT, "UTF-8");
935
		$this->_cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->_cleaned);
936
		$this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2nojavascript...', $this->_cleaned);
937
		$this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2novbscript...', $this->_cleaned);
938
		$this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu', '$1=$2nomozbinding...', $this->_cleaned);
939
		$this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->_cleaned);
940
		$this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
941
		$this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
942
		$this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*>#iUu', "$1>", $this->_cleaned);
943
		$this->_cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->_cleaned);
944
		do {
945
			$oldstring = $this->_cleaned;
946
			$this->_cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->_cleaned);
947
		} while ($oldstring !== $this->_cleaned);
948
		$this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
949
	}
950
951
}
952