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 — 2.6.x (#2509)
by Nicolas
04:59
created

contentPublish::createFilterComparisons()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 6
eloc 17
c 1
b 0
f 1
nc 4
nop 1
dl 0
loc 33
rs 8.439
1
<?php
2
3
/**
4
 * @package content
5
 */
6
/**
7
 * The Publish page is where the majority of an Authors time will
8
 * be spent in Symphony with adding, editing and removing entries
9
 * from Sections. This Page controls the entries table as well as
10
 * the Entry creation screens.
11
 */
12
13
class contentPublish extends AdministrationPage
14
{
15
    public $_errors = array();
16
17
    public function sort(&$sort, &$order, $params)
18
    {
19
        $section = $params['current-section'];
20
21
        // If `?unsort` is appended to the URL, then sorting is reverted
22
        // to 'none', aka. by 'entry-id'.
23
        if ($params['unsort']) {
24
            $section->setSortingField('id', false);
25
            $section->setSortingOrder('desc');
26
27
            redirect(Administration::instance()->getCurrentPageURL());
28
        }
29
30
        // By default, sorting information are retrieved from
31
        // the filesystem and stored inside the `Configuration` object
32
        if (is_null($sort) && is_null($order)) {
33
            $sort = $section->getSortingField();
34
            $order = $section->getSortingOrder();
35
36
            // Set the sorting in the `EntryManager` for subsequent use
37
            EntryManager::setFetchSorting($sort, $order);
38
        } else {
39
            $sort = General::sanitize($sort);
40
41
            // Ensure that this field is infact sortable, otherwise
42
            // fallback to IDs
43
            if (($field = FieldManager::fetch($sort)) instanceof Field && !$field->isSortable()) {
44
                $sort = $section->getDefaultSortingField();
45
            }
46
47
            // If the sort order or direction differs from what is saved,
48
            // update the config file and reload the page
49
            if ($sort != $section->getSortingField() || $order != $section->getSortingOrder()) {
50
                $section->setSortingField($sort, false);
51
                $section->setSortingOrder($order);
52
53 View Code Duplication
                if ($params['filters']) {
54
                    $params['filters'] = '?' . trim($params['filters'], '&amp;');
55
                }
56
57
                redirect(Administration::instance()->getCurrentPageURL() . $params['filters']);
58
            }
59
60
            // If the sort order or direction remains the same, reload the page
61
            if ($sort == $section->getSortingField() && $order == $section->getSortingOrder()) {
62 View Code Duplication
                if ($params['filters']) {
63
                    $params['filters'] = '?' . trim($params['filters'], '&amp;');
64
                }
65
66
                redirect(Administration::instance()->getCurrentPageURL() . $params['filters']);
67
            }
68
        }
69
    }
70
71
    /**
72
     * Append filtering interface
73
     */
74
    public function createFilteringInterface()
75
    {
76
        //Check if section has filtering enabled
77
        $context = $this->getContext();
78
        $handle = $context['section_handle'];
79
        $section_id = SectionManager::fetchIDFromHandle($handle);
80
        $section = SectionManager::fetch($section_id);
81
        $filter = $section->get('filter');
82
        $count = EntryManager::fetchCount($section_id);
83
84
        if ($filter !== 'no' && $count > 1) {
85
            $drawer = Widget::Drawer('filtering-' . $section_id, __('Filter Entries'), $this->createFilteringDrawer($section));
86
            $drawer->addClass('drawer-filtering');
87
            $this->insertDrawer($drawer);
88
        }
89
    }
90
91
    /**
92
     * Create filtering drawer
93
     */
94
    public function createFilteringDrawer($section)
95
    {
96
        $this->filteringForm = Widget::Form(null, 'get', 'filtering');
0 ignored issues
show
Bug introduced by
The property filteringForm 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...
97
        $this->createFilteringDuplicator($section);
98
99
        return $this->filteringForm;
100
    }
101
102
    public function createFilteringDuplicator($section)
103
    {
104
        $div = new XMLElement('div');
105
        $div->setAttribute('class', 'frame filters-duplicator');
106
        $div->setAttribute('data-interactive', 'data-interactive');
107
108
        $ol = new XMLElement('ol');
109
        $ol->setAttribute('data-add', __('Add filter'));
110
        $ol->setAttribute('data-remove', __('Clear filter'));
111
        $ol->setAttribute('data-empty', __('No filters applied yet.'));
112
113
        $this->createFieldFilters($ol, $section);
114
        $this->createSystemDateFilters($ol);
115
116
        $div->appendChild($ol);
117
        $this->filteringForm->appendChild($div);
118
    }
119
120
    private function createFieldFilters(&$wrapper, $section)
121
    {
122
        $filters = $_GET['filter'];
123
124
        foreach ($section->fetchFilterableFields() as $field) {
125
            if (!$field->canPublishFilter()) {
126
                continue;
127
            }
128
129
            $filter = $filters[$field->get('element_name')];
130
131
            // Filter data
132
            $data = array();
133
            $data['type'] = $field->get('element_name');
134
            $data['name'] = $field->get('label');
135
            $data['filter'] = $filter;
136
            $data['instance'] = 'unique';
137
            $data['search'] = $field->fetchSuggestionTypes();
138
            $data['operators'] = $field->fetchFilterableOperators();
139
            $data['comparisons'] = $this->createFilterComparisons($data);
140
            $data['query'] = $this->getFilterQuery($data);
141
            $data['field-id'] = $field->get('id');
142
143
            // Add existing filter
144
            if (isset($filter)) {
145
                $this->createFilter($wrapper, $data);
146
            }
147
148
            // Add filter template
149
            $data['instance'] = 'unique template';
150
            $data['query'] = '';
151
            $this->createFilter($wrapper, $data);
152
        }
153
    }
154
155
    private function createSystemDateFilters(&$wrapper)
156
    {
157
        $filters = $_GET['filter'];
158
        $dateField = new FieldDate;
159
160
        $fields = array(
161
            array(
162
                'type' => 'system:creation-date',
163
                'label' => __('System Creation Date')
164
            ),
165
            array(
166
                'type' => 'system:modification-date',
167
                'label' => __('System Modification Date')
168
            )
169
        );
170
171
        foreach ($fields as $field) {
172
            $filter = $filters[$field['type']];
173
174
            // Filter data
175
            $data = array();
176
            $data['type'] = $field['type'];
177
            $data['name'] = $field['label'];
178
            $data['filter'] = $filter;
179
            $data['instance'] = 'unique';
180
            $data['search'] = $dateField->fetchSuggestionTypes();
181
            $data['operators'] = $dateField->fetchFilterableOperators();
182
            $data['comparisons'] = $this->createFilterComparisons($data);
183
            $data['query'] = $this->getFilterQuery($data);
184
185
            // Add existing filter
186
            if (isset($filter)) {
187
                $this->createFilter($wrapper, $data);
188
            }
189
190
            // Add filter template
191
            $data['instance'] = 'unique template';
192
            $data['query'] = '';
193
            $this->createFilter($wrapper, $data);
194
        }
195
    }
196
197
    private function createFilter(&$wrapper, $data)
198
    {
199
        $li = new XMLElement('li');
200
        $li->setAttribute('class', $data['instance']);
201
        $li->setAttribute('data-type', $data['type']);
202
203
        // Header
204
        $li->appendChild(new XMLElement('header', $data['name'], array(
205
            'data-name' => $data['name']
206
        )));
207
208
        // Settings
209
        $div = new XMLElement('div', null, array('class' => 'two columns'));
210
211
        // Comparisons
212
        $label = Widget::Label();
213
        $label->setAttribute('class', 'column secondary');
214
215
        $select = Widget::Select($data['type'] . '-comparison', $data['comparisons'], array(
216
            'class' => 'comparison'
217
        ));
218
219
        $label->appendChild($select);
220
        $div->appendChild($label);
221
222
        // Query
223
        $label = Widget::Label();
224
        $label->setAttribute('class', 'column primary');
225
226
        $input = Widget::Input($data['type'], $data['query'], 'text', array(
227
            'placeholder' => __('Type and hit enter to apply filter…'),
228
            'autocomplete' => 'off'
229
        ));
230
        $input->setAttribute('class', 'filter');
231
        $label->appendChild($input);
232
233
        $this->createFilterSuggestions($label, $data);
234
235
        $div->appendChild($label);
236
        $li->appendChild($div);
237
        $wrapper->appendChild($li);
238
    }
239
240
    private function createFilterComparisons($data)
241
    {
242
        // Default comparison
243
        $comparisons = array();
244
245
        // Custom field comparisons
246
        foreach ($data['operators'] as $operator) {
247
            
248
            $filter = trim($operator['filter']);
249
            
250
            // Check selected state
251
            $selected = false;
252
            
253
            // Selected state : Comparison mode "between" (x to y)
254
            if ($operator['title'] === 'between' && preg_match('/^(-?(?:\d+(?:\.\d+)?|\.\d+)) to (-?(?:\d+(?:\.\d+)?|\.\d+))$/i', $data['filter'] )) {
255
                $selected = true;
256
            // Selected state : Other comparison modes (except "is")
257
            } else if ((!empty($filter) && strpos($data['filter'], $filter) === 0)) {
258
                $selected = true;
259
            }
260
	        
261
            $comparisons[] = array(
262
                $operator['filter'],
263
                $selected,
264
                __($operator['title']),
265
                null,
266
                null,
267
                array('data-comparison' => $operator['title'])
268
            );
269
        }
270
271
        return $comparisons;
272
    }
273
274
    private function createFilterSuggestions(&$wrapper, $data)
275
    {
276
        $ul = new XMLElement('ul');
277
        $ul->setAttribute('class', 'suggestions');
278
        $ul->setAttribute('data-field-id', $data['field-id']);
279
        $ul->setAttribute('data-associated-ids', '0');
280
        $ul->setAttribute('data-search-types', implode($data['search'], ','));
281
282
        // Add help text for each filter operator
283
        foreach ($data['operators'] as $operator) {
284
            $this->createFilterHelp($ul, $operator);
285
        }
286
287
        $wrapper->appendChild($ul);
288
    }
289
290
    private function createFilterHelp(&$wrapper, $operator)
291
    {
292
        if (empty($operator['help'])) {
293
            return;
294
        }
295
296
        $li = new XMLElement('li', __('Comparison mode') . ': ' . $operator['help'], array(
297
            'class' => 'help',
298
            'data-comparison' => $operator['title']
299
        ));
300
301
        $wrapper->appendChild($li);
302
    }
303
304
    private function getFilterQuery($data)
305
    {
306
        $query = $data['filter'];
307
308
        foreach ($data['operators'] as $operator) {
309
            $filter = trim($operator['filter']);
310
311
            if (!empty($filter) && strpos($data['filter'], $filter) === 0) {
312
                $query = substr($data['filter'], strlen($operator['filter']));
313
            }
314
        }
315
316
        return (string)$query;
317
    }
318
319 View Code Duplication
    public function build(array $context = array())
320
    {
321
        $section_id = SectionManager::fetchIDFromHandle($context['section_handle']);
322
323
        if ($section_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $section_id of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
324
            $context['associations'] = array(
325
                'parent' => SectionManager::fetchParentAssociations($section_id),
326
                'child' => SectionManager::fetchChildAssociations($section_id)
327
            );
328
        }
329
330
        return parent::build($context);
331
    }
332
333
    public function action()
334
    {
335
        $this->__switchboard('action');
336
    }
337
338
    public function __switchboard($type = 'view')
339
    {
340
        $function = ($type == 'action' ? '__action' : '__view') . ucfirst($this->_context['page']);
341
342
        if (!method_exists($this, $function)) {
343
            // If there is no action function, just return without doing anything
344
            if ($type == 'action') {
345
                return;
346
            }
347
348
            Administration::instance()->errorPageNotFound();
349
        }
350
351
        // Is this request allowed by server?
352
        if ($this->isRequestValid() === false) {
353
            $this->pageAlert(__('This request exceeds the maximum allowed request size of %s specified by your host.', array(
354
                    ini_get('post_max_size')
355
                )),
356
                Alert::ERROR
357
            );
358
        }
359
        $this->$function();
360
    }
361
362
    public function view()
363
    {
364
        $this->__switchboard();
365
    }
366
367
    public function __viewIndex()
368
    {
369
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
370
            Administration::instance()->throwCustomError(
371
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
372
                __('Unknown Section'),
373
                Page::HTTP_STATUS_NOT_FOUND
374
            );
375
        } elseif (!is_writable(CONFIG)) {
376
            $this->pageAlert(__('The Symphony configuration file, %s, is not writable. The sort order cannot be modified.', array('<code>/manifest/config.php</code>')), Alert::NOTICE);
377
        }
378
379
        $section = SectionManager::fetch($section_id);
380
381
        $this->setPageType('table');
382
        $this->setTitle(__('%1$s &ndash; %2$s', array($section->get('name'), __('Symphony'))));
383
384
        $filters = array();
385
        $filter_querystring = $prepopulate_querystring = $where = $joins = null;
386
        $current_page = (isset($_REQUEST['pg']) && is_numeric($_REQUEST['pg']) ? max(1, intval($_REQUEST['pg'])) : 1);
387
388
        if (isset($_REQUEST['filter'])) {
389
            // legacy implementation, convert single filter to an array
390
            // split string in the form ?filter=handle:value
391
            if (!is_array($_REQUEST['filter'])) {
392
                list($field_handle, $filter_value) = explode(':', $_REQUEST['filter'], 2);
393
                $filters[$field_handle] = rawurldecode($filter_value);
394
            } else {
395
                $filters = $_REQUEST['filter'];
396
            }
397
398
            foreach ($filters as $handle => $value) {
399
                // Handle multiple values through filtering. RE: #2290
400
                if ((is_array($value) && empty($value)) || trim($value) == '') {
401
                    continue;
402
                }
403
404 View Code Duplication
                if (!is_array($value)) {
405
                    $filter_type = Datasource::determineFilterType($value);
406
                    $value = preg_split('/'.($filter_type == Datasource::FILTER_AND ? '\+' : '(?<!\\\\),').'\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
407
                    $value = array_map('trim', $value);
408
                    $value = array_map(array('Datasource', 'removeEscapedCommas'), $value);
409
                }
410
411
                // Handle date meta data #2003
412
                $handle = Symphony::Database()->cleanValue($handle);
413
                if (in_array($handle, array('system:creation-date', 'system:modification-date'))) {
414
                    $date_joins = '';
415
                    $date_where = '';
416
                    $date = new FieldDate();
417
                    $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...
418
419
                    // Replace the date field where with the `creation_date` or `modification_date`.
420
                    $date_where = preg_replace('/`t\d+`.date/', ($field_id !== 'system:modification-date') ? '`e`.creation_date_gmt' : '`e`.modification_date_gmt', $date_where);
0 ignored issues
show
Bug introduced by
The variable $field_id 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...
421
                    $where .= $date_where;
422
                } else {
423
                    // Handle normal fields
424
                    $field_id = FieldManager::fetchFieldIDFromElementName(
425
                        $handle,
426
                        $section->get('id')
427
                    );
428
429
                    $field = FieldManager::fetch($field_id);
430
                    if ($field instanceof Field) {
431
                        $field->buildDSRetrievalSQL($value, $joins, $where, ($filter_type == Datasource::FILTER_AND ? true : false));
432
433
                        $value = implode(',', $value);
434
                        $encoded_value = rawurlencode($value);
435
                        $filter_querystring .= sprintf("filter[%s]=%s&amp;", $handle, $encoded_value);
436
437
                        // Some fields require that prepopulation be done via ID. RE: #2331
438
                        if (!is_numeric($value) && method_exists($field, 'fetchIDfromValue')) {
439
                            $encoded_value = $field->fetchIDfromValue($value);
440
                        }
441
                        $prepopulate_querystring .= sprintf("prepopulate[%d]=%s&amp;", $field_id, $encoded_value);
442
                    } else {
443
                        unset($filters[$handle]);
444
                    }
445
                }
446
            }
447
448
            $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
449
            $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
450
        }
451
452
        Sortable::initialize($this, $entries, $sort, $order, array(
453
            'current-section' => $section,
454
            'filters' => ($filter_querystring ? "&amp;" . $filter_querystring : ''),
455
            'unsort' => isset($_REQUEST['unsort'])
456
        ));
457
458
        $this->Form->setAttribute('action', Administration::instance()->getCurrentPageURL(). '?pg=' . $current_page.($filter_querystring ? "&amp;" . $filter_querystring : ''));
459
460
        // Build filtering interface
461
        $this->createFilteringInterface();
462
463
        $subheading_buttons = array(
464
            Widget::Anchor(__('Create New'), Administration::instance()->getCurrentPageURL().'new/'.($prepopulate_querystring ? '?' . $prepopulate_querystring : ''), __('Create a new entry'), 'create button', null, array('accesskey' => 'c'))
465
        );
466
467
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
468
        if (Symphony::Author()->isDeveloper()) {
469
            array_unshift($subheading_buttons, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button'));
470
        }
471
472
        $this->appendSubheading($section->get('name'), $subheading_buttons);
473
474
        /**
475
         * Allows adjustments to be made to the SQL where and joins statements
476
         * before they are used to fetch the entries for the page
477
         *
478
         * @delegate AdjustPublishFiltering
479
         * @since Symphony 2.3.3
480
         * @param string $context
481
         * '/publish/'
482
         * @param integer $section_id
483
         * An array of the current columns, passed by reference
484
         * @param string $where
485
         * The current where statement, or null if not set
486
         * @param string $joins
487
         */
488
        Symphony::ExtensionManager()->notifyMembers('AdjustPublishFiltering', '/publish/', array('section-id' => $section_id, 'where' => &$where, 'joins' => &$joins));
489
490
        // get visible columns
491
        $visible_columns = $section->fetchVisibleColumns();
492
        // extract the needed schema
493
        $element_names = array_values(array_map(function ($field) {
494
            return $field->get('element_name');
495
        }, $visible_columns));
496
497
        // Check that the filtered query fails that the filter is dropped and an
498
        // error is logged. #841 ^BA
499
        try {
500
            $entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), $where, $joins, true, false, true, $element_names);
0 ignored issues
show
Documentation introduced by
\Symphony::Configuration...imum_rows', 'symphony') is of type array|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...
501
        } catch (DatabaseException $ex) {
502
            $this->pageAlert(__('An error occurred while retrieving filtered entries. Showing all entries instead.'), Alert::ERROR);
503
            $filter_querystring = null;
504
            Symphony::Log()->pushToLog(sprintf(
505
                    '%s - %s%s%s',
506
                    $section->get('name') . ' Publish Index',
507
                    $ex->getMessage(),
508
                    ($ex->getFile() ? " in file " .  $ex->getFile() : null),
509
                    ($ex->getLine() ? " on line " . $ex->getLine() : null)
510
                ),
511
                E_NOTICE,
512
                true
513
            );
514
            $entries = EntryManager::fetchByPage($current_page, $section_id, Symphony::Configuration()->get('pagination_maximum_rows', 'symphony'), null, null, true, false, true, $element_names);
0 ignored issues
show
Documentation introduced by
\Symphony::Configuration...imum_rows', 'symphony') is of type array|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...
515
        }
516
517
        // Flag filtering
518
        if (isset($_REQUEST['filter'])) {
519
            $filter_stats = new XMLElement('p', '<span>– ' . __('%d of %d entries (filtered)', array($entries['total-entries'], EntryManager::fetchCount($section_id))) . '</span>', array('class' => 'inactive'));
520
        } else {
521
            $filter_stats = new XMLElement('p', '<span>– ' . __('%d entries', array($entries['total-entries'])) . '</span>', array('class' => 'inactive'));
522
        }
523
        $this->Breadcrumbs->appendChild($filter_stats);
524
525
        // Build table
526
        $columns = array();
527
528
        if (is_array($visible_columns) && !empty($visible_columns)) {
529
            foreach ($visible_columns as $column) {
530
                $columns[] = array(
531
                    'label' => $column->get('label'),
532
                    'sortable' => $column->isSortable(),
533
                    'handle' => $column->get('id'),
534
                    'attrs' => array(
535
                        'id' => 'field-' . $column->get('id'),
536
                        'class' => 'field-' . $column->get('type')
537
                    )
538
                );
539
            }
540
        } else {
541
            $columns[] = array(
542
                'label' => __('ID'),
543
                'sortable' => true,
544
                'handle' => 'id'
545
            );
546
        }
547
548
        $aTableHead = Sortable::buildTableHeaders($columns, $sort, $order, ($filter_querystring) ? "&amp;" . $filter_querystring : '');
549
550
        $child_sections = array();
551
        $associated_sections = $section->fetchChildAssociations(true);
552
553
        if (is_array($associated_sections) && !empty($associated_sections)) {
554
            foreach ($associated_sections as $key => $as) {
555
                $child_sections[$key] = SectionManager::fetch($as['child_section_id']);
556
                $aTableHead[] = array($child_sections[$key]->get('name'), 'col');
557
            }
558
        }
559
560
        /**
561
         * Allows the creation of custom table columns for each entry. Called
562
         * after all the Section Visible columns have been added as well
563
         * as the Section Associations
564
         *
565
         * @delegate AddCustomPublishColumn
566
         * @since Symphony 2.2
567
         * @param string $context
568
         * '/publish/'
569
         * @param array $tableHead
570
         * An array of the current columns, passed by reference
571
         * @param integer $section_id
572
         * The current Section ID
573
         */
574
        Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumn', '/publish/', array('tableHead' => &$aTableHead, 'section_id' => $section->get('id')));
575
576
        // Table Body
577
        $aTableBody = array();
578
579
        if (!is_array($entries['records']) || empty($entries['records'])) {
580
            $aTableBody = array(
581
                Widget::TableRow(array(Widget::TableData(__('None found.'), 'inactive', null, count($aTableHead))), 'odd')
582
            );
583
        } else {
584
            $field_pool = array();
585
586
            if (is_array($visible_columns) && !empty($visible_columns)) {
587
                foreach ($visible_columns as $column) {
588
                    $field_pool[$column->get('id')] = $column;
589
                }
590
            }
591
592
            $link_column = array_reverse($visible_columns);
593
            $link_column = end($link_column);
594
            reset($visible_columns);
595
596
            foreach ($entries['records'] as $entry) {
597
                $tableData = array();
598
599
                // Setup each cell
600
                if (!is_array($visible_columns) || empty($visible_columns)) {
601
                    $tableData[] = Widget::TableData(Widget::Anchor($entry->get('id'), Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'));
602
                } else {
603
                    $link = Widget::Anchor(
604
                        '',
605
                        Administration::instance()->getCurrentPageURL() . 'edit/' . $entry->get('id') . '/'.($filter_querystring ? '?' . $prepopulate_querystring : ''),
606
                        $entry->get('id'),
607
                        'content'
608
                    );
609
610
                    foreach ($visible_columns as $position => $column) {
611
                        $data = $entry->getData($column->get('id'));
612
                        $field = $field_pool[$column->get('id')];
613
614
                        $value = $field->prepareTableValue($data, ($column == $link_column) ? $link : null, $entry->get('id'));
615
616
                        if (!is_object($value) && (strlen(trim($value)) == 0 || $value == __('None'))) {
617
                            $value = ($position == 0 ? $link->generate() : __('None'));
618
                        }
619
620
                        if ($value == __('None')) {
621
                            $tableData[] = Widget::TableData($value, 'inactive field-' . $column->get('type') . ' field-' . $column->get('id'));
622
                        } else {
623
                            $tableData[] = Widget::TableData($value, 'field-' . $column->get('type') . ' field-' . $column->get('id'));
624
                        }
625
626
                        unset($field);
627
                    }
628
                }
629
630
                if (is_array($child_sections) && !empty($child_sections)) {
631
                    foreach ($child_sections as $key => $as) {
632
                        $field = FieldManager::fetch((int)$associated_sections[$key]['child_section_field_id']);
633
                        $parent_section_field_id = (int)$associated_sections[$key]['parent_section_field_id'];
634
635
                        if (!is_null($parent_section_field_id)) {
636
                            $search_value = $field->fetchAssociatedEntrySearchValue(
0 ignored issues
show
Bug introduced by
The method fetchAssociatedEntrySearchValue cannot be called on $field (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...
637
                                $entry->getData($parent_section_field_id),
638
                                $parent_section_field_id,
639
                                $entry->get('id')
640
                            );
641
                        } else {
642
                            $search_value = $entry->get('id');
643
                        }
644
645
                        if (!is_array($search_value)) {
646
                            $associated_entry_count = $field->fetchAssociatedEntryCount($search_value);
0 ignored issues
show
Bug introduced by
The method fetchAssociatedEntryCount cannot be called on $field (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...
647
648
                            $tableData[] = Widget::TableData(
649
                                Widget::Anchor(
650
                                    sprintf('%d &rarr;', max(0, intval($associated_entry_count))),
651
                                    sprintf(
652
                                        '%s/publish/%s/?filter[%s]=%s',
653
                                        SYMPHONY_URL,
654
                                        $as->get('handle'),
655
                                        $field->get('element_name'),
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (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...
656
                                        rawurlencode($search_value)
657
                                    ),
658
                                    $entry->get('id'),
659
                                    'content'
660
                                )
661
                            );
662
                        }
663
                    }
664
                }
665
666
                /**
667
                 * Allows Extensions to inject custom table data for each Entry
668
                 * into the Publish Index
669
                 *
670
                 * @delegate AddCustomPublishColumnData
671
                 * @since Symphony 2.2
672
                 * @param string $context
673
                 * '/publish/'
674
                 * @param array $tableData
675
                 *  An array of `Widget::TableData`, passed by reference
676
                 * @param integer $section_id
677
                 *  The current Section ID
678
                 * @param Entry $entry_id
679
                 *  The entry object, please note that this is by error and this will
680
                 *  be removed in Symphony 2.4. The entry object is available in
681
                 *  the 'entry' key as of Symphony 2.3.1.
682
                 * @param Entry $entry
683
                 *  The entry object for this row
684
                 */
685
                Symphony::ExtensionManager()->notifyMembers('AddCustomPublishColumnData', '/publish/', array(
686
                    'tableData' => &$tableData,
687
                    'section_id' => $section->get('id'),
688
                    'entry_id' => $entry,
689
                    'entry' => $entry
690
                ));
691
692
                $tableData[count($tableData) - 1]->appendChild(Widget::Label(__('Select Entry %d', array($entry->get('id'))), null, 'accessible', null, array(
693
                    'for' => 'entry-' . $entry->get('id')
694
                )));
695
                $tableData[count($tableData) - 1]->appendChild(Widget::Input('items['.$entry->get('id').']', null, 'checkbox', array(
696
                    'id' => 'entry-' . $entry->get('id')
697
                )));
698
699
                // Add a row to the body array, assigning each cell to the row
700
                $aTableBody[] = Widget::TableRow($tableData, null, 'id-' . $entry->get('id'));
701
            }
702
        }
703
704
        $table = Widget::Table(
705
            Widget::TableHead($aTableHead),
706
            null,
707
            Widget::TableBody($aTableBody),
708
            'selectable',
709
            null,
710
            array('role' => 'directory', 'aria-labelledby' => 'symphony-subheading', 'data-interactive' => 'data-interactive')
711
        );
712
713
        $this->Form->appendChild($table);
714
715
        $tableActions = new XMLElement('div');
716
        $tableActions->setAttribute('class', 'actions');
717
718
        $options = array(
719
            array(null, false, __('With Selected...')),
720
            array('delete', false, __('Delete'), 'confirm', null, array(
721
                'data-message' => __('Are you sure you want to delete the selected entries?')
722
            ))
723
        );
724
725
        $toggable_fields = $section->fetchToggleableFields();
726
727
        if (is_array($toggable_fields) && !empty($toggable_fields)) {
728
            $index = 2;
729
730
            foreach ($toggable_fields as $field) {
731
                $toggle_states = $field->getToggleStates();
732
733
                if (is_array($toggle_states)) {
734
                    $options[$index] = array('label' => __('Set %s', array($field->get('label'))), 'options' => array());
735
736
                    foreach ($toggle_states as $value => $state) {
737
                        $options[$index]['options'][] = array('toggle-' . $field->get('id') . '-' . $value, false, $state);
738
                    }
739
                }
740
741
                $index++;
742
            }
743
        }
744
745
        /**
746
         * Allows an extension to modify the existing options for this page's
747
         * With Selected menu. If the `$options` parameter is an empty array,
748
         * the 'With Selected' menu will not be rendered.
749
         *
750
         * @delegate AddCustomActions
751
         * @since Symphony 2.3.2
752
         * @param string $context
753
         * '/publish/'
754
         * @param array $options
755
         *  An array of arrays, where each child array represents an option
756
         *  in the With Selected menu. Options should follow the same format
757
         *  expected by `Widget::__SelectBuildOption`. Passed by reference.
758
         */
759
        Symphony::ExtensionManager()->notifyMembers('AddCustomActions', '/publish/', array(
760
            'options' => &$options
761
        ));
762
763
        if (!empty($options)) {
764
            $tableActions->appendChild(Widget::Apply($options));
765
            $this->Form->appendChild($tableActions);
766
        }
767
768
        if ($entries['total-pages'] > 1) {
769
            $ul = new XMLElement('ul');
770
            $ul->setAttribute('class', 'page');
771
772
            // First
773
            $li = new XMLElement('li');
774
775
            if ($current_page > 1) {
776
                $li->appendChild(Widget::Anchor(__('First'), Administration::instance()->getCurrentPageURL(). '?pg=1'.($filter_querystring ? "&amp;" . $filter_querystring : '')));
777
            } else {
778
                $li->setValue(__('First'));
779
            }
780
781
            $ul->appendChild($li);
782
783
            // Previous
784
            $li = new XMLElement('li');
785
786 View Code Duplication
            if ($current_page > 1) {
787
                $li->appendChild(Widget::Anchor(__('&larr; Previous'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page - 1).($filter_querystring ? "&amp;" . $filter_querystring : '')));
788
            } else {
789
                $li->setValue(__('&larr; Previous'));
790
            }
791
792
            $ul->appendChild($li);
793
794
            // Summary
795
            $li = new XMLElement('li');
796
797
            $li->setAttribute('title', __('Viewing %1$s - %2$s of %3$s entries', array(
798
                $entries['start'],
799
                ($current_page != $entries['total-pages']) ? $current_page * Symphony::Configuration()->get('pagination_maximum_rows', 'symphony') : $entries['total-entries'],
800
                $entries['total-entries']
801
            )));
802
803
            $pgform = Widget::Form(Administration::instance()->getCurrentPageURL(), 'get', 'paginationform');
804
805
            $pgmax = max($current_page, $entries['total-pages']);
806
            $pgform->appendChild(Widget::Input('pg', null, 'text', array(
807
                'data-active' => __('Go to page …'),
808
                'data-inactive' => __('Page %1$s of %2$s', array((string)$current_page, $pgmax)),
809
                'data-max' => $pgmax
810
            )));
811
812
            $li->appendChild($pgform);
813
            $ul->appendChild($li);
814
815
            // Next
816
            $li = new XMLElement('li');
817
818 View Code Duplication
            if ($current_page < $entries['total-pages']) {
819
                $li->appendChild(Widget::Anchor(__('Next &rarr;'), Administration::instance()->getCurrentPageURL(). '?pg=' . ($current_page + 1).($filter_querystring ? "&amp;" . $filter_querystring : '')));
820
            } else {
821
                $li->setValue(__('Next &rarr;'));
822
            }
823
824
            $ul->appendChild($li);
825
826
            // Last
827
            $li = new XMLElement('li');
828
829 View Code Duplication
            if ($current_page < $entries['total-pages']) {
830
                $li->appendChild(Widget::Anchor(__('Last'), Administration::instance()->getCurrentPageURL(). '?pg=' . $entries['total-pages'].($filter_querystring ? "&amp;" . $filter_querystring : '')));
831
            } else {
832
                $li->setValue(__('Last'));
833
            }
834
835
            $ul->appendChild($li);
836
837
            $this->Contents->appendChild($ul);
838
        }
839
    }
840
841
    public function __actionIndex()
0 ignored issues
show
Coding Style introduced by
__actionIndex uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__actionIndex uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
842
    {
843
        $checked = (is_array($_POST['items'])) ? array_keys($_POST['items']) : null;
844
845
        if (is_array($checked) && !empty($checked)) {
846
            /**
847
             * Extensions can listen for any custom actions that were added
848
             * through `AddCustomPreferenceFieldsets` or `AddCustomActions`
849
             * delegates.
850
             *
851
             * @delegate CustomActions
852
             * @since Symphony 2.3.2
853
             * @param string $context
854
             *  '/publish/'
855
             * @param array $checked
856
             *  An array of the selected rows. The value is usually the ID of the
857
             *  the associated object.
858
             */
859
            Symphony::ExtensionManager()->notifyMembers('CustomActions', '/publish/', array(
860
                'checked' => $checked
861
            ));
862
863
            switch ($_POST['with-selected']) {
864
                case 'delete':
865
                    /**
866
                     * Prior to deletion of entries. An array of Entry ID's is provided which
867
                     * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
868
                     * in Symphony 2.3.
869
                     *
870
                     * @delegate EntryPreDelete
871
                     * @param string $context
872
                     * '/publish/'
873
                     * @param array $entry_id
874
                     *  An array of Entry ID's passed by reference
875
                     */
876
                    Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
877
878
                    EntryManager::delete($checked);
879
880
                    /**
881
                     * After the deletion of entries, this delegate provides an array of Entry ID's
882
                     * that were deleted.
883
                     *
884
                     * @since Symphony 2.3
885
                     * @delegate EntryPostDelete
886
                     * @param string $context
887
                     * '/publish/'
888
                     * @param array $entry_id
889
                     *  An array of Entry ID's that were deleted.
890
                     */
891
                    Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
892
893
                    redirect($_SERVER['REQUEST_URI']);
894
                    break;
895
                default:
896
                    list($option, $field_id, $value) = explode('-', $_POST['with-selected'], 3);
897
898
                    if ($option == 'toggle') {
899
                        $field = FieldManager::fetch($field_id);
900
                        $fields = array($field->get('element_name') => $value);
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (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...
901
902
                        $section = SectionManager::fetch($field->get('parent_section'));
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (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...
903
904
                        foreach ($checked as $entry_id) {
905
                            $entry = EntryManager::fetch($entry_id);
906
                            $existing_data = $entry[0]->getData($field_id);
907
                            $entry[0]->setData($field_id, $field->toggleFieldData(is_array($existing_data) ? $existing_data : array(), $value, $entry_id));
0 ignored issues
show
Bug introduced by
The method toggleFieldData cannot be called on $field (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...
908
909
                            /**
910
                             * Just prior to editing of an Entry
911
                             *
912
                             * @delegate EntryPreEdit
913
                             * @param string $context
914
                             * '/publish/edit/'
915
                             * @param Section $section
916
                             * @param Entry $entry
917
                             * @param array $fields
918
                             */
919
                            Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array(
920
                                'section' => $section,
921
                                'entry' => &$entry[0],
922
                                'fields' => $fields
923
                            ));
924
925
                            $entry[0]->commit();
926
927
                            /**
928
                             * Editing an entry. Entry object is provided.
929
                             *
930
                             * @delegate EntryPostEdit
931
                             * @param string $context
932
                             * '/publish/edit/'
933
                             * @param Section $section
934
                             * @param Entry $entry
935
                             * @param array $fields
936
                             */
937
                            Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array(
938
                                'section' => $section,
939
                                'entry' => $entry[0],
940
                                'fields' => $fields
941
                            ));
942
                        }
943
944
                        redirect($_SERVER['REQUEST_URI']);
945
                    }
946
            }
947
        }
948
    }
949
950
    public function __viewNew()
0 ignored issues
show
Coding Style introduced by
__viewNew uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
951
    {
952 View Code Duplication
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
953
            Administration::instance()->throwCustomError(
954
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
955
                __('Unknown Section'),
956
                Page::HTTP_STATUS_NOT_FOUND
957
            );
958
        }
959
960
        $section = SectionManager::fetch($section_id);
961
962
        $this->setPageType('form');
963
        $this->setTitle(__('%1$s &ndash; %2$s', array($section->get('name'), __('Symphony'))));
964
965
        // Ensure errored entries still maintain any prepopulated values [#2211]
966
        $this->Form->setAttribute('action', $this->Form->getAttribute('action') . $this->getPrepopulateString());
967
        $this->Form->setAttribute('enctype', 'multipart/form-data');
968
969
        $sidebar_fields = $section->fetchFields(null, 'sidebar');
970
        $main_fields = $section->fetchFields(null, 'main');
971
972 View Code Duplication
        if (!empty($sidebar_fields) && !empty($main_fields)) {
973
            $this->Form->setAttribute('class', 'two columns');
974
        } else {
975
            $this->Form->setAttribute('class', 'columns');
976
        }
977
978
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
979
        if (Symphony::Author()->isDeveloper()) {
980
            $this->appendSubheading(__('Untitled'),
981
                Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button')
982
            );
983
        } else {
984
            $this->appendSubheading(__('Untitled'));
985
        }
986
987
        // Build filtered breadcrumb [#1378}
988
        $this->insertBreadcrumbs(array(
989
            Widget::Anchor($section->get('name'), SYMPHONY_URL . '/publish/' . $this->_context['section_handle'] . '/' . $this->getFilterString()),
990
        ));
991
992
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
0 ignored issues
show
Bug introduced by
It seems like \Symphony::Configuration..._upload_size', 'admin') targeting Configuration::get() can also be of type array; however, Widget::Input() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

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

An additional type check may prevent trouble.

Loading history...
993
994
        // If there is post data floating around, due to errors, create an entry object
995
        if (isset($_POST['fields'])) {
996
            $entry = EntryManager::create();
997
            $entry->set('section_id', $section_id);
998
            $entry->setDataFromPost($_POST['fields'], $error, true);
999
1000
            // Brand new entry, so need to create some various objects
1001
        } else {
1002
            $entry = EntryManager::create();
1003
            $entry->set('section_id', $section_id);
1004
        }
1005
1006
        // Check if there is a field to prepopulate
1007
        if (isset($_REQUEST['prepopulate'])) {
1008
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1009
                $this->Form->prependChild(Widget::Input(
1010
                    "prepopulate[{$field_id}]",
1011
                    rawurlencode($value),
1012
                    'hidden'
1013
                ));
1014
1015
                // The actual pre-populating should only happen if there is not existing fields post data
1016
                if (!isset($_POST['fields']) && $field = FieldManager::fetch($field_id)) {
1017
                    $entry->setData(
1018
                        $field->get('id'),
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (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...
1019
                        $field->processRawFieldData($value, $error, $message, true)
0 ignored issues
show
Bug introduced by
The variable $error 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...
Bug introduced by
The variable $message seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The method processRawFieldData cannot be called on $field (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...
1020
                    );
1021
                }
1022
            }
1023
        }
1024
1025
        $primary = new XMLElement('fieldset');
1026
        $primary->setAttribute('class', 'primary column');
1027
1028
        if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1029
            $message = __('Fields must be added to this section before an entry can be created.');
1030
1031 View Code Duplication
            if (Symphony::Author()->isDeveloper()) {
1032
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">'
1033
                . __('Add fields')
1034
                . '</a>';
1035
            }
1036
1037
            $this->pageAlert($message, Alert::ERROR);
1038
        } else {
1039 View Code Duplication
            if (is_array($main_fields) && !empty($main_fields)) {
1040
                foreach ($main_fields as $field) {
1041
                    $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1042
                }
1043
1044
                $this->Form->appendChild($primary);
1045
            }
1046
1047 View Code Duplication
            if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1048
                $sidebar = new XMLElement('fieldset');
1049
                $sidebar->setAttribute('class', 'secondary column');
1050
1051
                foreach ($sidebar_fields as $field) {
1052
                    $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1053
                }
1054
1055
                $this->Form->appendChild($sidebar);
1056
            }
1057
1058
            $div = new XMLElement('div');
1059
            $div->setAttribute('class', 'actions');
1060
            $div->appendChild(Widget::Input('action[save]', __('Create Entry'), 'submit', array('accesskey' => 's')));
1061
1062
            $this->Form->appendChild($div);
1063
1064
            // Create a Drawer for Associated Sections
1065
            $this->prepareAssociationsDrawer($section);
0 ignored issues
show
Bug introduced by
It seems like $section defined by \SectionManager::fetch($section_id) on line 960 can also be of type array; however, contentPublish::prepareAssociationsDrawer() does only seem to accept object<Section>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1066
        }
1067
    }
1068
1069
    public function __actionNew()
0 ignored issues
show
Coding Style introduced by
__actionNew uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
__actionNew uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1070
    {
1071
        if (array_key_exists('save', $_POST['action']) || array_key_exists("done", $_POST['action'])) {
1072
            $section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle']);
1073
1074
            if (!$section = SectionManager::fetch($section_id)) {
1075
                Administration::instance()->throwCustomError(
1076
                    __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1077
                    __('Unknown Section'),
1078
                    Page::HTTP_STATUS_NOT_FOUND
1079
                );
1080
            }
1081
1082
            $entry = EntryManager::create();
1083
            $entry->set('author_id', Symphony::Author()->get('id'));
1084
            $entry->set('section_id', $section_id);
1085
            $entry->set('creation_date', DateTimeObj::get('c'));
1086
            $entry->set('modification_date', DateTimeObj::get('c'));
1087
1088
            $fields = $_POST['fields'];
1089
1090
            // Combine FILES and POST arrays, indexed by their custom field handles
1091
            if (isset($_FILES['fields'])) {
1092
                $filedata = General::processFilePostData($_FILES['fields']);
1093
1094
                foreach ($filedata as $handle => $data) {
1095
                    if (!isset($fields[$handle])) {
1096
                        $fields[$handle] = $data;
1097
                    } elseif (isset($data['error']) && $data['error'] == UPLOAD_ERR_NO_FILE) {
1098
                        $fields[$handle] = null;
1099
                    } else {
1100
                        foreach ($data as $ii => $d) {
1101
                            if (isset($d['error']) && $d['error'] == UPLOAD_ERR_NO_FILE) {
1102
                                $fields[$handle][$ii] = null;
1103
                            } elseif (is_array($d) && !empty($d)) {
1104
                                foreach ($d as $key => $val) {
1105
                                    $fields[$handle][$ii][$key] = $val;
1106
                                }
1107
                            }
1108
                        }
1109
                    }
1110
                }
1111
            }
1112
1113
            // Initial checks to see if the Entry is ok
1114
            if (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) {
1115
                $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1116
1117
                // Secondary checks, this will actually process the data and attempt to save
1118 View Code Duplication
            } elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) {
1119
                foreach ($errors as $field_id => $message) {
0 ignored issues
show
Bug introduced by
The expression $errors of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1120
                    $this->pageAlert($message, Alert::ERROR);
1121
                }
1122
1123
                // Everything is awesome. Dance.
1124
            } else {
1125
                /**
1126
                 * Just prior to creation of an Entry
1127
                 *
1128
                 * @delegate EntryPreCreate
1129
                 * @param string $context
1130
                 * '/publish/new/'
1131
                 * @param Section $section
1132
                 * @param Entry $entry
1133
                 * @param array $fields
1134
                 */
1135
                Symphony::ExtensionManager()->notifyMembers('EntryPreCreate', '/publish/new/', array('section' => $section, 'entry' => &$entry, 'fields' => &$fields));
1136
1137
                // Check to see if the dancing was premature
1138
                if (!$entry->commit()) {
1139
                    $this->pageAlert(null, Alert::ERROR);
1140
                } else {
1141
                    /**
1142
                     * Creation of an Entry. New Entry object is provided.
1143
                     *
1144
                     * @delegate EntryPostCreate
1145
                     * @param string $context
1146
                     * '/publish/new/'
1147
                     * @param Section $section
1148
                     * @param Entry $entry
1149
                     * @param array $fields
1150
                     */
1151
                    Symphony::ExtensionManager()->notifyMembers('EntryPostCreate', '/publish/new/', array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1152
1153
                    $prepopulate_querystring = $this->getPrepopulateString();
1154
                    redirect(sprintf(
1155
                        '%s/publish/%s/edit/%d/created/%s',
1156
                        SYMPHONY_URL,
1157
                        $this->_context['section_handle'],
1158
                        $entry->get('id'),
1159
                        (!empty($prepopulate_querystring) ? $prepopulate_querystring : null)
1160
                    ));
1161
                }
1162
            }
1163
        }
1164
    }
1165
1166
    public function __viewEdit()
0 ignored issues
show
Coding Style introduced by
__viewEdit uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1167
    {
1168 View Code Duplication
        if (!$section_id = SectionManager::fetchIDFromHandle($this->_context['section_handle'])) {
1169
            Administration::instance()->throwCustomError(
1170
                __('The Section, %s, could not be found.', array('<code>' . $this->_context['section_handle'] . '</code>')),
1171
                __('Unknown Section'),
1172
                Page::HTTP_STATUS_NOT_FOUND
1173
            );
1174
        }
1175
1176
        $section = SectionManager::fetch($section_id);
1177
        $entry_id = intval($this->_context['entry_id']);
1178
        $base = '/publish/'.$this->_context['section_handle'] . '/';
1179
        $new_link = $base . 'new/';
1180
        $filter_link = $base;
1181
1182
        EntryManager::setFetchSorting('id', 'DESC');
1183
1184 View Code Duplication
        if (!$existingEntry = EntryManager::fetch($entry_id)) {
1185
            Administration::instance()->throwCustomError(
1186
                __('Unknown Entry'),
1187
                __('The Entry, %s, could not be found.', array($entry_id)),
1188
                Page::HTTP_STATUS_NOT_FOUND
1189
            );
1190
        }
1191
        $existingEntry = $existingEntry[0];
1192
1193
        // If there is post data floating around, due to errors, create an entry object
1194
        if (isset($_POST['fields'])) {
1195
            $fields = $_POST['fields'];
1196
1197
            $entry = EntryManager::create();
1198
            $entry->set('id', $entry_id);
1199
            $entry->set('author_id', $existingEntry->get('author_id'));
1200
            $entry->set('section_id', $existingEntry->get('section_id'));
1201
            $entry->set('creation_date', $existingEntry->get('creation_date'));
1202
            $entry->set('modification_date', $existingEntry->get('modification_date'));
1203
            $entry->setDataFromPost($fields, $errors, true);
1204
1205
            // Editing an entry, so need to create some various objects
1206
        } else {
1207
            $entry = $existingEntry;
1208
            $fields = array();
1209
1210
            if (!$section) {
1211
                $section = SectionManager::fetch($entry->get('section_id'));
1212
            }
1213
        }
1214
1215
        /**
1216
         * Just prior to rendering of an Entry edit form.
1217
         *
1218
         * @delegate EntryPreRender
1219
         * @param string $context
1220
         * '/publish/edit/'
1221
         * @param Section $section
1222
         * @param Entry $entry
1223
         * @param array $fields
1224
         */
1225
        Symphony::ExtensionManager()->notifyMembers('EntryPreRender', '/publish/edit/', array(
1226
            'section' => $section,
1227
            'entry' => &$entry,
1228
            'fields' => $fields
1229
        ));
1230
1231
        // Iterate over the `prepopulate` parameters to build a URL
1232
        // to remember this state for Create New, View all Entries and
1233
        // Breadcrumb links. If `prepopulate` doesn't exist, this will
1234
        // just use the standard pages (ie. no filtering)
1235
        if (isset($_REQUEST['prepopulate'])) {
1236
            $new_link .= $this->getPrepopulateString();
1237
            $filter_link .= $this->getFilterString();
1238
        }
1239
1240
        if (isset($this->_context['flag'])) {
1241
            // These flags are only relevant if there are no errors
1242
            if (empty($this->_errors)) {
1243
                $time = Widget::Time();
1244
1245
                switch ($this->_context['flag']) {
1246
                    case 'saved':
1247
                        $message = __('Entry updated at %s.', array($time->generate()));
1248
                        break;
1249
                    case 'created':
1250
                        $message = __('Entry created at %s.', array($time->generate()));
1251
                }
1252
1253
                $this->pageAlert(
1254
                    $message
0 ignored issues
show
Bug introduced by
The variable $message 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...
1255
                    . ' <a href="' . SYMPHONY_URL . $new_link . '" accesskey="c">'
1256
                    . __('Create another?')
1257
                    . '</a> <a href="' . SYMPHONY_URL . $filter_link . '" accesskey="a">'
1258
                    . __('View all Entries')
1259
                    . '</a>',
1260
                    Alert::SUCCESS
1261
                );
1262
            }
1263
        }
1264
1265
        // Determine the page title
1266
        $field_id = Symphony::Database()->fetchVar('id', 0, sprintf("
1267
            SELECT `id`
1268
            FROM `tbl_fields`
1269
            WHERE `parent_section` = %d
1270
            ORDER BY `sortorder` LIMIT 1",
1271
            $section->get('id')
1272
        ));
1273
        if (!is_null($field_id)) {
1274
            $field = FieldManager::fetch($field_id);
1275
        }
1276
1277
        if ($field) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $field of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1278
            $title = $field->prepareReadableValue($existingEntry->getData($field->get('id')), $entry_id, true);
0 ignored issues
show
Bug introduced by
The variable $field 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...
Bug introduced by
The method get cannot be called on $field (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...
Bug introduced by
The method prepareReadableValue cannot be called on $field (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...
1279
        } else {
1280
            $title = '';
1281
        }
1282
1283
        if (trim($title) == '') {
1284
            $title = __('Untitled');
1285
        }
1286
1287
        // Check if there is a field to prepopulate
1288
        if (isset($_REQUEST['prepopulate'])) {
1289
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1290
                $this->Form->prependChild(Widget::Input(
1291
                    "prepopulate[{$field_id}]",
1292
                    rawurlencode($value),
1293
                    'hidden'
1294
                ));
1295
            }
1296
        }
1297
1298
        $this->setPageType('form');
1299
        $this->Form->setAttribute('enctype', 'multipart/form-data');
1300
        $this->setTitle(__('%1$s &ndash; %2$s &ndash; %3$s', array($title, $section->get('name'), __('Symphony'))));
1301
1302
        $sidebar_fields = $section->fetchFields(null, 'sidebar');
1303
        $main_fields = $section->fetchFields(null, 'main');
1304
1305 View Code Duplication
        if (!empty($sidebar_fields) && !empty($main_fields)) {
1306
            $this->Form->setAttribute('class', 'two columns');
1307
        } else {
1308
            $this->Form->setAttribute('class', 'columns');
1309
        }
1310
1311
        // Only show the Edit Section button if the Author is a developer. #938 ^BA
1312
        if (Symphony::Author()->isDeveloper()) {
1313
            $this->appendSubheading($title, Widget::Anchor(__('Edit Section'), SYMPHONY_URL . '/blueprints/sections/edit/' . $section_id . '/', __('Edit Section Configuration'), 'button'));
1314
        } else {
1315
            $this->appendSubheading($title);
1316
        }
1317
1318
        $this->insertBreadcrumbs(array(
1319
            Widget::Anchor($section->get('name'), SYMPHONY_URL . (isset($filter_link) ? $filter_link : $base)),
1320
        ));
1321
1322
        $this->Form->appendChild(Widget::Input('MAX_FILE_SIZE', Symphony::Configuration()->get('max_upload_size', 'admin'), 'hidden'));
0 ignored issues
show
Bug introduced by
It seems like \Symphony::Configuration..._upload_size', 'admin') targeting Configuration::get() can also be of type array; however, Widget::Input() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

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

An additional type check may prevent trouble.

Loading history...
1323
1324
        $primary = new XMLElement('fieldset');
1325
        $primary->setAttribute('class', 'primary column');
1326
1327
        if ((!is_array($main_fields) || empty($main_fields)) && (!is_array($sidebar_fields) || empty($sidebar_fields))) {
1328
            $message = __('Fields must be added to this section before an entry can be created.');
1329
1330 View Code Duplication
            if (Symphony::Author()->isDeveloper()) {
1331
                $message .= ' <a href="' . SYMPHONY_URL . '/blueprints/sections/edit/' . $section->get('id') . '/" accesskey="c">'
1332
                . __('Add fields')
1333
                . '</a>';
1334
            }
1335
1336
            $this->pageAlert($message, Alert::ERROR);
1337
        } else {
1338 View Code Duplication
            if (is_array($main_fields) && !empty($main_fields)) {
1339
                foreach ($main_fields as $field) {
1340
                    $primary->appendChild($this->__wrapFieldWithDiv($field, $entry));
1341
                }
1342
1343
                $this->Form->appendChild($primary);
1344
            }
1345
1346 View Code Duplication
            if (is_array($sidebar_fields) && !empty($sidebar_fields)) {
1347
                $sidebar = new XMLElement('fieldset');
1348
                $sidebar->setAttribute('class', 'secondary column');
1349
1350
                foreach ($sidebar_fields as $field) {
1351
                    $sidebar->appendChild($this->__wrapFieldWithDiv($field, $entry));
1352
                }
1353
1354
                $this->Form->appendChild($sidebar);
1355
            }
1356
1357
            $div = new XMLElement('div');
1358
            $div->setAttribute('class', 'actions');
1359
            $div->appendChild(Widget::Input('action[save]', __('Save Changes'), 'submit', array('accesskey' => 's')));
1360
1361
            $button = new XMLElement('button', __('Delete'));
1362
            $button->setAttributeArray(array('name' => 'action[delete]', 'class' => 'button confirm delete', 'title' => __('Delete this entry'), 'type' => 'submit', 'accesskey' => 'd', 'data-message' => __('Are you sure you want to delete this entry?')));
1363
            $div->appendChild($button);
1364
1365
            $this->Form->appendChild($div);
1366
1367
            // Create a Drawer for Associated Sections
1368
            $this->prepareAssociationsDrawer($section);
0 ignored issues
show
Bug introduced by
It seems like $section can also be of type array; however, contentPublish::prepareAssociationsDrawer() does only seem to accept object<Section>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1369
        }
1370
    }
1371
1372
    public function __actionEdit()
0 ignored issues
show
Coding Style introduced by
__actionEdit uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1373
    {
1374
        $entry_id = intval($this->_context['entry_id']);
1375
1376
        if (@array_key_exists('save', $_POST['action']) || @array_key_exists("done", $_POST['action'])) {
1377 View Code Duplication
            if (!$ret = EntryManager::fetch($entry_id)) {
1378
                Administration::instance()->throwCustomError(
1379
                    __('The Entry, %s, could not be found.', array($entry_id)),
1380
                    __('Unknown Entry'),
1381
                    Page::HTTP_STATUS_NOT_FOUND
1382
                );
1383
            }
1384
1385
            $entry = $ret[0];
1386
1387
            $section = SectionManager::fetch($entry->get('section_id'));
1388
1389
            $post = General::getPostData();
1390
            $fields = $post['fields'];
1391
1392
            // Initial checks to see if the Entry is ok
1393
            if (Entry::__ENTRY_FIELD_ERROR__ == $entry->checkPostData($fields, $this->_errors)) {
1394
                $this->pageAlert(__('Some errors were encountered while attempting to save.'), Alert::ERROR);
1395
1396
                // Secondary checks, this will actually process the data and attempt to save
1397 View Code Duplication
            } elseif (Entry::__ENTRY_OK__ != $entry->setDataFromPost($fields, $errors)) {
0 ignored issues
show
Bug introduced by
The variable $errors does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
1398
                foreach ($errors as $field_id => $message) {
1399
                    $this->pageAlert($message, Alert::ERROR);
1400
                }
1401
1402
                // Everything is awesome. Dance.
1403
            } else {
1404
                /**
1405
                 * Just prior to editing of an Entry.
1406
                 *
1407
                 * @delegate EntryPreEdit
1408
                 * @param string $context
1409
                 * '/publish/edit/'
1410
                 * @param Section $section
1411
                 * @param Entry $entry
1412
                 * @param array $fields
1413
                 */
1414
                Symphony::ExtensionManager()->notifyMembers('EntryPreEdit', '/publish/edit/', array('section' => $section, 'entry' => &$entry, 'fields' => $fields));
1415
1416
                // Check to see if the dancing was premature
1417
                if (!$entry->commit()) {
1418
                    $this->pageAlert(null, Alert::ERROR);
1419
                } else {
1420
                    /**
1421
                     * Just after the editing of an Entry
1422
                     *
1423
                     * @delegate EntryPostEdit
1424
                     * @param string $context
1425
                     * '/publish/edit/'
1426
                     * @param Section $section
1427
                     * @param Entry $entry
1428
                     * @param array $fields
1429
                     */
1430
                    Symphony::ExtensionManager()->notifyMembers('EntryPostEdit', '/publish/edit/', array('section' => $section, 'entry' => $entry, 'fields' => $fields));
1431
1432
                    redirect(sprintf(
1433
                        '%s/publish/%s/edit/%d/saved/%s',
1434
                        SYMPHONY_URL,
1435
                        $this->_context['section_handle'],
1436
                        $entry->get('id'),
1437
                        $this->getPrepopulateString()
1438
                    ));
1439
                }
1440
            }
1441
        } elseif (@array_key_exists('delete', $_POST['action']) && is_numeric($entry_id)) {
1442
            /**
1443
             * Prior to deletion of entries. An array of Entry ID's is provided which
1444
             * can be manipulated. This delegate was renamed from `Delete` to `EntryPreDelete`
1445
             * in Symphony 2.3.
1446
             *
1447
             * @delegate EntryPreDelete
1448
             * @param string $context
1449
             * '/publish/'
1450
             * @param array $entry_id
1451
             *    An array of Entry ID's passed by reference
1452
             */
1453
            $checked = array($entry_id);
1454
            Symphony::ExtensionManager()->notifyMembers('EntryPreDelete', '/publish/', array('entry_id' => &$checked));
1455
1456
            EntryManager::delete($checked);
1457
1458
            /**
1459
             * After the deletion of entries, this delegate provides an array of Entry ID's
1460
             * that were deleted.
1461
             *
1462
             * @since Symphony 2.3
1463
             * @delegate EntryPostDelete
1464
             * @param string $context
1465
             * '/publish/'
1466
             * @param array $entry_id
1467
             *  An array of Entry ID's that were deleted.
1468
             */
1469
            Symphony::ExtensionManager()->notifyMembers('EntryPostDelete', '/publish/', array('entry_id' => $checked));
1470
1471
            redirect(SYMPHONY_URL . '/publish/'.$this->_context['section_handle'].'/');
1472
        }
1473
    }
1474
1475
    /**
1476
     * Given a Field and Entry object, this function will wrap
1477
     * the Field's displayPublishPanel result with a div that
1478
     * contains some contextual information such as the Field ID,
1479
     * the Field handle and whether it is required or not.
1480
     *
1481
     * @param Field $field
1482
     * @param Entry $entry
1483
     * @return XMLElement
1484
     */
1485
    private function __wrapFieldWithDiv(Field $field, Entry $entry)
1486
    {
1487
        $is_hidden = $this->isFieldHidden($field);
1488
        $div = new XMLElement('div', null, array('id' => 'field-' . $field->get('id'), 'class' => 'field field-'.$field->handle().($field->get('required') == 'yes' ? ' required' : '').($is_hidden === true ? ' irrelevant' : '')));
1489
1490
        $field->setAssociationContext($div);
1491
1492
        $field->displayPublishPanel(
1493
            $div, $entry->getData($field->get('id')),
1494
            (isset($this->_errors[$field->get('id')]) ? $this->_errors[$field->get('id')] : null),
1495
            null, null, (is_numeric($entry->get('id')) ? $entry->get('id') : null)
1496
        );
1497
1498
        /**
1499
         * Allows developers modify the field before it is rendered in the publish
1500
         * form. Passes the `Field` object, `Entry` object, the `XMLElement` div and
1501
         * any errors for the entire `Entry`. Only the `$div` element
1502
         * will be altered before appending to the page, the rest are read only.
1503
         *
1504
         * @since Symphony 2.5.0
1505
         * @delegate ModifyFieldPublishWidget
1506
         * @param string $context
1507
         * '/backend/'
1508
         * @param Field $field
1509
         * @param Entry $entry
1510
         * @param array $errors
1511
         * @param Widget $widget
1512
         */
1513
        Symphony::ExtensionManager()->notifyMembers('ModifyFieldPublishWidget', '/backend/', array(
1514
            'field' => $field,
1515
            'entry' => $entry,
1516
            'errors' => $this->_errors,
1517
            'widget' => &$div
1518
        ));
1519
1520
        return $div;
1521
    }
1522
1523
    /**
1524
     * Check whether the given `$field` will be hidden because it's been
1525
     * prepopulated.
1526
     *
1527
     * @param  Field  $field
1528
     * @return boolean
1529
     */
1530
    public function isFieldHidden(Field $field)
1531
    {
1532
        if ($field->get('hide_when_prepopulated') == 'yes') {
1533
            if (isset($_REQUEST['prepopulate'])) {
1534
                foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1535
                    if ($field_id == $field->get('id')) {
1536
                        return true;
1537
                    }
1538
                }
1539
            }
1540
        }
1541
1542
        return false;
1543
    }
1544
1545
    /**
1546
     * Prepare a Drawer to visualize section associations
1547
     *
1548
     * @param  Section $section The current Section object
1549
     * @throws InvalidArgumentException
1550
     * @throws Exception
1551
     */
1552
    private function prepareAssociationsDrawer($section)
1553
    {
1554
        $entry_id = (!is_null($this->_context['entry_id'])) ? $this->_context['entry_id'] : null;
1555
        $show_entries = Symphony::Configuration()->get('association_maximum_rows', 'symphony');
1556
1557
        if (is_null($entry_id) && !isset($_GET['prepopulate']) || is_null($show_entries) || $show_entries == 0) {
1558
            return;
1559
        }
1560
1561
        $parent_associations = SectionManager::fetchParentAssociations($section->get('id'), true);
0 ignored issues
show
Documentation introduced by
$section->get('id') is of type array|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...
1562
        $child_associations = SectionManager::fetchChildAssociations($section->get('id'), true);
0 ignored issues
show
Documentation introduced by
$section->get('id') is of type array|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...
1563
        $content = null;
1564
        $drawer_position = 'vertical-right';
1565
1566
        /**
1567
         * Prepare Associations Drawer from an Extension
1568
         *
1569
         * @since Symphony 2.3.3
1570
         * @delegate PrepareAssociationsDrawer
1571
         * @param string $context
1572
         * '/publish/'
1573
         * @param integer $entry_id
1574
         *  The entry ID or null
1575
         * @param array $parent_associations
1576
         *  Array of Sections
1577
         * @param array $child_associations
1578
         *  Array of Sections
1579
         * @param string $drawer_position
1580
         *  The position of the Drawer, defaults to `vertical-right`. Available
1581
         *  values of `vertical-left, `vertical-right` and `horizontal`
1582
         */
1583
        Symphony::ExtensionManager()->notifyMembers('PrepareAssociationsDrawer', '/publish/', array(
1584
            'entry_id' => $entry_id,
1585
            'parent_associations' => &$parent_associations,
1586
            'child_associations' => &$child_associations,
1587
            'content' => &$content,
1588
            'drawer-position' => &$drawer_position
1589
        ));
1590
1591
        // If there are no associations, return now.
1592
        if (
1593
            (is_null($parent_associations) || empty($parent_associations))
1594
            &&
1595
            (is_null($child_associations) || empty($child_associations))
1596
        ) {
1597
            return;
1598
        }
1599
1600
        if (!($content instanceof XMLElement)) {
1601
            $content = new XMLElement('div', null, array('class' => 'content'));
1602
            $content->setSelfClosingTag(false);
1603
1604
            // Process Parent Associations
1605
            if (!is_null($parent_associations) && !empty($parent_associations)) {
1606
                foreach ($parent_associations as $as) {
1607
                    if ($field = FieldManager::fetch($as['parent_section_field_id'])) {
1608
                        if (isset($_GET['prepopulate'])) {
1609
                            $prepopulate_field = key($_GET['prepopulate']);
1610
                        }
1611
1612
                        // get associated entries if entry exists,
1613
                        if ($entry_id) {
1614
                            $entry_ids = $field->findParentRelatedEntries($as['child_section_field_id'], $entry_id);
0 ignored issues
show
Bug introduced by
The method findParentRelatedEntries cannot be called on $field (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...
1615
1616
                            // get prepopulated entry otherwise
1617
                        } elseif (isset($_GET['prepopulate'])) {
1618
                            $entry_ids = array(intval(current($_GET['prepopulate'])));
1619
                        } else {
1620
                            $entry_ids = array();
1621
                        }
1622
1623
                        // Use $schema for perf reasons
1624
                        $schema = array($field->get('element_name'));
0 ignored issues
show
Bug introduced by
The method get cannot be called on $field (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...
1625
                        $where = (!empty($entry_ids)) ? sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids)) : null;
1626
                        $entries = (!empty($entry_ids) || isset($_GET['prepopulate']) && $field->get('id') === $prepopulate_field)
0 ignored issues
show
Bug introduced by
The variable $prepopulate_field 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...
Bug introduced by
The method get cannot be called on $field (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...
1627
                            ? EntryManager::fetchByPage(1, $as['parent_section_id'], $show_entries, $where, null, false, false, true, $schema)
0 ignored issues
show
Documentation introduced by
$show_entries is of type array|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...
1628
                            : array();
1629
                        $has_entries = !empty($entries) && $entries['total-entries'] != 0;
1630
1631
                        if ($has_entries) {
1632
                            $element = new XMLElement('section', null, array('class' => 'association parent'));
1633
                            $header = new XMLElement('header');
1634
                            $header->appendChild(new XMLElement('p', __('Linked to %s in', array('<a class="association-section" href="' . SYMPHONY_URL . '/publish/' . $as['handle'] . '/">' . $as['name'] . '</a>'))));
1635
                            $element->appendChild($header);
1636
1637
                            $ul = new XMLElement('ul', null, array(
1638
                                'class' => 'association-links',
1639
                                'data-section-id' => $as['child_section_id'],
1640
                                'data-association-ids' => implode(', ', $entry_ids)
1641
                            ));
1642
1643
                            foreach ($entries['records'] as $e) {
1644
                                // let the field create the mark up
1645
                                $li = $field->prepareAssociationsDrawerXMLElement($e, $as);
0 ignored issues
show
Bug introduced by
The method prepareAssociationsDrawerXMLElement cannot be called on $field (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...
1646
                                // add it to the unordered list
1647
                                $ul->appendChild($li);
1648
                            }
1649
1650
                            $element->appendChild($ul);
1651
                            $content->appendChild($element);
1652
                        }
1653
                    }
1654
                }
1655
            }
1656
1657
            // Process Child Associations
1658
            if (!is_null($child_associations) && !empty($child_associations)) {
1659
                foreach ($child_associations as $as) {
1660
                    // Get the related section
1661
                    $child_section = SectionManager::fetch($as['child_section_id']);
1662
1663
                    if (!($child_section instanceof Section)) {
1664
                        continue;
1665
                    }
1666
1667
                    // Get the visible field instance (using the sorting field, this is more flexible than visibleColumns())
1668
                    // Get the link field instance
1669
                    $visible_field   = current($child_section->fetchVisibleColumns());
1670
                    $relation_field  = FieldManager::fetch($as['child_section_field_id']);
1671
1672
                    // Get entries, using $schema for performance reasons.
1673
                    $entry_ids = $relation_field->findRelatedEntries($entry_id, $as['parent_section_field_id']);
0 ignored issues
show
Bug introduced by
The method findRelatedEntries cannot be called on $relation_field (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...
1674
                    $schema = $visible_field ? array($visible_field->get('element_name')) : array();
1675
                    $where = sprintf(' AND `e`.`id` IN (%s)', implode(', ', $entry_ids));
1676
1677
                    $entries = (!empty($entry_ids)) ? EntryManager::fetchByPage(1, $as['child_section_id'], $show_entries, $where, null, false, false, true, $schema) : array();
0 ignored issues
show
Documentation introduced by
$show_entries is of type array|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...
1678
                    $has_entries = !empty($entries) && $entries['total-entries'] != 0;
1679
1680
                    // Build the HTML of the relationship
1681
                    $element = new XMLElement('section', null, array('class' => 'association child'));
1682
                    $header = new XMLElement('header');
1683
                    $filter = '?filter[' . $relation_field->get('element_name') . ']=' . $entry_id;
0 ignored issues
show
Bug introduced by
The method get cannot be called on $relation_field (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...
1684
                    $prepopulate = '?prepopulate[' . $as['child_section_field_id'] . ']=' . $entry_id;
1685
1686
                    // Create link with filter or prepopulate
1687
                    $link = SYMPHONY_URL . '/publish/' . $as['handle'] . '/' . $filter;
1688
                    $a = new XMLElement('a', $as['name'], array(
1689
                        'class' => 'association-section',
1690
                        'href' => $link
1691
                    ));
1692
1693
                    // Create new entries
1694
                    $create = new XMLElement('a', __('Create New'), array(
1695
                        'class' => 'button association-new',
1696
                        'href' => SYMPHONY_URL . '/publish/' . $as['handle'] . '/new/' . $prepopulate
1697
                    ));
1698
1699
                    // Display existing entries
1700
                    if ($has_entries) {
1701
                        $header->appendChild(new XMLElement('p', __('Links in %s', array($a->generate()))));
1702
1703
                        $ul = new XMLElement('ul', null, array(
1704
                            'class' => 'association-links',
1705
                            'data-section-id' => $as['child_section_id'],
1706
                            'data-association-ids' => implode(', ', $entry_ids)
1707
                        ));
1708
1709
                        foreach ($entries['records'] as $key => $e) {
1710
                            // let the first visible field create the mark up
1711
                            if ($visible_field) {
1712
                                $li = $visible_field->prepareAssociationsDrawerXMLElement($e, $as, $prepopulate);
1713
                            }
1714
                            // or use the system:id if no visible field exists.
1715
                            else {
1716
                                $li = Field::createAssociationsDrawerXMLElement($e->get('id'), $e, $as, $prepopulate);
1717
                            }
1718
1719
                            // add it to the unordered list
1720
                            $ul->appendChild($li);
1721
                        }
1722
1723
                        $element->appendChild($ul);
1724
1725
                        // If we are only showing 'some' of the entries, then show this on the UI
1726
                        if ($entries['total-entries'] > $show_entries) {
1727
                            $pagination = new XMLElement('li', null, array(
1728
                                'class' => 'association-more',
1729
                                'data-current-page' => '1',
1730
                                'data-total-pages' => ceil($entries['total-entries'] / $show_entries),
1731
                                'data-total-entries' => $entries['total-entries']
1732
                            ));
1733
                            $counts = new XMLElement('a', __('Show more entries'), array(
1734
                                'href' => $link
1735
                            ));
1736
1737
                            $pagination->appendChild($counts);
1738
                            $ul->appendChild($pagination);
1739
                        }
1740
1741
                        // No entries
1742
                    } else {
1743
                        $element->setAttribute('class', 'association child empty');
1744
                        $header->appendChild(new XMLElement('p', __('No links in %s', array($a->generate()))));
1745
                    }
1746
1747
                    $header->appendChild($create);
1748
                    $element->prependChild($header);
1749
                    $content->appendChild($element);
1750
                }
1751
            }
1752
        }
1753
1754
        $drawer = Widget::Drawer('section-associations', __('Show Associations'), $content);
1755
        $this->insertDrawer($drawer, $drawer_position, 'prepend');
1756
    }
1757
1758
    /**
1759
     * If this entry is being prepopulated, this function will return the prepopulated
1760
     * fields and values as a query string.
1761
     *
1762
     * @since Symphony 2.5.2
1763
     * @return string
1764
     */
1765 View Code Duplication
    public function getPrepopulateString()
1766
    {
1767
        $prepopulate_querystring = '';
1768
1769
        if (isset($_REQUEST['prepopulate'])) {
1770
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1771
                $prepopulate_querystring .= sprintf("prepopulate[%s]=%s&", $field_id, rawurldecode($value));
1772
            }
1773
            $prepopulate_querystring = trim($prepopulate_querystring, '&');
1774
        }
1775
1776
        // This is to prevent the value being interpreted as an additional GET
1777
        // parameter. eg. prepopulate[cat]=Minx&June, would come through as:
1778
        // $_GET['cat'] = Minx
1779
        // $_GET['June'] = ''
1780
        $prepopulate_querystring = preg_replace("/&amp;$/", '', $prepopulate_querystring);
1781
1782
        return $prepopulate_querystring ? '?' . $prepopulate_querystring : null;
1783
    }
1784
1785
    /**
1786
     * If the entry is being prepopulated, we may want to filter other views by this entry's
1787
     * value. This function will create that filter query string.
1788
     *
1789
     * @since Symphony 2.5.2
1790
     * @return string
1791
     */
1792 View Code Duplication
    public function getFilterString()
1793
    {
1794
        $filter_querystring = '';
1795
1796
        if (isset($_REQUEST['prepopulate'])) {
1797
            foreach ($_REQUEST['prepopulate'] as $field_id => $value) {
1798
                $handle = FieldManager::fetchHandleFromID($field_id);
1799
                $filter_querystring .= sprintf("filter[%s]=%s&", $handle, rawurldecode($value));
1800
            }
1801
            $filter_querystring = trim($filter_querystring, '&');
1802
        }
1803
1804
        // This is to prevent the value being interpreted as an additional GET
1805
        // parameter. eg. filter[cat]=Minx&June, would come through as:
1806
        // $_GET['cat'] = Minx
1807
        // $_GET['June'] = ''
1808
        $filter_querystring = preg_replace("/&amp;$/", '', $filter_querystring);
1809
1810
        return $filter_querystring ? '?' . $filter_querystring : null;
1811
    }
1812
}
1813