GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — integration ( 45cc9f...98bc42 )
by Brendan
05:52
created

SectionDatasource::execute()   F

Complexity

Conditions 50
Paths > 20000

Size

Total Lines 199
Code Lines 112

Duplication

Lines 5
Ratio 2.51 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 50
eloc 112
nc 33288
nop 1
dl 5
loc 199
rs 2
c 2
b 1
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
/**
4
 * @package data-sources
5
 */
6
/**
7
 * The `SectionDatasource` allows a user to retrieve entries from a given
8
 * section on the Frontend. This datasource type exposes the filtering provided
9
 * by the Fields in the given section to narrow the result set down. The resulting
10
 * entries can be grouped, sorted and allows pagination. Results can be chained
11
 * from other `SectionDatasource`'s using output parameters.
12
 *
13
 * @since Symphony 2.3
14
 * @link http://getsymphony.com/learn/concepts/view/data-sources/
15
 */
16
17
class SectionDatasource extends Datasource
18
{
19
    /**
20
     * An array of Field objects that this Datasource has created to display
21
     * the results.
22
     */
23
    private static $_fieldPool = array();
24
25
    /**
26
     * An array of the Symphony meta data parameters.
27
     */
28
    private static $_system_parameters = array(
29
        'system:id',
30
        'system:author',
31
        'system:creation-date',
32
        'system:modification-date',
33
        'system:date' // deprecated
34
    );
35
36
    /**
37
     * Set's the Section ID that this Datasource will use as it's source
38
     *
39
     * @param integer $source
40
     */
41
    public function setSource($source)
42
    {
43
        $this->_source = (int)$source;
0 ignored issues
show
Bug introduced by
The property _source 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...
44
    }
45
46
    /**
47
     * Return's the Section ID that this datasource is using as it's source
48
     *
49
     * @return integer
50
     */
51
    public function getSource()
52
    {
53
        return $this->_source;
54
    }
55
56
    /**
57
     * If this Datasource requires System Parameters to be output, this function
58
     * will return true, otherwise false.
59
     *
60
     * @return boolean
61
     */
62
    public function canProcessSystemParameters()
63
    {
64
        if (!is_array($this->dsParamPARAMOUTPUT)) {
0 ignored issues
show
Bug introduced by
The property dsParamPARAMOUTPUT 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...
65
            return false;
66
        }
67
68
        foreach (self::$_system_parameters as $system_parameter) {
69
            if (in_array($system_parameter, $this->dsParamPARAMOUTPUT) === true) {
70
                return true;
71
            }
72
        }
73
74
        return false;
75
    }
76
77
    /**
78
     * Given a name for the group, and an associative array that
79
     * contains three keys, `attr`, `records` and `groups`. Grouping
80
     * of Entries is done by the grouping Field at a PHP level, not
81
     * through the Database.
82
     *
83
     * @param string $element
84
     *  The name for the XML node for this group
85
     * @param array $group
86
     *  An associative array of the group data, includes `attr`, `records`
87
     *  and `groups` keys.
88
     * @throws Exception
89
     * @return XMLElement
90
     */
91
    public function processRecordGroup($element, array $group)
92
    {
93
        $xGroup = new XMLElement($element, null, $group['attr']);
94
95
        if (is_array($group['records']) && !empty($group['records'])) {
96 View Code Duplication
            if (isset($group['records'][0])) {
97
                $data = $group['records'][0]->getData();
98
                $pool = FieldManager::fetch(array_keys($data));
99
                self::$_fieldPool += $pool;
100
            }
101
102
            foreach ($group['records'] as $entry) {
103
                $xEntry = $this->processEntry($entry);
104
105
                if ($xEntry instanceof XMLElement) {
106
                    $xGroup->appendChild($xEntry);
107
                }
108
            }
109
        }
110
111
        if (is_array($group['groups']) && !empty($group['groups'])) {
112
            foreach ($group['groups'] as $element => $group) {
113
                foreach ($group as $g) {
114
                    $xGroup->appendChild(
115
                        $this->processRecordGroup($element, $g)
0 ignored issues
show
Bug introduced by
It seems like $this->processRecordGroup($element, $g) can be null; however, appendChild() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
116
                    );
117
                }
118
            }
119
        }
120
121
        if (!$this->_param_output_only) {
122
            return $xGroup;
123
        }
124
    }
125
126
    /**
127
     * Given an Entry object, this function will generate an XML representation
128
     * of the Entry to be returned. It will also add any parameters selected
129
     * by this datasource to the parameter pool.
130
     *
131
     * @param Entry $entry
132
     * @throws Exception
133
     * @return XMLElement|boolean
134
     *  Returns boolean when only parameters are to be returned.
135
     */
136
    public function processEntry(Entry $entry)
137
    {
138
        $data = $entry->getData();
139
140
        $xEntry = new XMLElement('entry');
141
        $xEntry->setAttribute('id', $entry->get('id'));
142
143
        if (!empty($this->_associated_sections)) {
0 ignored issues
show
Bug introduced by
The property _associated_sections 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...
144
            $this->setAssociatedEntryCounts($xEntry, $entry);
145
        }
146
147
        if ($this->_can_process_system_parameters) {
0 ignored issues
show
Bug introduced by
The property _can_process_system_parameters does not seem to exist. Did you mean _system_parameters?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
148
            $this->processSystemParameters($entry);
149
        }
150
151
        foreach ($data as $field_id => $values) {
152
            if (!isset(self::$_fieldPool[$field_id]) || !is_object(self::$_fieldPool[$field_id])) {
153
                self::$_fieldPool[$field_id] = FieldManager::fetch($field_id);
154
            }
155
156
            $this->processOutputParameters($entry, $field_id, $values);
157
158
            if (!$this->_param_output_only) {
159
                foreach ($this->dsParamINCLUDEDELEMENTS as $handle) {
0 ignored issues
show
Bug introduced by
The property dsParamINCLUDEDELEMENTS 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...
160
                    list($handle, $mode) = preg_split('/\s*:\s*/', $handle, 2);
161
162
                    if (self::$_fieldPool[$field_id]->get('element_name') === $handle) {
163
                        self::$_fieldPool[$field_id]->appendFormattedElement($xEntry, $values, ($this->dsParamHTMLENCODE === 'yes' ? true : false), $mode, $entry->get('id'));
0 ignored issues
show
Bug introduced by
The property dsParamHTMLENCODE 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...
164
                    }
165
                }
166
            }
167
        }
168
169
        if ($this->_param_output_only) {
170
            return true;
171
        }
172
173
        if (in_array('system:date', $this->dsParamINCLUDEDELEMENTS)) {
174
            $xDate = new XMLElement('system-date');
175
            $xDate->appendChild(
176
                General::createXMLDateObject(
0 ignored issues
show
Security Bug introduced by
It seems like \General::createXMLDateO...ion_date')), 'created') targeting General::createXMLDateObject() can also be of type false; however, XMLElement::appendChild() does only seem to accept object<XMLElement>|string, did you maybe forget to handle an error condition?
Loading history...
177
                    DateTimeObj::get('U', $entry->get('creation_date')),
0 ignored issues
show
Documentation introduced by
\DateTimeObj::get('U', $...->get('creation_date')) is of type false|string, but the function expects a integer.

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...
178
                    'created'
179
                )
180
            );
181
            $xDate->appendChild(
182
                General::createXMLDateObject(
0 ignored issues
show
Security Bug introduced by
It seems like \General::createXMLDateO...on_date')), 'modified') targeting General::createXMLDateObject() can also be of type false; however, XMLElement::appendChild() does only seem to accept object<XMLElement>|string, did you maybe forget to handle an error condition?
Loading history...
183
                    DateTimeObj::get('U', $entry->get('modification_date')),
0 ignored issues
show
Documentation introduced by
\DateTimeObj::get('U', $...t('modification_date')) is of type false|string, but the function expects a integer.

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...
184
                    'modified'
185
                )
186
            );
187
            $xEntry->appendChild($xDate);
188
        }
189
190
        return $xEntry;
191
    }
192
193
    /**
194
     * An entry may be associated to other entries from various fields through
195
     * the section associations. This function will set the number of related
196
     * entries as attributes to the main `<entry>` element grouped by the
197
     * related entry's section.
198
     *
199
     * @param XMLElement $xEntry
200
     *  The <entry> XMLElement that the associated section counts will
201
     *  be set on
202
     * @param Entry $entry
203
     *  The current entry object
204
     * @throws Exception
205
     */
206
    public function setAssociatedEntryCounts(XMLElement &$xEntry, Entry $entry)
207
    {
208
        $associated_entry_counts = $entry->fetchAllAssociatedEntryCounts($this->_associated_sections);
209
210
        if (!empty($associated_entry_counts)) {
211
            foreach ($associated_entry_counts as $section_id => $fields) {
212
                foreach ($this->_associated_sections as $section) {
213
                    if ($section['id'] !== $section_id) {
214
                        continue;
215
                    }
216
217
                    // For each related field show the count (#2083)
218
                    foreach ($fields as $field_id => $count) {
219
                        $field_handle = FieldManager::fetchHandleFromID($field_id);
220
                        if ($field_handle) {
221
                            $xEntry->setAttribute($section['handle'] . '-' . $field_handle, (string)$count);
222
                        }
223
224
                        // Backwards compatibility (without field handle)
225
                        $xEntry->setAttribute($section['handle'], (string)$count);
226
                    }
227
                }
228
            }
229
        }
230
    }
231
232
    /**
233
     * Given an Entry object, this function will iterate over the `dsParamPARAMOUTPUT`
234
     * setting to see any of the Symphony system parameters need to be set.
235
     * The current system parameters supported are `system:id`, `system:author`
236
     * and `system:date`. If these parameters are found, the result is added
237
     * to the `$param_pool` array using the key, `ds-datasource-handle.parameter-name`
238
     * For the moment, this function also supports the pre Symphony 2.3 syntax,
239
     * `ds-datasource-handle` which did not support multiple parameters.
240
     *
241
     * @param Entry $entry
242
     *  The Entry object that contains the values that may need to be added
243
     *  into the parameter pool.
244
     */
245
    public function processSystemParameters(Entry $entry)
246
    {
247
        if (!isset($this->dsParamPARAMOUTPUT)) {
248
            return;
249
        }
250
251
        // Support the legacy parameter `ds-datasource-handle`
252
        $key = 'ds-' . $this->dsParamROOTELEMENT;
0 ignored issues
show
Bug introduced by
The property dsParamROOTELEMENT 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...
253
        $singleParam = count($this->dsParamPARAMOUTPUT) === 1;
254
255
        foreach ($this->dsParamPARAMOUTPUT as $param) {
256
            // The new style of paramater is `ds-datasource-handle.field-handle`
257
            $param_key = $key . '.' . str_replace(':', '-', $param);
258
259
            if ($param === 'system:id') {
260
                $this->_param_pool[$param_key][] = $entry->get('id');
0 ignored issues
show
Bug introduced by
The property _param_pool 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...
261
262
                if ($singleParam) {
263
                    $this->_param_pool[$key][] = $entry->get('id');
264
                }
265 View Code Duplication
            } elseif ($param === 'system:author') {
266
                $this->_param_pool[$param_key][] = $entry->get('author_id');
267
268
                if ($singleParam) {
269
                    $this->_param_pool[$key][] = $entry->get('author_id');
270
                }
271
            } elseif ($param === 'system:creation-date' || $param === 'system:date') {
272
                $this->_param_pool[$param_key][] = $entry->get('creation_date');
273
274
                if ($singleParam) {
275
                    $this->_param_pool[$key][] = $entry->get('creation_date');
276
                }
277 View Code Duplication
            } elseif ($param === 'system:modification-date') {
278
                $this->_param_pool[$param_key][] = $entry->get('modification_date');
279
280
                if ($singleParam) {
281
                    $this->_param_pool[$key][] = $entry->get('modification_date');
282
                }
283
            }
284
        }
285
    }
286
287
    /**
288
     * Given an Entry object, a `$field_id` and an array of `$data`, this
289
     * function iterates over the `dsParamPARAMOUTPUT` and will call the
290
     * field's (identified by `$field_id`) `getParameterPoolValue` function
291
     * to add parameters to the `$this->_param_pool`.
292
     *
293
     * @param Entry $entry
294
     * @param integer $field_id
295
     * @param array $data
296
     */
297
    public function processOutputParameters(Entry $entry, $field_id, array $data)
298
    {
299
        if (!isset($this->dsParamPARAMOUTPUT)) {
300
            return;
301
        }
302
303
        // Support the legacy parameter `ds-datasource-handle`
304
        $key = 'ds-' . $this->dsParamROOTELEMENT;
305
306
        foreach ($this->dsParamPARAMOUTPUT as $param) {
307
            if (self::$_fieldPool[$field_id]->get('element_name') !== $param) {
308
                continue;
309
            }
310
311
            // The new style of paramater is `ds-datasource-handle.field-handle`
312
            $param_key = $key . '.' . str_replace(':', '-', $param);
313
314
            if (!isset($this->_param_pool[$param_key]) || !is_array($this->_param_pool[$param_key])) {
315
                $this->_param_pool[$param_key] = array();
316
            }
317
318
            $param_pool_values = self::$_fieldPool[$field_id]->getParameterPoolValue($data, $entry->get('id'));
319
320
            if (is_array($param_pool_values)) {
321
                $this->_param_pool[$param_key] = array_merge($param_pool_values, $this->_param_pool[$param_key]);
322
            } elseif (!is_null($param_pool_values)) {
323
                $this->_param_pool[$param_key][] = $param_pool_values;
324
            }
325
        }
326
    }
327
328
    /**
329
     * This function iterates over `dsParamFILTERS` and builds the relevant
330
     * `$where` and `$joins` parameters with SQL. This SQL is generated from
331
     * `Field->buildDSRetrievalSQL`. A third parameter, `$group` is populated
332
     * with boolean from `Field->requiresSQLGrouping()`
333
     *
334
     * @param string $where
335
     * @param string $joins
336
     * @param boolean $group
337
     * @throws Exception
338
     */
339
    public function processFilters(&$where, &$joins, &$group)
340
    {
341
        if (!is_array($this->dsParamFILTERS) || empty($this->dsParamFILTERS)) {
0 ignored issues
show
Bug introduced by
The property dsParamFILTERS 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...
342
            return;
343
        }
344
345
        $pool = FieldManager::fetch(array_filter(array_keys($this->dsParamFILTERS), 'is_int'));
346
        self::$_fieldPool += $pool;
347
348
        if (!is_string($where)) {
349
            $where = '';
350
        }
351
352
        foreach ($this->dsParamFILTERS as $field_id => $filter) {
353
            if ((is_array($filter) && empty($filter)) || trim($filter) === '') {
354
                continue;
355
            }
356
357 View Code Duplication
            if (!is_array($filter)) {
358
                $filter_type = Datasource::determineFilterType($filter);
359
                $value = preg_split('/'.($filter_type === Datasource::FILTER_AND ? '\+' : '(?<!\\\\),').'\s*/', $filter, -1, PREG_SPLIT_NO_EMPTY);
360
                $value = array_map('trim', $value);
361
                $value = array_map(array('Datasource', 'removeEscapedCommas'), $value);
362
            } else {
363
                $value = $filter;
364
            }
365
366
            if (!in_array($field_id, self::$_system_parameters) && $field_id !== 'id' && !(self::$_fieldPool[$field_id] instanceof Field)) {
367
                throw new Exception(
368
                    __(
369
                        'Error creating field object with id %1$d, for filtering in data source %2$s. Check this field exists.',
370
                        array($field_id, '<code>' . $this->dsParamROOTELEMENT . '</code>')
371
                    )
372
                );
373
            }
374
375
            // Support system:id as well as the old 'id'. #1691
376
            if ($field_id === 'system:id' || $field_id === 'id') {
377
                $c = 'IN';
378
379
                if (stripos($value[0], 'not:') === 0) {
380
                    $value[0] = preg_replace('/^not:\s*/', null, $value[0]);
381
                    $c = 'NOT IN';
382
                }
383
384
                // Cast all ID's to integers. (RE: #2191)
385
                $value = array_map(function ($val) {
386
                    $val = General::intval($val);
387
388
                    // General::intval can return -1, so reset that to 0
389
                    // so there are no side effects for the following
390
                    // array_sum and array_filter calls. RE: #2475
391
                    if ($val === -1) {
392
                        $val = 0;
393
                    }
394
395
                    return $val;
396
                }, $value);
397
                $count = array_sum($value);
398
                $value = array_filter($value);
399
400
                // If the ID was cast to 0, then we need to filter on 'id' = 0,
401
                // which will of course return no results, but without it the
402
                // Datasource will return ALL results, which is not the
403
                // desired behaviour. RE: #1619
404
                if ($count === 0) {
405
                    $value[] = 0;
406
                }
407
408
                // If there are no ID's, no need to filter. RE: #1567
409
                if (!empty($value)) {
410
                    $where .= " AND `e`.id " . $c . " (".implode(", ", $value).") ";
411
                }
412
            } elseif ($field_id === 'system:creation-date' || $field_id === 'system:modification-date' || $field_id === 'system:date') {
413
                $date_joins = '';
414
                $date_where = '';
415
                $date = new FieldDate();
416
                $date->buildDSRetrievalSQL($value, $date_joins, $date_where, ($filter_type === Datasource::FILTER_AND ? true : false));
0 ignored issues
show
Bug introduced by
The variable $filter_type 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...
417
418
                // Replace the date field where with the `creation_date` or `modification_date`.
419
                $date_where = preg_replace('/`t\d+`.date/', ($field_id !== 'system:modification-date') ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt', $date_where);
420
                $where .= $date_where;
421
            } else {
422
                if (!self::$_fieldPool[$field_id]->buildDSRetrievalSQL($value, $joins, $where, ($filter_type === Datasource::FILTER_AND ? true : false))) {
423
                    $this->_force_empty_result = true;
424
                    return;
425
                }
426
427
                if (!$group) {
428
                    $group = self::$_fieldPool[$field_id]->requiresSQLGrouping();
429
                }
430
            }
431
        }
432
    }
433
434
    public function execute(array &$param_pool = null)
435
    {
436
        $result = new XMLElement($this->dsParamROOTELEMENT);
437
        $this->_param_pool = $param_pool;
438
        $where = null;
439
        $joins = null;
440
        $group = false;
441
442
        if (!$section = SectionManager::fetch((int)$this->getSource())) {
443
            $about = $this->about();
444
            trigger_error(__('The Section, %s, associated with the Data source, %s, could not be found.', array($this->getSource(), '<code>' . $about['name'] . '</code>')), E_USER_ERROR);
445
        }
446
447
        $sectioninfo = new XMLElement('section', General::sanitize($section->get('name')), array(
448
            'id' => $section->get('id'),
449
            'handle' => $section->get('handle')
450
        ));
451
452
        if ($this->_force_empty_result === true) {
453
            if ($this->dsParamREDIRECTONREQUIRED === 'yes') {
0 ignored issues
show
Bug introduced by
The property dsParamREDIRECTONREQUIRED 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...
454
                throw new FrontendPageNotFoundException;
455
            }
456
457
            $this->_force_empty_result = false; //this is so the section info element doesn't disappear.
458
            $error = new XMLElement('error', __("Data source not executed, required parameter is missing."), array(
459
                'required-param' => $this->dsParamREQUIREDPARAM
0 ignored issues
show
Bug introduced by
The property dsParamREQUIREDPARAM 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...
460
            ));
461
            $result->appendChild($error);
462
            $result->prependChild($sectioninfo);
463
464
            return $result;
465
        }
466
467
        if ($this->_negate_result === true) {
468
            if ($this->dsParamREDIRECTONFORBIDDEN === 'yes') {
0 ignored issues
show
Bug introduced by
The property dsParamREDIRECTONFORBIDDEN 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...
469
                throw new FrontendPageNotFoundException;
470
            }
471
472
            $this->_negate_result = false; //this is so the section info element doesn't disappear.
473
            $result = $this->negateXMLSet();
474
            $result->prependChild($sectioninfo);
475
476
            return $result;
477
        }
478
479
        if (is_array($this->dsParamINCLUDEDELEMENTS)) {
480
            $include_pagination_element = in_array('system:pagination', $this->dsParamINCLUDEDELEMENTS);
481
        } else {
482
            $this->dsParamINCLUDEDELEMENTS = array();
483
        }
484
485
        if (isset($this->dsParamPARAMOUTPUT) && !is_array($this->dsParamPARAMOUTPUT)) {
486
            $this->dsParamPARAMOUTPUT = array($this->dsParamPARAMOUTPUT);
487
        }
488
489
        $this->_can_process_system_parameters = $this->canProcessSystemParameters();
0 ignored issues
show
Bug introduced by
The property _can_process_system_parameters does not seem to exist. Did you mean _system_parameters?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
490
491
        if (!isset($this->dsParamPAGINATERESULTS)) {
492
            $this->dsParamPAGINATERESULTS = 'yes';
0 ignored issues
show
Bug introduced by
The property dsParamPAGINATERESULTS 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...
493
        }
494
495
        // Process Filters
496
        $this->processFilters($where, $joins, $group);
497
498
        // Process Sorting
499
        if ($this->dsParamSORT === 'system:id') {
500
            EntryManager::setFetchSorting('system:id', $this->dsParamORDER);
0 ignored issues
show
Bug introduced by
The property dsParamORDER 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...
501
        } elseif ($this->dsParamSORT === 'system:date' || $this->dsParamSORT === 'system:creation-date') {
0 ignored issues
show
Bug introduced by
The property dsParamSORT 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...
502
            EntryManager::setFetchSorting('system:creation-date', $this->dsParamORDER);
503
        } elseif ($this->dsParamSORT === 'system:modification-date') {
504
            EntryManager::setFetchSorting('system:modification-date', $this->dsParamORDER);
505
        } else {
506
            EntryManager::setFetchSorting(
507
                FieldManager::fetchFieldIDFromElementName($this->dsParamSORT, $this->getSource()),
508
                $this->dsParamORDER
509
            );
510
        }
511
512
        // combine `INCLUDEDELEMENTS`, `PARAMOUTPUT` and `GROUP` into an
513
        // array of field handles to optimise the `EntryManager` queries
514
        $datasource_schema = $this->dsParamINCLUDEDELEMENTS;
515
516
        if (is_array($this->dsParamPARAMOUTPUT)) {
517
            $datasource_schema = array_merge($datasource_schema, $this->dsParamPARAMOUTPUT);
518
        }
519
520
        if ($this->dsParamGROUP) {
521
            $datasource_schema[] = FieldManager::fetchHandleFromID($this->dsParamGROUP);
0 ignored issues
show
Bug introduced by
The property dsParamGROUP 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...
522
        }
523
524
        $entries = EntryManager::fetchByPage(
525
            ($this->dsParamPAGINATERESULTS === 'yes' && $this->dsParamSTARTPAGE > 0 ? $this->dsParamSTARTPAGE : 1),
0 ignored issues
show
Bug introduced by
The property dsParamSTARTPAGE 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...
526
            $this->getSource(),
527
            ($this->dsParamPAGINATERESULTS === 'yes' && $this->dsParamLIMIT >= 0 ? $this->dsParamLIMIT : null),
0 ignored issues
show
Bug introduced by
The property dsParamLIMIT 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...
528
            $where,
529
            $joins,
530
            $group,
531
            (!$include_pagination_element ? true : false),
0 ignored issues
show
Bug introduced by
The variable $include_pagination_element 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...
532
            true,
533
            array_unique($datasource_schema)
534
        );
535
536
        /**
537
         * Immediately after building entries allow modification of the Data Source entries array
538
         *
539
         * @delegate DataSourceEntriesBuilt
540
         * @param string $context
541
         * '/frontend/'
542
         * @param Datasource $datasource
543
         * @param array $entries
544
         * @param array $filters
545
         */
546
        Symphony::ExtensionManager()->notifyMembers('DataSourceEntriesBuilt', '/frontend/', array(
547
            'datasource' => &$this,
548
            'entries' => &$entries,
549
            'filters' => $this->dsParamFILTERS
550
        ));
551
552
        if (($entries['total-entries'] <= 0 || $include_pagination_element === true) && (!is_array($entries['records']) || empty($entries['records'])) || (int)$this->dsParamSTARTPAGE === 0) {
553
            if ($this->dsParamREDIRECTONEMPTY === 'yes') {
0 ignored issues
show
Bug introduced by
The property dsParamREDIRECTONEMPTY 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...
554
                throw new FrontendPageNotFoundException;
555
            }
556
557
            $this->_force_empty_result = false;
558
            $result = $this->emptyXMLSet();
559
            $result->prependChild($sectioninfo);
560
561
            if ($include_pagination_element) {
562
                $pagination_element = General::buildPaginationElement();
563
564
                if ($pagination_element instanceof XMLElement && $result instanceof XMLElement) {
565
                    $result->prependChild($pagination_element);
566
                }
567
            }
568
        } else {
569
            if (!$this->_param_output_only) {
570
                $result->appendChild($sectioninfo);
571
572
                if ($include_pagination_element) {
573
                    $t = ($this->dsParamPAGINATERESULTS === 'yes' && isset($this->dsParamLIMIT) && $this->dsParamLIMIT >= 0 ? $this->dsParamLIMIT : $entries['total-entries']);
574
575
                    $pagination_element = General::buildPaginationElement(
576
                        $entries['total-entries'],
577
                        $entries['total-pages'],
578
                        $t,
579
                        ($this->dsParamPAGINATERESULTS === 'yes' && $this->dsParamSTARTPAGE > 0 ? $this->dsParamSTARTPAGE : 1)
580
                    );
581
582
                    if ($pagination_element instanceof XMLElement && $result instanceof XMLElement) {
583
                        $result->prependChild($pagination_element);
584
                    }
585
                }
586
            }
587
588
            // If this datasource has a Limit greater than 0 or the Limit is not set
589
            if (!isset($this->dsParamLIMIT) || $this->dsParamLIMIT > 0) {
590
                if (!isset($this->dsParamASSOCIATEDENTRYCOUNTS) || $this->dsParamASSOCIATEDENTRYCOUNTS === 'yes') {
0 ignored issues
show
Bug introduced by
The property dsParamASSOCIATEDENTRYCOUNTS 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...
591
                    $this->_associated_sections = $section->fetchChildAssociations();
592
                }
593
594
                // If the datasource require's GROUPING
595
                if (isset($this->dsParamGROUP)) {
596
                    self::$_fieldPool[$this->dsParamGROUP] = FieldManager::fetch($this->dsParamGROUP);
597
598
                    if (self::$_fieldPool[$this->dsParamGROUP] === null) {
599
                        throw new SymphonyErrorPage(vsprintf("The field used for grouping '%s' cannot be found.", $this->dsParamGROUP));
600
                    }
601
602
                    $groups = self::$_fieldPool[$this->dsParamGROUP]->groupRecords($entries['records']);
0 ignored issues
show
Bug introduced by
The method groupRecords cannot be called on self::$_fieldPool[$this->dsParamGROUP] (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
603
604
                    foreach ($groups as $element => $group) {
605
                        foreach ($group as $g) {
606
                            $result->appendChild(
607
                                $this->processRecordGroup($element, $g)
0 ignored issues
show
Bug introduced by
It seems like $this->processRecordGroup($element, $g) can be null; however, appendChild() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
608
                            );
609
                        }
610
                    }
611
                } else {
612 View Code Duplication
                    if (isset($entries['records'][0])) {
613
                        $data = $entries['records'][0]->getData();
614
                        $pool = FieldManager::fetch(array_keys($data));
615
                        self::$_fieldPool += $pool;
616
                    }
617
618
                    foreach ($entries['records'] as $entry) {
619
                        $xEntry = $this->processEntry($entry);
620
621
                        if ($xEntry instanceof XMLElement) {
622
                            $result->appendChild($xEntry);
623
                        }
624
                    }
625
                }
626
            }
627
        }
628
629
        $param_pool = $this->_param_pool;
630
631
        return $result;
632
    }
633
}
634