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
Pull Request — integration (#2604)
by Brendan
04:24
created

SectionDatasource   F

Complexity

Total Complexity 133

Size/Duplication

Total Lines 629
Duplicated Lines 5.09 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 8
Bugs 4 Features 0
Metric Value
c 8
b 4
f 0
dl 32
loc 629
rs 3.2644
wmc 133
lcom 1
cbo 13

10 Methods

Rating   Name   Duplication   Size   Complexity  
A setSource() 0 4 1
F execute() 5 201 50
A getSource() 0 4 1
A canProcessSystemParameters() 0 14 4
D processFilters() 8 103 27
C processRecordGroup() 5 34 11
C processEntry() 0 57 12
C setAssociatedEntryCounts() 0 25 7
C processSystemParameters() 14 41 12
C processOutputParameters() 0 30 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SectionDatasource often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SectionDatasource, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
    /**
4
     * @package data-sources
5
     */
6
7
    /**
8
     * The `SectionDatasource` allows a user to retrieve entries from a given
9
     * section on the Frontend. This datasource type exposes the filtering provided
10
     * by the Fields in the given section to narrow the result set down. The resulting
11
     * entries can be grouped, sorted and allows pagination. Results can be chained
12
     * from other `SectionDatasource`'s using output parameters.
13
     *
14
     * @since Symphony 2.3
15
     * @link http://getsymphony.com/learn/concepts/view/data-sources/
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
        public function execute(array &$param_pool = null)
47
        {
48
            $result = new XMLElement($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...
49
            $this->_param_pool = $param_pool;
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...
50
            $where = null;
51
            $joins = null;
52
            $group = false;
53
54
            if (!$section = SectionManager::fetch((int)$this->getSource())) {
55
                $about = $this->about();
56
                trigger_error(__('The Section, %s, associated with the Data source, %s, could not be found.',
57
                    array($this->getSource(), '<code>' . $about['name'] . '</code>')), E_USER_ERROR);
58
            }
59
60
            $sectioninfo = new XMLElement('section', General::sanitize($section->get('name')), array(
61
                'id' => $section->get('id'),
62
                'handle' => $section->get('handle')
63
            ));
64
65
            if ($this->_force_empty_result === true) {
66
                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...
67
                    throw new FrontendPageNotFoundException;
68
                }
69
70
                $this->_force_empty_result = false; //this is so the section info element doesn't disappear.
71
                $error = new XMLElement('error', __("Data source not executed, required parameter is missing."), array(
72
                    '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...
73
                ));
74
                $result->appendChild($error);
75
                $result->prependChild($sectioninfo);
76
77
                return $result;
78
            }
79
80
            if ($this->_negate_result === true) {
81
                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...
82
                    throw new FrontendPageNotFoundException;
83
                }
84
85
                $this->_negate_result = false; //this is so the section info element doesn't disappear.
86
                $result = $this->negateXMLSet();
87
                $result->prependChild($sectioninfo);
88
89
                return $result;
90
            }
91
92
            if (is_array($this->dsParamINCLUDEDELEMENTS)) {
93
                $include_pagination_element = in_array('system:pagination', $this->dsParamINCLUDEDELEMENTS);
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...
94
            } else {
95
                $this->dsParamINCLUDEDELEMENTS = array();
96
            }
97
98
            if (isset($this->dsParamPARAMOUTPUT) && !is_array($this->dsParamPARAMOUTPUT)) {
99
                $this->dsParamPARAMOUTPUT = 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...
100
            }
101
102
            $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...
103
104
            if (!isset($this->dsParamPAGINATERESULTS)) {
105
                $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...
106
            }
107
108
            // Process Filters
109
            $this->processFilters($where, $joins, $group);
110
111
            // Process Sorting
112
            if ($this->dsParamSORT === 'system:id') {
113
                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...
114
            } 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...
115
                EntryManager::setFetchSorting('system:creation-date', $this->dsParamORDER);
116
            } elseif ($this->dsParamSORT === 'system:modification-date') {
117
                EntryManager::setFetchSorting('system:modification-date', $this->dsParamORDER);
118
            } else {
119
                EntryManager::setFetchSorting(
120
                    FieldManager::fetchFieldIDFromElementName($this->dsParamSORT, $this->getSource()),
121
                    $this->dsParamORDER
122
                );
123
            }
124
125
            // combine `INCLUDEDELEMENTS`, `PARAMOUTPUT` and `GROUP` into an
126
            // array of field handles to optimise the `EntryManager` queries
127
            $datasource_schema = $this->dsParamINCLUDEDELEMENTS;
128
129
            if (is_array($this->dsParamPARAMOUTPUT)) {
130
                $datasource_schema = array_merge($datasource_schema, $this->dsParamPARAMOUTPUT);
131
            }
132
133
            if ($this->dsParamGROUP) {
134
                $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...
135
            }
136
137
            $entries = EntryManager::fetchByPage(
138
                ($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...
139
                $this->getSource(),
140
                ($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...
141
                $where,
142
                $joins,
143
                $group,
144
                (!$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...
145
                true,
146
                array_unique($datasource_schema)
147
            );
148
149
            /**
150
             * Immediately after building entries allow modification of the Data Source entries array
151
             *
152
             * @delegate DataSourceEntriesBuilt
153
             * @param string $context
154
             * '/frontend/'
155
             * @param Datasource $datasource
156
             * @param array $entries
157
             * @param array $filters
158
             */
159
            Symphony::ExtensionManager()->notifyMembers('DataSourceEntriesBuilt', '/frontend/', array(
160
                'datasource' => &$this,
161
                'entries' => &$entries,
162
                'filters' => $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...
163
            ));
164
165
            if (($entries['total-entries'] <= 0 || $include_pagination_element === true) && (!is_array($entries['records']) || empty($entries['records'])) || (int)$this->dsParamSTARTPAGE === 0) {
166
                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...
167
                    throw new FrontendPageNotFoundException;
168
                }
169
170
                $this->_force_empty_result = false;
171
                $result = $this->emptyXMLSet();
172
                $result->prependChild($sectioninfo);
173
174
                if ($include_pagination_element) {
175
                    $pagination_element = General::buildPaginationElement();
176
177
                    if ($pagination_element instanceof XMLElement && $result instanceof XMLElement) {
178
                        $result->prependChild($pagination_element);
179
                    }
180
                }
181
            } else {
182
                if (!$this->_param_output_only) {
183
                    $result->appendChild($sectioninfo);
184
185
                    if ($include_pagination_element) {
186
                        $t = ($this->dsParamPAGINATERESULTS === 'yes' && isset($this->dsParamLIMIT) && $this->dsParamLIMIT >= 0 ? $this->dsParamLIMIT : $entries['total-entries']);
187
188
                        $pagination_element = General::buildPaginationElement(
189
                            $entries['total-entries'],
190
                            $entries['total-pages'],
191
                            $t,
192
                            ($this->dsParamPAGINATERESULTS === 'yes' && $this->dsParamSTARTPAGE > 0 ? $this->dsParamSTARTPAGE : 1)
193
                        );
194
195
                        if ($pagination_element instanceof XMLElement && $result instanceof XMLElement) {
196
                            $result->prependChild($pagination_element);
197
                        }
198
                    }
199
                }
200
201
                // If this datasource has a Limit greater than 0 or the Limit is not set
202
                if (!isset($this->dsParamLIMIT) || $this->dsParamLIMIT > 0) {
203
                    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...
204
                        $this->_associated_sections = $section->fetchChildAssociations();
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...
205
                    }
206
207
                    // If the datasource require's GROUPING
208
                    if (isset($this->dsParamGROUP)) {
209
                        self::$_fieldPool[$this->dsParamGROUP] = FieldManager::fetch($this->dsParamGROUP);
210
211
                        if (self::$_fieldPool[$this->dsParamGROUP] === null) {
212
                            throw new SymphonyErrorPage(vsprintf("The field used for grouping '%s' cannot be found.",
213
                                $this->dsParamGROUP));
214
                        }
215
216
                        $groups = self::$_fieldPool[$this->dsParamGROUP]->groupRecords($entries['records']);
217
218
                        foreach ($groups as $element => $group) {
219
                            foreach ($group as $g) {
220
                                $result->appendChild(
221
                                    $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...
222
                                );
223
                            }
224
                        }
225
                    } else {
226 View Code Duplication
                        if (isset($entries['records'][0])) {
227
                            $data = $entries['records'][0]->getData();
228
                            $pool = FieldManager::fetch(array_keys($data));
229
                            self::$_fieldPool += $pool;
230
                        }
231
232
                        foreach ($entries['records'] as $entry) {
233
                            $xEntry = $this->processEntry($entry);
234
235
                            if ($xEntry instanceof XMLElement) {
236
                                $result->appendChild($xEntry);
237
                            }
238
                        }
239
                    }
240
                }
241
            }
242
243
            $param_pool = $this->_param_pool;
244
245
            return $result;
246
        }
247
248
        /**
249
         * Return's the Section ID that this datasource is using as it's source
250
         *
251
         * @return integer
252
         */
253
        public function getSource()
254
        {
255
            return $this->_source;
256
        }
257
258
        /**
259
         * If this Datasource requires System Parameters to be output, this function
260
         * will return true, otherwise false.
261
         *
262
         * @return boolean
263
         */
264
        public function canProcessSystemParameters()
265
        {
266
            if (!is_array($this->dsParamPARAMOUTPUT)) {
267
                return false;
268
            }
269
270
            foreach (self::$_system_parameters as $system_parameter) {
271
                if (in_array($system_parameter, $this->dsParamPARAMOUTPUT) === true) {
272
                    return true;
273
                }
274
            }
275
276
            return false;
277
        }
278
279
        /**
280
         * This function iterates over `dsParamFILTERS` and builds the relevant
281
         * `$where` and `$joins` parameters with SQL. This SQL is generated from
282
         * `Field->buildDSRetrievalSQL`. A third parameter, `$group` is populated
283
         * with boolean from `Field->requiresSQLGrouping()`
284
         *
285
         * @param string $where
286
         * @param string $joins
287
         * @param boolean $group
288
         * @throws Exception
289
         */
290
        public function processFilters(&$where, &$joins, &$group)
291
        {
292
            if (!is_array($this->dsParamFILTERS) || empty($this->dsParamFILTERS)) {
293
                return;
294
            }
295
296
            $pool = FieldManager::fetch(array_filter(array_keys($this->dsParamFILTERS), 'is_int'));
297
            self::$_fieldPool += $pool;
298
299
            if (!is_string($where)) {
300
                $where = '';
301
            }
302
303
            foreach ($this->dsParamFILTERS as $field_id => $filter) {
304
                if ((is_array($filter) && empty($filter)) || trim($filter) === '') {
305
                    continue;
306
                }
307
308 View Code Duplication
                if (!is_array($filter)) {
309
                    $filter_type = Datasource::determineFilterType($filter);
310
                    $value = preg_split('/' . ($filter_type === Datasource::FILTER_AND ? '\+' : '(?<!\\\\),') . '\s*/',
311
                        $filter, -1, PREG_SPLIT_NO_EMPTY);
312
                    $value = array_map('trim', $value);
313
                    $value = array_map(array('Datasource', 'removeEscapedCommas'), $value);
314
                } else {
315
                    $value = $filter;
316
                }
317
318
                if (!in_array($field_id,
319
                        self::$_system_parameters) && $field_id !== 'id' && !(self::$_fieldPool[$field_id] instanceof Field)
320
                ) {
321
                    throw new Exception(
322
                        __(
323
                            'Error creating field object with id %1$d, for filtering in data source %2$s. Check this field exists.',
324
                            array($field_id, '<code>' . $this->dsParamROOTELEMENT . '</code>')
325
                        )
326
                    );
327
                }
328
329
                // Support system:id as well as the old 'id'. #1691
330
                if ($field_id === 'system:id' || $field_id === 'id') {
331
                    $c = 'IN';
332
333
                    if (stripos($value[0], 'not:') === 0) {
334
                        $value[0] = preg_replace('/^not:\s*/', null, $value[0]);
335
                        $c = 'NOT IN';
336
                    }
337
338
                    // Cast all ID's to integers. (RE: #2191)
339
                    $value = array_map(function ($val) {
340
                        $val = General::intval($val);
341
342
                        // General::intval can return -1, so reset that to 0
343
                        // so there are no side effects for the following
344
                        // array_sum and array_filter calls. RE: #2475
345
                        if ($val === -1) {
346
                            $val = 0;
347
                        }
348
349
                        return $val;
350
                    }, $value);
351
                    $count = array_sum($value);
352
                    $value = array_filter($value);
353
354
                    // If the ID was cast to 0, then we need to filter on 'id' = 0,
355
                    // which will of course return no results, but without it the
356
                    // Datasource will return ALL results, which is not the
357
                    // desired behaviour. RE: #1619
358
                    if ($count === 0) {
359
                        $value[] = 0;
360
                    }
361
362
                    // If there are no ID's, no need to filter. RE: #1567
363
                    if (!empty($value)) {
364
                        $where .= " AND `e`.id " . $c . " (" . implode(", ", $value) . ") ";
365
                    }
366
                } elseif ($field_id === 'system:creation-date' || $field_id === 'system:modification-date' || $field_id === 'system:date') {
367
                    $date_joins = '';
368
                    $date_where = '';
369
                    $date = new FieldDate();
370
                    $date->buildDSRetrievalSQL($value, $date_joins, $date_where,
371
                        ($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...
372
373
                    // Replace the date field where with the `creation_date` or `modification_date`.
374
                    $date_where = preg_replace('/`t\d+`.date/',
375
                        ($field_id !== 'system:modification-date') ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt',
376
                        $date_where);
377
                    $where .= $date_where;
378
                } else {
379
                    if (!self::$_fieldPool[$field_id]->buildDSRetrievalSQL($value, $joins, $where,
380
                        ($filter_type === Datasource::FILTER_AND ? true : false))
381
                    ) {
382
                        $this->_force_empty_result = true;
383
384
                        return;
385
                    }
386
387
                    if (!$group) {
388
                        $group = self::$_fieldPool[$field_id]->requiresSQLGrouping();
389
                    }
390
                }
391
            }
392
        }
393
394
        /**
395
         * Given a name for the group, and an associative array that
396
         * contains three keys, `attr`, `records` and `groups`. Grouping
397
         * of Entries is done by the grouping Field at a PHP level, not
398
         * through the Database.
399
         *
400
         * @param string $element
401
         *  The name for the XML node for this group
402
         * @param array $group
403
         *  An associative array of the group data, includes `attr`, `records`
404
         *  and `groups` keys.
405
         * @throws Exception
406
         * @return XMLElement
407
         */
408
        public function processRecordGroup($element, array $group)
409
        {
410
            $xGroup = new XMLElement($element, null, $group['attr']);
411
412
            if (is_array($group['records']) && !empty($group['records'])) {
413 View Code Duplication
                if (isset($group['records'][0])) {
414
                    $data = $group['records'][0]->getData();
415
                    $pool = FieldManager::fetch(array_keys($data));
416
                    self::$_fieldPool += $pool;
417
                }
418
419
                foreach ($group['records'] as $entry) {
420
                    $xEntry = $this->processEntry($entry);
421
422
                    if ($xEntry instanceof XMLElement) {
423
                        $xGroup->appendChild($xEntry);
424
                    }
425
                }
426
            }
427
428
            if (is_array($group['groups']) && !empty($group['groups'])) {
429
                foreach ($group['groups'] as $element => $group) {
430
                    foreach ($group as $g) {
431
                        $xGroup->appendChild(
432
                            $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...
433
                        );
434
                    }
435
                }
436
            }
437
438
            if (!$this->_param_output_only) {
439
                return $xGroup;
440
            }
441
        }
442
443
        /**
444
         * Given an Entry object, this function will generate an XML representation
445
         * of the Entry to be returned. It will also add any parameters selected
446
         * by this datasource to the parameter pool.
447
         *
448
         * @param Entry $entry
449
         * @throws Exception
450
         * @return XMLElement|boolean
451
         *  Returns boolean when only parameters are to be returned.
452
         */
453
        public function processEntry(Entry $entry)
454
        {
455
            $data = $entry->getData();
456
457
            $xEntry = new XMLElement('entry');
458
            $xEntry->setAttribute('id', $entry->get('id'));
459
460
            if (!empty($this->_associated_sections)) {
461
                $this->setAssociatedEntryCounts($xEntry, $entry);
462
            }
463
464
            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...
465
                $this->processSystemParameters($entry);
466
            }
467
468
            foreach ($data as $field_id => $values) {
469
                if (!isset(self::$_fieldPool[$field_id]) || !is_object(self::$_fieldPool[$field_id])) {
470
                    self::$_fieldPool[$field_id] = FieldManager::fetch($field_id);
471
                }
472
473
                $this->processOutputParameters($entry, $field_id, $values);
474
475
                if (!$this->_param_output_only) {
476
                    foreach ($this->dsParamINCLUDEDELEMENTS as $handle) {
477
                        list($handle, $mode) = preg_split('/\s*:\s*/', $handle, 2);
478
479
                        if (self::$_fieldPool[$field_id]->get('element_name') === $handle) {
480
                            self::$_fieldPool[$field_id]->appendFormattedElement($xEntry, $values,
481
                                ($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...
482
                        }
483
                    }
484
                }
485
            }
486
487
            if ($this->_param_output_only) {
488
                return true;
489
            }
490
491
            if (in_array('system:date', $this->dsParamINCLUDEDELEMENTS)) {
492
                $xDate = new XMLElement('system-date');
493
                $xDate->appendChild(
494
                    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...
495
                        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...
496
                        'created'
497
                    )
498
                );
499
                $xDate->appendChild(
500
                    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...
501
                        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...
502
                        'modified'
503
                    )
504
                );
505
                $xEntry->appendChild($xDate);
506
            }
507
508
            return $xEntry;
509
        }
510
511
        /**
512
         * An entry may be associated to other entries from various fields through
513
         * the section associations. This function will set the number of related
514
         * entries as attributes to the main `<entry>` element grouped by the
515
         * related entry's section.
516
         *
517
         * @param XMLElement $xEntry
518
         *  The <entry> XMLElement that the associated section counts will
519
         *  be set on
520
         * @param Entry $entry
521
         *  The current entry object
522
         * @throws Exception
523
         */
524
        public function setAssociatedEntryCounts(XMLElement &$xEntry, Entry $entry)
525
        {
526
            $associated_entry_counts = $entry->fetchAllAssociatedEntryCounts($this->_associated_sections);
527
528
            if (!empty($associated_entry_counts)) {
529
                foreach ($associated_entry_counts as $section_id => $fields) {
530
                    foreach ($this->_associated_sections as $section) {
531
                        if ($section['id'] !== $section_id) {
532
                            continue;
533
                        }
534
535
                        // For each related field show the count (#2083)
536
                        foreach ($fields as $field_id => $count) {
537
                            $field_handle = FieldManager::fetchHandleFromID($field_id);
538
                            if ($field_handle) {
539
                                $xEntry->setAttribute($section['handle'] . '-' . $field_handle, (string)$count);
540
                            }
541
542
                            // Backwards compatibility (without field handle)
543
                            $xEntry->setAttribute($section['handle'], (string)$count);
544
                        }
545
                    }
546
                }
547
            }
548
        }
549
550
        /**
551
         * Given an Entry object, this function will iterate over the `dsParamPARAMOUTPUT`
552
         * setting to see any of the Symphony system parameters need to be set.
553
         * The current system parameters supported are `system:id`, `system:author`
554
         * and `system:date`. If these parameters are found, the result is added
555
         * to the `$param_pool` array using the key, `ds-datasource-handle.parameter-name`
556
         * For the moment, this function also supports the pre Symphony 2.3 syntax,
557
         * `ds-datasource-handle` which did not support multiple parameters.
558
         *
559
         * @param Entry $entry
560
         *  The Entry object that contains the values that may need to be added
561
         *  into the parameter pool.
562
         */
563
        public function processSystemParameters(Entry $entry)
564
        {
565
            if (!isset($this->dsParamPARAMOUTPUT)) {
566
                return;
567
            }
568
569
            // Support the legacy parameter `ds-datasource-handle`
570
            $key = 'ds-' . $this->dsParamROOTELEMENT;
571
            $singleParam = count($this->dsParamPARAMOUTPUT) === 1;
572
573
            foreach ($this->dsParamPARAMOUTPUT as $param) {
574
                // The new style of paramater is `ds-datasource-handle.field-handle`
575
                $param_key = $key . '.' . str_replace(':', '-', $param);
576
577
                if ($param === 'system:id') {
578
                    $this->_param_pool[$param_key][] = $entry->get('id');
579
580
                    if ($singleParam) {
581
                        $this->_param_pool[$key][] = $entry->get('id');
582
                    }
583 View Code Duplication
                } elseif ($param === 'system:author') {
584
                    $this->_param_pool[$param_key][] = $entry->get('author_id');
585
586
                    if ($singleParam) {
587
                        $this->_param_pool[$key][] = $entry->get('author_id');
588
                    }
589
                } elseif ($param === 'system:creation-date' || $param === 'system:date') {
590
                    $this->_param_pool[$param_key][] = $entry->get('creation_date');
591
592
                    if ($singleParam) {
593
                        $this->_param_pool[$key][] = $entry->get('creation_date');
594
                    }
595 View Code Duplication
                } elseif ($param === 'system:modification-date') {
596
                    $this->_param_pool[$param_key][] = $entry->get('modification_date');
597
598
                    if ($singleParam) {
599
                        $this->_param_pool[$key][] = $entry->get('modification_date');
600
                    }
601
                }
602
            }
603
        }
604
605
        /**
606
         * Given an Entry object, a `$field_id` and an array of `$data`, this
607
         * function iterates over the `dsParamPARAMOUTPUT` and will call the
608
         * field's (identified by `$field_id`) `getParameterPoolValue` function
609
         * to add parameters to the `$this->_param_pool`.
610
         *
611
         * @param Entry $entry
612
         * @param integer $field_id
613
         * @param array $data
614
         */
615
        public function processOutputParameters(Entry $entry, $field_id, array $data)
616
        {
617
            if (!isset($this->dsParamPARAMOUTPUT)) {
618
                return;
619
            }
620
621
            // Support the legacy parameter `ds-datasource-handle`
622
            $key = 'ds-' . $this->dsParamROOTELEMENT;
623
624
            foreach ($this->dsParamPARAMOUTPUT as $param) {
625
                if (self::$_fieldPool[$field_id]->get('element_name') !== $param) {
626
                    continue;
627
                }
628
629
                // The new style of paramater is `ds-datasource-handle.field-handle`
630
                $param_key = $key . '.' . str_replace(':', '-', $param);
631
632
                if (!isset($this->_param_pool[$param_key]) || !is_array($this->_param_pool[$param_key])) {
633
                    $this->_param_pool[$param_key] = array();
634
                }
635
636
                $param_pool_values = self::$_fieldPool[$field_id]->getParameterPoolValue($data, $entry->get('id'));
637
638
                if (is_array($param_pool_values)) {
639
                    $this->_param_pool[$param_key] = array_merge($param_pool_values, $this->_param_pool[$param_key]);
640
                } elseif (!is_null($param_pool_values)) {
641
                    $this->_param_pool[$param_key][] = $param_pool_values;
642
                }
643
            }
644
        }
645
    }
646