Completed
Push — master ( e5db64...945d9a )
by Schlaefer
05:09 queued 28s
created

SearchableBehavior::_addCondLike()   F

Complexity

Conditions 22
Paths 3848

Size

Total Lines 66

Duplication

Lines 20
Ratio 30.3 %

Importance

Changes 0
Metric Value
cc 22
nc 3848
nop 4
dl 20
loc 66
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Copyright 2009-2013, Cake Development Corporation (http://cakedc.com)
4
 *
5
 * Licensed under The MIT License
6
 * Redistributions of files must retain the above copyright notice.
7
 *
8
 * @copyright Copyright 2009 - 2013, Cake Development Corporation (http://cakedc.com)
9
 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
10
 */
11
App::uses('ModelBehavior', 'Model');
12
13
/**
14
 * Searchable behavior
15
 *
16
 */
17
class SearchableBehavior extends ModelBehavior {
18
19
/**
20
 * Default settings
21
 * - wildcardAny: the character used instead of % (% is a normal character then)
22
 * - wildcardOne: the character used instead of _ (_ is a normal character then)
23
 * - like: auto add % wildcard to beginning, end or both (both false => user can enter wildcards himself)
24
 * - connectorAnd: the character between search terms to specify an "and" relationship (binds stronger than or, similar to * and + in math)
25
 * - connectorOr: the character between search terms to specify an "or" relationship
26
 *
27
 * @var array
28
 */
29
	protected $_defaults = array(
30
		'wildcardAny' => '*', //on windows/unix/mac/google/... thats the default one
31
		'wildcardOne' => '?', //on windows/unix/mac thats the default one
32
		'like' => array('before' => true, 'after' => true),
33
		'connectorAnd' => null,
34
		'connectorOr' => null,
35
	);
36
37
/**
38
 * Configuration of model
39
 *
40
 * @param Model $Model
41
 * @param array $config
42
 * @return void
43
 */
44
	public function setup(Model $Model, $config = array()) {
45
		$this->_defaults = array_merge($this->_defaults, (array)Configure::read('Search.Searchable'));
46
		$this->settings[$Model->alias] = array_merge($this->_defaults, $config);
47
		if (empty($Model->filterArgs)) {
48
			return;
49
		}
50
		foreach ($Model->filterArgs as $key => $val) {
51
			if (!isset($val['name'])) {
52
				$Model->filterArgs[$key]['name'] = $key;
53
			}
54
			if (!isset($val['field'])) {
55
				$Model->filterArgs[$key]['field'] = $Model->filterArgs[$key]['name'];
56
			}
57
			if (!isset($val['type'])) {
58
				$Model->filterArgs[$key]['type'] = 'value';
59
			}
60
		}
61
	}
62
63
/**
64
 * parseCriteria
65
 * parses the GET data and returns the conditions for the find('all')/paginate
66
 * we are just going to test if the params are legit
67
 *
68
 * @param Model $Model
69
 * @param array $data Criteria of key->value pairs from post/named parameters
70
 * @return array Array of conditions that express the conditions needed for the search
71
 */
72
	public function parseCriteria(Model $Model, $data) {
73
		$conditions = array();
74
		foreach ($Model->filterArgs as $field) {
75
			// If this field was not passed and a default value exists, use that instead.
76 View Code Duplication
			if (!array_key_exists($field['name'], $data) && array_key_exists('defaultValue', $field)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
77
				$data[$field['name']] = $field['defaultValue'];
78
			}
79
80
			if (in_array($field['type'], array('like'))) {
81
				$this->_addCondLike($Model, $conditions, $data, $field);
82
			} elseif (in_array($field['type'], array('value'))) {
83
				$this->_addCondValue($Model, $conditions, $data, $field);
84
			} elseif ($field['type'] === 'expression') {
85
				$this->_addCondExpression($Model, $conditions, $data, $field);
86
			} elseif ($field['type'] === 'query') {
87
				$this->_addCondQuery($Model, $conditions, $data, $field);
88
			} elseif ($field['type'] === 'subquery') {
89
				$this->_addCondSubquery($Model, $conditions, $data, $field);
90
			}
91
		}
92
		return $conditions;
93
	}
94
95
/**
96
 * Validate search
97
 *
98
 * @param Model $Model
99
 * @param null $data
100
 * @return boolean always true
101
 */
102
	public function validateSearch(Model $Model, $data = null) {
103
		if (!empty($data)) {
104
			$Model->set($data);
105
		}
106
		$keys = array_keys($Model->data[$Model->alias]);
107
		foreach ($keys as $key) {
108
			if (empty($Model->data[$Model->alias][$key])) {
109
				unset($Model->data[$Model->alias][$key]);
110
			}
111
		}
112
		return true;
113
	}
114
115
/**
116
 * filter retrieving variables only that present in  Model::filterArgs
117
 *
118
 * @param Model $Model
119
 * @param array $vars
120
 * @return array, filtered args
0 ignored issues
show
Documentation introduced by
The doc-type array, could not be parsed: Expected "|" or "end of type", but got "," at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
121
 */
122
	public function passedArgs(Model $Model, $vars) {
123
		$result = array();
124
		foreach ($vars as $var => $val) {
125
			if (in_array($var, Set::extract($Model->filterArgs, '{n}.name'))) {
0 ignored issues
show
Documentation introduced by
'{n}.name' is of type string, but the function expects a array|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
126
				$result[$var] = $val;
127
			}
128
		}
129
		return $result;
130
	}
131
132
/**
133
 * Generates a query string using the same API Model::find() uses, calling the beforeFind process for the model
134
 *
135
 * @param Model $Model
136
 * @param string $type Type of find operation (all / first / count / neighbors / list / threaded)
137
 * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
138
 * @return array Array of records
139
 * @link http://book.cakephp.org/view/1018/find
140
 */
141
	public function getQuery(Model $Model, $type = 'first', $query = array()) {
142
		$Model->findQueryType = $type;
143
		$Model->id = $Model->getID();
144
		$query = $Model->buildQuery($type, $query);
145
		$this->findQueryType = null;
0 ignored issues
show
Bug introduced by
The property findQueryType does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
146
		return $this->_queryGet($Model, $query);
0 ignored issues
show
Bug introduced by
It seems like $query defined by $Model->buildQuery($type, $query) on line 144 can also be of type null; however, SearchableBehavior::_queryGet() does only seem to accept array, 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...
147
	}
148
149
/**
150
 * Clear all associations
151
 *
152
 * @param Model $Model
153
 * @param boolean $reset
154
 * @return void
155
 */
156
	public function unbindAllModels(Model $Model, $reset = false) {
157
		$assocs = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
158
		$unbind = array();
159
		foreach ($assocs as $assoc) {
160
			$unbind[$assoc] = array_keys($Model->{$assoc});
161
		}
162
		$Model->unbindModel($unbind, $reset);
163
	}
164
165
/**
166
 * For custom queries inside the model
167
 * example "makePhoneCondition": $cond = array('OR' => array_merge($this->condLike('cell_number', $filter), $this->condLike('landline_number', $filter, array('before' => false))));
168
 *
169
 * @param Model $Model
170
 * @param $name
171
 * @param $data
172
 * @param array $field
173
 * @return array of conditions
174
 */
175
	public function condLike(Model $Model, $name, $data, $field = array()) {
176
		$conditions = array();
177
		$field['name'] = $name;
178
		if (!is_array($data)) {
179
			$data = array($name => $data);
180
		}
181
		if (!isset($field['field'])) {
182
			$field['field'] = $field['name'];
183
		}
184
		return $this->_addCondLike($Model, $conditions, $data, $field);
185
	}
186
187
/**
188
 * Replace substitutions with original wildcards
189
 * but first, escape the original wildcards in the text to use them as normal search text
190
 *
191
 * @param Model $Model
192
 * @param $data
193
 * @param array $options
194
 * @return string queryLikeString
195
 */
196
	public function formatLike(Model $Model, $data, $options = array()) {
197
		$options = array_merge($this->settings[$Model->alias], $options);
198
		$from = $to = $substFrom = $substTo = array();
199 View Code Duplication
		if ($options['wildcardAny'] !== '%') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
			$from[] = '%';
201
			$to[] = '\%';
202
			$substFrom[] = $options['wildcardAny'];
203
			$substTo[] = '%';
204
		}
205 View Code Duplication
		if ($options['wildcardOne'] !== '_') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
			$from[] = '_';
207
			$to[] = '\_';
208
			$substFrom[] = $options['wildcardOne'];
209
			$substTo[] = '_';
210
		}
211
		if (!empty($from)) {
212
			// escape first
213
			$data = str_replace($from, $to, $data);
214
			// replace wildcards
215
			$data = str_replace($substFrom, $substTo, $data);
216
		}
217
		return $data;
218
	}
219
220
/**
221
 * Return the current chars for querying LIKE statements on this model
222
 *
223
 * @param Model $Model Reference to the model
224
 * @param array $options
225
 * @return array, [one=>..., any=>...]
0 ignored issues
show
Documentation introduced by
The doc-type array, could not be parsed: Expected "|" or "end of type", but got "," at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
226
 */
227
	public function getWildcards(Model $Model, $options = array()) {
228
		$options = array_merge($this->settings[$Model->alias], $options);
229
		return array('any' => $options['wildcardAny'], 'one' => $options['wildcardOne']);
230
	}
231
232
/**
233
 * Add Conditions based on fuzzy comparison
234
 *
235
 * @param Model $Model Reference to the model
236
 * @param array $conditions existing Conditions collected for the model
237
 * @param array $data Array of data used in search query
238
 * @param array $field Field definition information
239
 * @return array Conditions
240
 */
241
	protected function _addCondLike(Model $Model, &$conditions, $data, $field) {
242
		if (!is_array($this->settings[$Model->alias]['like'])) {
243
			$this->settings[$Model->alias]['like'] = array('before' => $this->settings[$Model->alias]['like'], 'after' => $this->settings[$Model->alias]['like']);
244
		}
245
		$field = array_merge($this->settings[$Model->alias]['like'], $field);
246
		if (empty($data[$field['name']])) {
247
			return $conditions;
248
		}
249
		$fieldNames = (array)$field['field'];
250
251
		$cond = array();
252
		foreach ($fieldNames as $fieldName) {
253 View Code Duplication
			if (strpos($fieldName, '.') === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
				$fieldName = $Model->alias . '.' . $fieldName;
255
			}
256
257
			if ($field['before'] === true) {
258
				$field['before'] = '%';
259
			}
260
			if ($field['after'] === true) {
261
				$field['after'] = '%';
262
			}
263
			//if both before and after are false, LIKE allows custom placeholders, % and _ are always treated as normal chars
264
			$options = $this->settings[$Model->alias];
265
			$from = $to = $substFrom = $substTo = array();
266 View Code Duplication
			if ($options['wildcardAny'] !== '%' || ($field['before'] !== false || $field['after'] !== false)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
267
				$from[] = '%';
268
				$to[] = '\%';
269
			}
270 View Code Duplication
			if ($options['wildcardOne'] !== '_' || ($field['before'] !== false || $field['after'] !== false)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
271
				$from[] = '_';
272
				$to[] = '\_';
273
			}
274
			$value = $data[$field['name']];
275
			if (!empty($from)) {
276
				$value = str_replace($from, $to, $value);
277
			}
278
			if ($field['before'] === false && $field['after'] === false) {
279
				if ($options['wildcardAny'] !== '%') {
280
					$substFrom[] = $options['wildcardAny'];
281
					$substTo[] = '%';
282
				}
283
				if ($options['wildcardOne'] !== '_') {
284
					$substFrom[] = $options['wildcardOne'];
285
					$substTo[] = '_';
286
				}
287
				$value = str_replace($substFrom, $substTo, $value);
288
			}
289
290
			if (!empty($field['connectorAnd']) || !empty($field['connectorOr'])) {
291
				$cond[] = $this->_connectedLike($value, $field, $fieldName);
292
			} else {
293
				$cond[$fieldName . " LIKE"] = $field['before'] . $value . $field['after'];
294
			}
295
		}
296 View Code Duplication
		if (count($cond) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
297
			if (isset($conditions['OR'])) {
298
				$conditions[]['OR'] = $cond;
299
			} else {
300
				$conditions['OR'] = $cond;
301
			}
302
		} else {
303
			$conditions = array_merge($conditions, $cond);
304
		}
305
		return $conditions;
306
	}
307
308
/**
309
 * Form AND/OR query array using String::tokenize to separate
310
 * search terms by or/and connectors.
311
 *
312
 * @param mixed $value
313
 * @param array $field
314
 * @param string $fieldName
315
 * @return array Conditions
316
 */
317
	protected function _connectedLike($value, $field, $fieldName) {
318
		$or = array();
319
		$orValues = String::tokenize($value, $field['connectorOr']);
320
		foreach ($orValues as $orValue) {
321
			$andValues = String::tokenize($orValue, $field['connectorAnd']);
322
			$and = array();
323
			foreach ($andValues as $andValue) {
324
				$and[] = array($fieldName . " LIKE" => $field['before'] . $andValue . $field['after']);
325
			}
326
327
			$or[] = array('AND' => $and);
328
		}
329
330
		return array('OR' => $or);
331
	}
332
333
/**
334
 * Add Conditions based on exact comparison
335
 *
336
 * @param Model $Model Reference to the model
337
 * @param array $conditions existing Conditions collected for the model
338
 * @param array $data Array of data used in search query
339
 * @param array $field Field definition information
340
 * @return array of conditions
341
 */
342
	protected function _addCondValue(Model $Model, &$conditions, $data, $field) {
343
		$fieldNames = (array)$field['field'];
344
		$fieldValue = isset($data[$field['name']]) ? $data[$field['name']] : null;
345
346
		$cond = array();
347
		foreach ($fieldNames as $fieldName) {
348 View Code Duplication
			if (strpos($fieldName, '.') === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
349
				$fieldName = $Model->alias . '.' . $fieldName;
350
			}
351
			if (is_array($fieldValue) && empty($fieldValue)) {
352
				continue;
353
			}
354
			if (!is_array($fieldValue) && ($fieldValue === null || $fieldValue === '' && empty($field['allowEmpty']))) {
355
				continue;
356
			}
357
358
			if (is_array($fieldValue) || !is_array($fieldValue) && (string)$fieldValue !== '') {
359
				$cond[$fieldName] = $fieldValue;
360
			} elseif (isset($data[$field['name']]) && !empty($field['allowEmpty'])) {
361
				$schema = $Model->schema($field['name']);
362
				if (isset($schema) && ($schema['default'] !== null || !empty($schema['null']))) {
363
					$cond[$fieldName] = $schema['default'];
364
				} elseif (!empty($fieldValue)) {
365
					$cond[$fieldName] = $fieldValue;
366
				} else {
367
					$cond[$fieldName] = $fieldValue;
368
				}
369
			}
370
		}
371 View Code Duplication
		if (count($cond) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
			if (isset($conditions['OR'])) {
373
				$conditions[]['OR'] = $cond;
374
			} else {
375
				$conditions['OR'] = $cond;
376
			}
377
		} else {
378
			$conditions = array_merge($conditions, $cond);
379
		}
380
		return $conditions;
381
	}
382
383
/**
384
 * Add Conditions based expressions to search conditions.
385
 *
386
 * @param Model $Model  Instance of AppModel
387
 * @param array $conditions Existing conditions.
388
 * @param array $data Data for a field.
389
 * @param array $field Info for field.
390
 * @return array of conditions modified by this method
391
 */
392
	protected function _addCondExpression(Model $Model, &$conditions, $data, $field) {
393
		$fieldName = $field['field'];
394
395
		if ((method_exists($Model, $field['method']) || $this->_checkBehaviorMethods($Model, $field['method'])) && (!empty($field['allowEmpty']) || !empty($data[$field['name']]) || (isset($data[$field['name']]) && (string)$data[$field['name']] !== ''))) {
396
			$fieldValues = $Model->{$field['method']}($data, $field);
397
			if (!empty($conditions[$fieldName]) && is_array($conditions[$fieldName])) {
398
				$conditions[$fieldName] = array_unique(array_merge(array($conditions[$fieldName]), array($fieldValues)));
399
			} else {
400
				$conditions[$fieldName] = $fieldValues;
401
			}
402
		}
403
		return $conditions;
404
	}
405
406
/**
407
 * Add Conditions based query to search conditions.
408
 *
409
 * @param Model $Model  Instance of AppModel
410
 * @param array $conditions Existing conditions.
411
 * @param array $data Data for a field.
412
 * @param array $field Info for field.
413
 * @return array of conditions modified by this method
414
 */
415
	protected function _addCondQuery(Model $Model, &$conditions, $data, $field) {
416
		if ((method_exists($Model, $field['method']) || $this->_checkBehaviorMethods($Model, $field['method'])) && (!empty($field['allowEmpty']) || !empty($data[$field['name']]) || (isset($data[$field['name']]) && (string)$data[$field['name']] !== ''))) {
417
			$conditionsAdd = $Model->{$field['method']}($data, $field);
418
			// if our conditions function returns something empty, nothing to merge in
419
			if (!empty($conditionsAdd)) {
420
				$conditions = Set::merge($conditions, (array)$conditionsAdd);
421
			}
422
		}
423
		return $conditions;
424
	}
425
426
/**
427
 * Add Conditions based subquery to search conditions.
428
 *
429
 * @param Model $Model  Instance of AppModel
430
 * @param array $conditions Existing conditions.
431
 * @param array $data Data for a field.
432
 * @param array $field Info for field.
433
 * @return array of conditions modified by this method
434
 */
435
	protected function _addCondSubquery(Model $Model, &$conditions, $data, $field) {
436
		$fieldName = $field['field'];
437
		if ((method_exists($Model, $field['method']) || $this->_checkBehaviorMethods($Model, $field['method'])) && (!empty($field['allowEmpty']) || !empty($data[$field['name']]) || (isset($data[$field['name']]) && (string)$data[$field['name']] !== ''))) {
438
			$subquery = $Model->{$field['method']}($data, $field);
439
			// if our subquery function returns something empty, nothing to merge in
440
			if (!empty($subquery)) {
441
				$conditions[] = $Model->getDataSource()->expression("$fieldName in ($subquery)");
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class DataSource as the method expression() does only exist in the following sub-classes of DataSource: DboDummy, DboFourthTestSource, DboMock, DboPostgresTestDb, DboSecondTestSource, DboSource, DboSqliteTestDb, DboTestSource, DboThirdTestSource, Mysql, Postgres, Sqlite, Sqlserver, SqlserverTestDb. 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...
442
			}
443
		}
444
		return $conditions;
445
	}
446
447
/**
448
 * Helper method for getQuery.
449
 * extension of dbo source method. Create association query.
450
 *
451
 * @param Model $Model
452
 * @param array $queryData
453
 * @return string
454
 */
455
	protected function _queryGet(Model $Model, $queryData = array()) {
456
		/** @var DboSource $db  */
457
		$db = $Model->getDataSource();
458
		$queryData = $this->_scrubQueryData($queryData);
459
		$recursive = null;
460
		$byPass = false;
461
		$null = null;
462
		$linkedModels = array();
463
464
		if (isset($queryData['recursive'])) {
465
			$recursive = $queryData['recursive'];
466
		}
467
468
		if ($recursive !== null) {
469
			$_recursive = $Model->recursive;
0 ignored issues
show
Unused Code introduced by
$_recursive is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
470
			$Model->recursive = $recursive;
471
		}
472
473
		if (!empty($queryData['fields'])) {
474
			$byPass = true;
475
			$queryData['fields'] = $db->fields($Model, null, $queryData['fields']);
476
		} else {
477
			$queryData['fields'] = $db->fields($Model);
478
		}
479
480
		$_associations = $Model->associations();
481
482
		if ($Model->recursive == -1) {
483
			$_associations = array();
484
		} elseif ($Model->recursive == 0) {
485
			unset($_associations[2], $_associations[3]);
486
		}
487
488
		foreach ($_associations as $type) {
489
			foreach ($Model->{$type} as $assoc => $assocData) {
490
				$linkModel = $Model->{$assoc};
491
				$external = isset($assocData['external']);
492
493
				$linkModel->getDataSource();
494
				if ($Model->useDbConfig === $linkModel->useDbConfig) {
495
					if ($byPass) {
496
						$assocData['fields'] = false;
497
					}
498
					if ($db->generateAssociationQuery($Model, $linkModel, $type, $assoc, $assocData, $queryData, $external, $null) === true) {
0 ignored issues
show
Unused Code introduced by
The call to DboSource::generateAssociationQuery() has too many arguments starting with $null.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
499
						$linkedModels[$type . '/' . $assoc] = true;
500
					}
501
				}
502
			}
503
		}
504
505
		return trim($db->generateAssociationQuery($Model, null, null, null, null, $queryData, false, $null));
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Unused Code introduced by
The call to DboSource::generateAssociationQuery() has too many arguments starting with $null.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
506
	}
507
508
/**
509
 * Private helper method to remove query metadata in given data array.
510
 *
511
 * @param array $data
512
 * @return array
513
 */
514
	protected function _scrubQueryData($data) {
515
		static $base = null;
516
		if ($base === null) {
517
			$base = array_fill_keys(array('conditions', 'fields', 'joins', 'order', 'limit', 'offset', 'group'), array());
518
		}
519
		return (array)$data + $base;
520
	}
521
522
/**
523
 * Check if model have some method in attached behaviors
524
 *
525
 * @param Model $Model
526
 * @param string $method
527
 * @return boolean, true if method exists in attached and enabled behaviors
0 ignored issues
show
Documentation introduced by
The doc-type boolean, could not be parsed: Expected "|" or "end of type", but got "," at position 7. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
528
 */
529
	protected function _checkBehaviorMethods(Model $Model, $method) {
530
		$behaviors = $Model->Behaviors->enabled();
531
		$count = count($behaviors);
532
		$found = false;
533
		for ($i = 0; $i < $count; $i++) {
534
			$name = $behaviors[$i];
535
			$methods = get_class_methods($Model->Behaviors->{$name});
536
			$check = array_flip($methods);
537
			$found = isset($check[$method]);
538
			if ($found) {
539
				return true;
540
			}
541
		}
542
		return $found;
543
	}
544
545
}
546