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.

EntryManager::saveFieldData()   F
last analyzed

Complexity

Conditions 12
Paths 379

Size

Total Lines 66
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
cc 12
eloc 33
c 4
b 0
f 1
nc 379
nop 3
dl 0
loc 66
rs 3.8958

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @package toolkit
5
 */
6
/**
7
 * The `EntryManager` is responsible for all `Entry` objects in Symphony.
8
 * Entries are stored in the database in a cluster of tables. There is a
9
 * parent entry row stored in `tbl_entries` and then each field's data is
10
 * stored in a separate table, `tbl_entries_data_{field_id}`. Where Field ID
11
 * is generated when the Section is saved. This Manager provides basic
12
 * add, edit, delete and fetching methods for Entries.
13
 */
14
15
class EntryManager
16
{
17
    /**
18
     * The Field ID that will be used to sort when fetching Entries, defaults
19
     * to null, which implies the Entry ID (id column in `tbl_entries`).
20
     * To order by core fields, use one of
21
     * 'system:creation-date', 'system:modification-date', 'system:id'.
22
     * @var integer|string
23
     */
24
    protected static $_fetchSortField = null;
25
26
    /**
27
     * The direction that entries should be sorted in, available options are
28
     * RAND, ASC or DESC. Defaults to null, which implies ASC
29
     * @var string
30
     */
31
    protected static $_fetchSortDirection = null;
32
33
    /**
34
     * Setter function for the default sorting direction of the Fetch
35
     * function. Available options are RAND, ASC or DESC.
36
     *
37
     * @param string $direction
38
     *  The direction that entries should be sorted in, available options
39
     *  are RAND, ASC or DESC.
40
     */
41
    public static function setFetchSortingDirection($direction)
42
    {
43
        $direction = strtoupper($direction);
44
45
        if ($direction == 'RANDOM') {
46
            $direction = 'RAND';
47
        }
48
49
        self::$_fetchSortDirection = (in_array($direction, array('RAND', 'ASC', 'DESC')) ? $direction : null);
50
    }
51
52
    /**
53
     * Sets the field to applying the sorting direction on when fetching
54
     * entries
55
     *
56
     * @param integer $field_id
57
     *  The ID of the Field that should be sorted on
58
     */
59
    public static function setFetchSortingField($field_id)
60
    {
61
        self::$_fetchSortField = $field_id;
62
    }
63
64
    /**
65
     * Convenience function that will set sorting field and direction
66
     * by calling `setFetchSortingField()` & `setFetchSortingDirection()`
67
     *
68
     * @see toolkit.EntryManager#setFetchSortingField()
69
     * @see toolkit.EntryManager#setFetchSortingDirection()
70
     * @param integer $field_id
71
     *  The ID of the Field that should be sorted on
72
     * @param string $direction
73
     *  The direction that entries should be sorted in, available options
74
     *  are RAND, ASC or DESC. Defaults to ASC
75
     */
76
    public static function setFetchSorting($field_id, $direction = 'ASC')
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$direction" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$direction"; expected 0 but found 1
Loading history...
77
    {
78
        self::setFetchSortingField($field_id);
79
        self::setFetchSortingDirection($direction);
80
    }
81
82
    /**
83
     * Returns an object representation of the sorting for the
84
     * EntryManager, with the field and direction provided
85
     *
86
     * @return StdClass
87
     */
88
    public static function getFetchSorting()
89
    {
90
        return (object)array(
91
            'field' => self::$_fetchSortField,
92
            'direction' => self::$_fetchSortDirection
93
        );
94
    }
95
96
    /**
97
     * Executes the SQL queries need to save a field's data for the specified
98
     * entry id.
99
     *
100
     * It first locks the table for writes, it then deletes existing data and then
101
     * it inserts a new row for the data. Errors are discarded and the lock is
102
     * released, if it was acquired.
103
     *
104
     * @param int $entry_id
105
     *  The entry id to save the data for
106
     * @param int $field_id
107
     *  The field id to save the data for
108
     * @param array $field
109
     *  The field data to save
110
     */
111
    protected static function saveFieldData($entry_id, $field_id, $field)
112
    {
113
        // Check that we have a field id
114
        if (empty($field_id)) {
115
            return;
116
        }
117
118
        // Ignore parameter when not an array
119
        if (!is_array($field)) {
0 ignored issues
show
introduced by
The condition is_array($field) is always true.
Loading history...
120
            $field = array();
121
        }
122
123
        $did_lock = false;
124
        $exception = null;
125
        try {
0 ignored issues
show
Coding Style introduced by
Blank line found at start of control structure
Loading history...
126
127
            // Check if table exists
128
            $table_name = 'tbl_entries_data_' . General::intval($field_id);
129
            if (!Symphony::Database()->tableExists($table_name)) {
130
                return;
131
            }
132
133
            // Lock the table for write
134
            $did_lock = Symphony::Database()->query("LOCK TABLES `$table_name` WRITE");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $table_name instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
135
136
            // Delete old data
137
            Symphony::Database()->delete($table_name, sprintf("
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \n `entry_id` = %d does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
138
                `entry_id` = %d", $entry_id
139
            ));
140
141
            // Insert new data
142
            $data = array(
143
                'entry_id' => $entry_id
144
            );
145
146
            $fields = array();
147
148
            foreach ($field as $key => $value) {
149
                if (is_array($value)) {
150
                    foreach ($value as $ii => $v) {
151
                        $fields[$ii][$key] = $v;
152
                    }
153
                } else {
154
                    $fields[max(0, count($fields) - 1)][$key] = $value;
155
                }
156
            }
157
158
            foreach ($fields as $index => $field_data) {
159
                $fields[$index] = array_merge($data, $field_data);
160
            }
161
162
            // Insert only if we have field data
163
            if (!empty($fields)) {
164
                Symphony::Database()->insert($fields, $table_name);
165
            }
166
        } catch (Exception $ex) {
167
            $exception = $ex;
168
            Symphony::Log()->pushExceptionToLog($ex, true);
169
        }
170
171
        if ($did_lock) {
172
            Symphony::Database()->query('UNLOCK TABLES');
173
        }
174
175
        if ($exception) {
176
            throw $exception;
177
        }
178
    }
179
180
    /**
181
     * Given an Entry object, iterate over all of the fields in that object
182
     * an insert them into their relevant entry tables.
183
     *
184
     * @see EntryManager::saveFieldData()
185
     * @param Entry $entry
186
     *  An Entry object to insert into the database
187
     * @throws DatabaseException
188
     * @return boolean
189
     */
190
    public static function add(Entry $entry)
191
    {
192
        $fields = $entry->get();
193
        Symphony::Database()->insert($fields, 'tbl_entries');
194
195
        if (!$entry_id = Symphony::Database()->getInsertID()) {
196
            return false;
197
        }
198
199
        // Iterate over all data for this entry
200
        foreach ($entry->getData() as $field_id => $field) {
201
            // Write data
202
            static::saveFieldData($entry_id, $field_id, $field);
203
        }
204
205
        $entry->set('id', $entry_id);
206
207
        return true;
208
    }
209
210
    /**
211
     * Update an existing Entry object given an Entry object
212
     *
213
     * @see EntryManager::saveFieldData()
214
     * @param Entry $entry
215
     *  An Entry object
216
     * @throws DatabaseException
217
     * @return boolean
218
     */
219
    public static function edit(Entry $entry)
220
    {
221
        // Update modification date and modification author.
222
        Symphony::Database()->update(
223
            array(
224
                'modification_author_id' => $entry->get('modification_author_id'),
225
                'modification_date' => $entry->get('modification_date'),
226
                'modification_date_gmt' => $entry->get('modification_date_gmt')
227
            ),
228
            'tbl_entries',
229
            sprintf(' `id` = %d', (int)$entry->get('id'))
230
        );
231
232
        // Iterate over all data for this entry
233
        foreach ($entry->getData() as $field_id => $field) {
234
            // Write data
235
            static::saveFieldData($entry->get('id'), $field_id, $field);
0 ignored issues
show
Bug introduced by
It seems like $entry->get('id') can also be of type array; however, parameter $entry_id of EntryManager::saveFieldData() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

235
            static::saveFieldData(/** @scrutinizer ignore-type */ $entry->get('id'), $field_id, $field);
Loading history...
236
        }
237
238
        return true;
239
    }
240
241
    /**
242
     * Given an Entry ID, or an array of Entry ID's, delete all
243
     * data associated with this Entry using a Field's `entryDataCleanup()`
244
     * function, and then remove this Entry from `tbl_entries`. If the `$entries`
245
     * all belong to the same section, passing `$section_id` will improve
246
     * performance
247
     *
248
     * @param array|integer $entries
249
     *  An entry_id, or an array of entry id's to delete
250
     * @param integer $section_id (optional)
251
     *  If possible, the `$section_id` of the the `$entries`. This parameter
252
     *  should be left as null if the `$entries` array contains entry_id's for
253
     *  multiple sections.
254
     * @throws DatabaseException
255
     * @throws Exception
256
     * @return boolean
257
     */
258
    public static function delete($entries, $section_id = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$section_id" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$section_id"; expected 0 but found 1
Loading history...
259
    {
260
        $needs_data = true;
261
262
        if (!is_array($entries)) {
263
            $entries = array($entries);
264
        }
265
266
        // Get the section's schema
267
        if (!is_null($section_id)) {
268
            $section = SectionManager::fetch($section_id);
269
270
            if ($section instanceof Section) {
271
                $fields = $section->fetchFields();
272
                $data = array();
273
274
                foreach ($fields as $field) {
275
                    $reflection = new ReflectionClass($field);
276
                    // This field overrides the default implementation, so pass it data.
277
                    $data[$field->get('element_name')] = $reflection->getMethod('entryDataCleanup')->class == 'Field' ? false : true;
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
278
                }
279
280
                $data = array_filter($data);
281
282
                if (empty($data)) {
283
                    $needs_data = false;
284
                }
285
            }
286
        }
287
288
        // We'll split $entries into blocks of 2500 (random number)
289
        // and process the deletion in chunks.
290
        $chunks = array_chunk($entries, 2500);
291
292
        foreach ($chunks as $chunk) {
293
            $entry_list = implode("', '", $chunk);
294
295
            // If we weren't given a `section_id` we'll have to process individually
296
            // If we don't need data for any field, we can process the whole chunk
297
            // without building Entry objects, otherwise we'll need to build
298
            // Entry objects with data
299
            if (is_null($section_id) || !$needs_data) {
300
                $entries = $chunk;
301
            } elseif ($needs_data) {
302
                $entries = self::fetch($chunk, $section_id);
303
            }
304
305
            if ($needs_data) {
306
                foreach ($entries as $id) {
307
                    // Handles the case where `section_id` was not provided
308
                    if (is_null($section_id)) {
309
                        $e = self::fetch($id);
310
311
                        if (!is_array($e)) {
312
                            continue;
313
                        }
314
315
                        $e = current($e);
316
317
                        if (!$e instanceof Entry) {
318
                            continue;
319
                        }
320
321
                        // If we needed data, whole Entry objects will exist
322
                    } elseif ($needs_data) {
323
                        $e = $id;
324
                        $id = $e->get('id');
325
                    }
326
327
                    // Time to loop over it and send it to the fields.
328
                    // Note we can't rely on the `$fields` array as we may
329
                    // also be dealing with the case where `section_id` hasn't
330
                    // been provided
331
                    $entry_data = $e->getData();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $e does not seem to be defined for all execution paths leading up to this point.
Loading history...
332
333
                    foreach ($entry_data as $field_id => $data) {
334
                        $field = FieldManager::fetch($field_id);
335
                        $field->entryDataCleanup($id, $data);
336
                    }
337
                }
338
            } else {
339
                foreach ($fields as $field) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $fields does not seem to be defined for all execution paths leading up to this point.
Loading history...
340
                    $field->entryDataCleanup($chunk);
341
                }
342
            }
343
344
            Symphony::Database()->delete('tbl_entries', " `id` IN ('$entry_list') ");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $entry_list instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
345
        }
346
347
        return true;
348
    }
349
350
    /**
351
     * This function will return an array of Entry objects given an ID or an array of ID's.
352
     * Do not provide `$entry_id` as an array if not specifying the `$section_id`. This function
353
     * is commonly passed custom SQL statements through the `$where` and `$join` parameters
354
     * that is generated by the fields of this section.
355
     *
356
     * @since Symphony 2.7.0 it will also call a new method on fields,
357
     * `buildSortingSelectSQL()`, to make sure fields can add ordering columns in
358
     * the SELECT clause. This is required on MySQL 5.7+ strict mode.
359
     *
360
     * @param integer|array $entry_id
361
     *  An array of Entry ID's or an Entry ID to return
362
     * @param integer $section_id
363
     *  The ID of the Section that these entries are contained in
364
     * @param integer $limit
365
     *  The limit of entries to return
366
     * @param integer $start
367
     *  The starting offset of the entries to return
368
     * @param string $where
369
     *  Any custom WHERE clauses. The tbl_entries alias is `e`
370
     * @param string $joins
371
     *  Any custom JOIN's
372
     * @param boolean $group
373
     *  Whether the entries need to be grouped by Entry ID or not
374
     * @param boolean $buildentries
375
     *  Whether to return an array of entry ID's or Entry objects. Defaults to
376
     *  true, which will return Entry objects
377
     * @param array $element_names
378
     *  Choose whether to get data from a subset of fields or all fields in a section,
379
     *  by providing an array of field names. Defaults to null, which will load data
380
     *  from all fields in a section.
381
     * @param boolean $enable_sort
382
     *  Defaults to true, if false this function will not apply any sorting
383
     * @throws Exception
384
     * @return array
385
     *  If `$buildentries` is true, this function will return an array of Entry objects,
386
     *  otherwise it will return an associative array of Entry data from `tbl_entries`
387
     */
388
    public static function fetch($entry_id = null, $section_id = null, $limit = null, $start = null, $where = null, $joins = null, $group = false, $buildentries = true, $element_names = null, $enable_sort = true)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$entry_id" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$entry_id"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$section_id" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$section_id"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$limit" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$limit"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$start" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$start"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$where" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$where"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$joins" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$joins"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$group" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$group"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$buildentries" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$buildentries"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$element_names" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$element_names"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$enable_sort" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$enable_sort"; expected 0 but found 1
Loading history...
389
    {
390
        $sort = null;
391
        $sortSelectClause = null;
392
393
        if (!$entry_id && !$section_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $section_id of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
394
            return array();
395
        }
396
397
        if (!$section_id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $section_id of type integer|null is loosely compared to false; this is ambiguous if the integer can be 0. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
398
            $section_id = self::fetchEntrySectionID($entry_id);
0 ignored issues
show
Bug introduced by
It seems like $entry_id can also be of type array; however, parameter $entry_id of EntryManager::fetchEntrySectionID() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

398
            $section_id = self::fetchEntrySectionID(/** @scrutinizer ignore-type */ $entry_id);
Loading history...
399
        }
400
401
        $section = SectionManager::fetch($section_id);
402
        if (!is_object($section)) {
403
            return array();
404
        }
405
406
        // SORTING
407
        // A single $entry_id doesn't need to be sorted on, or if it's explicitly disabled
408
        if ((!is_array($entry_id) && General::intval($entry_id) > 0) || !$enable_sort) {
409
            $sort = null;
410
411
        // Check for RAND first, since this works independently of any specific field
412
        } elseif (self::$_fetchSortDirection == 'RAND') {
413
            $sort = 'ORDER BY RAND() ';
414
415
        // Handle Creation Date or the old Date sorting
416
        } elseif (self::$_fetchSortField === 'system:creation-date' || self::$_fetchSortField === 'date') {
417
            $sort = sprintf('ORDER BY `e`.`creation_date` %s', self::$_fetchSortDirection);
418
419
        // Handle Modification Date sorting
420
        } elseif (self::$_fetchSortField === 'system:modification-date') {
421
            $sort = sprintf('ORDER BY `e`.`modification_date` %s', self::$_fetchSortDirection);
422
423
        // Handle sorting for System ID
424
        } elseif (self::$_fetchSortField == 'system:id' || self::$_fetchSortField == 'id') {
425
            $sort = sprintf('ORDER BY `e`.`id` %s', self::$_fetchSortDirection);
426
427
        // Handle when the sort field is an actual Field
428
        } elseif (self::$_fetchSortField && $field = FieldManager::fetch(self::$_fetchSortField)) {
0 ignored issues
show
Bug introduced by
It seems like self::_fetchSortField can also be of type string; however, parameter $id of FieldManager::fetch() does only seem to accept array|integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

428
        } elseif (self::$_fetchSortField && $field = FieldManager::fetch(/** @scrutinizer ignore-type */ self::$_fetchSortField)) {
Loading history...
429
            if ($field->isSortable()) {
430
                $field->buildSortingSQL($joins, $where, $sort, self::$_fetchSortDirection);
431
                $sortSelectClause = $field->buildSortingSelectSQL($sort, self::$_fetchSortDirection);
432
            }
433
434
        // Handle if the section has a default sorting field
435
        } elseif ($section->getSortingField() && $field = FieldManager::fetch($section->getSortingField())) {
436
            if ($field->isSortable()) {
437
                $field->buildSortingSQL($joins, $where, $sort, $section->getSortingOrder());
438
                $sortSelectClause = $field->buildSortingSelectSQL($sort, $section->getSortingOrder());
439
            }
440
441
        // No sort specified, so just sort on system id
442
        } else {
443
            $sort = sprintf('ORDER BY `e`.`id` %s', self::$_fetchSortDirection);
444
        }
445
446
        if ($field && !$group) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.
Loading history...
447
            $group = $field->requiresSQLGrouping();
448
        }
449
450
        if ($entry_id && !is_array($entry_id)) {
451
            // The entry ID may be a comma-separated string, so explode it to make it
452
            // a proper array
453
            $entry_id = explode(',', $entry_id);
454
        }
455
456
        // An existing entry ID will be an array now, and we can force integer values
457
        if ($entry_id) {
458
            $entry_id = array_map(array('General', 'intval'), $entry_id);
0 ignored issues
show
Bug introduced by
It seems like $entry_id can also be of type integer; however, parameter $arr1 of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

458
            $entry_id = array_map(array('General', 'intval'), /** @scrutinizer ignore-type */ $entry_id);
Loading history...
459
        }
460
461
        $sql = sprintf("
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \n SELECT %s`... %s\n does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
462
            SELECT %s`e`.`id`, `e`.section_id,
463
                `e`.`author_id`, `e`.`modification_author_id`,
464
                `e`.`creation_date` AS `creation_date`,
465
                `e`.`modification_date` AS `modification_date`
466
                %s
467
            FROM `tbl_entries` AS `e`
468
            %s
469
            WHERE 1
470
            %s
471
            %s
472
            %s
473
            %s
474
            %s
475
            ",
476
            $group ? 'DISTINCT ' : '',
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
477
            $sortSelectClause ? ', ' . $sortSelectClause : '',
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
478
            $joins,
479
            $entry_id ? "AND `e`.`id` IN ('".implode("', '", $entry_id)."') " : '',
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Bug introduced by
It seems like $entry_id can also be of type integer; however, parameter $pieces of implode() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

479
            $entry_id ? "AND `e`.`id` IN ('".implode("', '", /** @scrutinizer ignore-type */ $entry_id)."') " : '',
Loading history...
480
            $section_id ? sprintf("AND `e`.`section_id` = %d", $section_id) : '',
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
Coding Style Comprehensibility introduced by
The string literal AND `e`.`section_id` = %d does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
481
            $where,
482
            $sort,
483
            $limit ? sprintf('LIMIT %d, %d', $start, $limit) : ''
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
484
        );
485
486
        $rows = Symphony::Database()->fetch($sql);
487
488
        // Create UNIX timestamps, as it has always been (Re: #2501)
489
        foreach ($rows as &$entry) {
490
            $entry['creation_date'] = DateTimeObj::get('U', $entry['creation_date']);
491
            $entry['modification_date'] = DateTimeObj::get('U', $entry['modification_date']);
492
        }
0 ignored issues
show
Coding Style introduced by
No blank line found after control structure
Loading history...
493
        unset($entry);
494
495
        return ($buildentries && (is_array($rows) && !empty($rows)) ? self::__buildEntries($rows, $section_id, $element_names) : $rows);
496
    }
497
498
    /**
499
     * Given an array of Entry data from `tbl_entries` and a section ID, return an
500
     * array of Entry objects. For performance reasons, it's possible to pass an array
501
     * of field handles via `$element_names`, so that only a subset of the section schema
502
     * will be queried. This function currently only supports Entry from one section at a
503
     * time.
504
     *
505
     * @param array $rows
506
     *  An array of Entry data from `tbl_entries` including the Entry ID, Entry section,
507
     *  the ID of the Author who created the Entry, and a Unix timestamp of creation
508
     * @param integer $section_id
509
     *  The section ID of the entries in the `$rows`
510
     * @param array $element_names
511
     *  Choose whether to get data from a subset of fields or all fields in a section,
512
     *  by providing an array of field names. Defaults to null, which will load data
513
     *  from all fields in a section.
514
     * @throws DatabaseException
515
     * @return array
516
     *  An array of Entry objects
517
     */
518
    public static function __buildEntries(array $rows, $section_id, $element_names = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$element_names" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$element_names"; expected 0 but found 1
Loading history...
519
    {
520
        $entries = array();
521
522
        if (empty($rows)) {
523
            return $entries;
524
        }
525
526
        $schema = FieldManager::fetchFieldIDFromElementName($element_names, $section_id);
527
528
        if (is_int($schema)) {
529
            $schema = array($schema);
530
        }
531
532
        $raw = array();
533
        $rows_string = '';
534
535
        // Append meta data:
536
        foreach ($rows as $entry) {
537
            $raw[$entry['id']]['meta'] = $entry;
538
            $rows_string .= $entry['id'] . ',';
539
        }
540
541
        $rows_string = trim($rows_string, ',');
542
543
        // Append field data:
544
        if (is_array($schema)) {
545
            foreach ($schema as $field_id) {
546
                try {
547
                    $row = Symphony::Database()->fetch("SELECT * FROM `tbl_entries_data_{$field_id}` WHERE `entry_id` IN ($rows_string) ORDER BY `id` ASC");
0 ignored issues
show
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $field_id instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
Coding Style Best Practice introduced by
As per coding-style, please use concatenation or sprintf for the variable $rows_string instead of interpolation.

It is generally a best practice as it is often more readable to use concatenation instead of interpolation for variables inside strings.

// Instead of
$x = "foo $bar $baz";

// Better use either
$x = "foo " . $bar . " " . $baz;
$x = sprintf("foo %s %s", $bar, $baz);
Loading history...
548
                } catch (Exception $e) {
549
                    // No data due to error
550
                    continue;
551
                }
552
553
                if (!is_array($row) || empty($row)) {
554
                    continue;
555
                }
556
557
                foreach ($row as $r) {
558
                    $entry_id = $r['entry_id'];
559
560
                    unset($r['id']);
561
                    unset($r['entry_id']);
562
563
                    if (!isset($raw[$entry_id]['fields'][$field_id])) {
564
                        $raw[$entry_id]['fields'][$field_id] = $r;
565
                    } else {
566
                        foreach (array_keys($r) as $key) {
567
                            // If this field already has been set, we need to take the existing
568
                            // value and make it array, adding the current value to it as well
569
                            // There is a special check incase the the field's value has been
570
                            // purposely set to null in the database.
571
                            if (
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after opening bracket; newline found
Loading history...
572
                                (
573
                                    isset($raw[$entry_id]['fields'][$field_id][$key])
574
                                    || is_null($raw[$entry_id]['fields'][$field_id][$key])
575
                                )
576
                                && !is_array($raw[$entry_id]['fields'][$field_id][$key])
577
                            ) {
578
                                $raw[$entry_id]['fields'][$field_id][$key] = array(
579
                                    $raw[$entry_id]['fields'][$field_id][$key],
580
                                    $r[$key]
581
                                );
582
583
                                // This key/value hasn't been set previously, so set it
584
                            } elseif (!isset($raw[$entry_id]['fields'][$field_id][$key])) {
585
                                $raw[$entry_id]['fields'][$field_id] = array($r[$key]);
586
587
                                // This key has been set and it's an array, so just append
588
                                // this value onto the array
589
                            } else {
590
                                $raw[$entry_id]['fields'][$field_id][$key][] = $r[$key];
591
                            }
592
                        }
593
                    }
594
                }
595
            }
596
        }
597
598
        // Loop over the array of entry data and convert it to an array of Entry objects
599
        foreach ($raw as $entry) {
600
            $obj = self::create();
601
602
            $obj->set('id', $entry['meta']['id']);
603
            $obj->set('author_id', $entry['meta']['author_id']);
604
            $obj->set('modification_author_id', $entry['meta']['modification_author_id']);
605
            $obj->set('section_id', $entry['meta']['section_id']);
606
            $obj->set('creation_date', DateTimeObj::get('c', $entry['meta']['creation_date']));
607
608
            if (isset($entry['meta']['modification_date'])) {
609
                $obj->set('modification_date', DateTimeObj::get('c', $entry['meta']['modification_date']));
610
            } else {
611
                $obj->set('modification_date', $obj->get('creation_date'));
612
            }
613
614
            $obj->creationDate = $obj->get('creation_date');
0 ignored issues
show
Deprecated Code introduced by
The property Entry::$creationDate has been deprecated: Since Symphony 2.3.1, use $entry->get('creation_date') instead. This variable will be removed in Symphony 3.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

614
            /** @scrutinizer ignore-deprecated */ $obj->creationDate = $obj->get('creation_date');

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Documentation Bug introduced by
It seems like $obj->get('creation_date') can also be of type array. However, the property $creationDate is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
615
616
            if (isset($entry['fields']) && is_array($entry['fields'])) {
617
                foreach ($entry['fields'] as $field_id => $data) {
618
                    $obj->setData($field_id, $data);
619
                }
620
            }
621
622
            $entries[] = $obj;
623
        }
624
625
        return $entries;
626
    }
627
628
629
    /**
630
     * Given an Entry ID, return the Section ID that it belongs to
631
     *
632
     * @param integer $entry_id
633
     *  The ID of the Entry to return it's section
634
     * @return integer
635
     *  The Section ID for this Entry's section
636
     */
637
    public static function fetchEntrySectionID($entry_id)
638
    {
639
        return Symphony::Database()->fetchVar('section_id', 0, sprintf("
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \n SELECT `se...WHERE `id` = %d LIMIT 1 does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Bug Best Practice introduced by
The expression return Symphony::Databas...d LIMIT 1', $entry_id)) also could return the type string which is incompatible with the documented return type integer.
Loading history...
640
            SELECT `section_id` FROM `tbl_entries` WHERE `id` = %d LIMIT 1",
641
            $entry_id
642
        ));
643
    }
644
645
    /**
646
     * Return the count of the number of entries in a particular section.
647
     *
648
     * @param integer $section_id
649
     *  The ID of the Section where the Entries are to be counted
650
     * @param string $where
651
     *  Any custom WHERE clauses
652
     * @param string $joins
653
     *  Any custom JOIN's
654
     * @param boolean $group
655
     *  Whether the entries need to be grouped by Entry ID or not
656
     * @return integer
657
     */
658
    public static function fetchCount($section_id = null, $where = null, $joins = null, $group = false)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$section_id" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$section_id"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$where" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$where"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$joins" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$joins"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$group" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$group"; expected 0 but found 1
Loading history...
659
    {
660
        if (is_null($section_id)) {
661
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
662
        }
663
664
        $section = SectionManager::fetch($section_id);
665
666
        if (!is_object($section)) {
667
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
668
        }
669
670
        return Symphony::Database()->fetchVar('count', 0, sprintf("
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal \n SELECT... %s\n does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Bug Best Practice introduced by
The expression return Symphony::Databas..., $section_id, $where)) also could return the type string which is incompatible with the documented return type integer.
Loading history...
671
                SELECT COUNT(%s`e`.id) as `count`
672
                FROM `tbl_entries` AS `e`
673
                %s
674
                WHERE `e`.`section_id` = %d
675
                %s
676
            ",
677
            $group ? 'DISTINCT ' : '',
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
678
            $joins,
679
            $section_id,
680
            $where
681
        ));
682
    }
683
684
    /**
685
     * Returns an array of Entry objects, with some basic pagination given
686
     * the number of Entry's to return and the current starting offset. This
687
     * function in turn calls the fetch function that does alot of the heavy
688
     * lifting. For instance, if there are 60 entries in a section and the pagination
689
     * dictates that per page, 15 entries are to be returned, by passing 2 to
690
     * the $page parameter you could return entries 15-30
691
     *
692
     * @param integer $page
693
     *  The page to return, defaults to 1
694
     * @param integer $section_id
695
     *  The ID of the Section that these entries are contained in
696
     * @param integer $entriesPerPage
697
     *  The number of entries to return per page.
698
     * @param string $where
699
     *  Any custom WHERE clauses
700
     * @param string $joins
701
     *  Any custom JOIN's
702
     * @param boolean $group
703
     *  Whether the entries need to be grouped by Entry ID or not
704
     * @param boolean $records_only
705
     *  If this is set to true, an array of Entry objects will be returned
706
     *  without any basic pagination information. Defaults to false
707
     * @param boolean $buildentries
708
     *  Whether to return an array of entry ID's or Entry objects. Defaults to
709
     *  true, which will return Entry objects
710
     * @param array $element_names
711
     *  Choose whether to get data from a subset of fields or all fields in a section,
712
     *  by providing an array of field names. Defaults to null, which will load data
713
     *  from all fields in a section.
714
     * @throws Exception
715
     * @return array
716
     *  Either an array of Entry objects, or an associative array containing
717
     *  the total entries, the start position, the entries per page and the
718
     *  Entry objects
719
     */
720
    public static function fetchByPage($page = 1, $section_id, $entriesPerPage, $where = null, $joins = null, $group = false, $records_only = false, $buildentries = true, array $element_names = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$page" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$page"; expected 0 but found 1
Loading history...
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$where" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$where"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$joins" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$joins"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$group" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$group"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$records_only" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$records_only"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$buildentries" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$buildentries"; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between argument "$element_names" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$element_names"; expected 0 but found 1
Loading history...
721
    {
722
        if ($entriesPerPage != null && !is_string($entriesPerPage) && !is_numeric($entriesPerPage)) {
0 ignored issues
show
introduced by
The condition is_numeric($entriesPerPage) is always true.
Loading history...
723
            throw new Exception(__('Entry limit specified was not a valid type. String or Integer expected.'));
724
        } elseif ($entriesPerPage == null) {
725
            $records = self::fetch(null, $section_id, null, null, $where, $joins, $group, $buildentries, $element_names);
726
727
            $count = self::fetchCount($section_id, $where, $joins, $group);
728
729
            $entries = array(
730
                'total-entries' => $count,
731
                'total-pages' => 1,
732
                'remaining-pages' => 0,
733
                'remaining-entries' => 0,
734
                'start' => 1,
735
                'limit' => $count,
736
                'records' => $records
737
            );
738
739
            return $entries;
740
        } else {
741
            $start = (max(1, $page) - 1) * $entriesPerPage;
742
743
            $records = ($entriesPerPage == '0' ? null : self::fetch(null, $section_id, $entriesPerPage, $start, $where, $joins, $group, $buildentries, $element_names));
0 ignored issues
show
Coding Style introduced by
Inline shorthand IF statement requires brackets around comparison
Loading history...
744
745
            if ($records_only) {
746
                return array('records' => $records);
747
            }
748
749
            $entries = array(
750
                'total-entries' => self::fetchCount($section_id, $where, $joins, $group),
751
                'records' => $records,
752
                'start' => max(1, $start),
753
                'limit' => $entriesPerPage
754
            );
755
756
            $entries['remaining-entries'] = max(0, $entries['total-entries'] - ($start + $entriesPerPage));
757
            $entries['total-pages'] = max(1, ceil($entries['total-entries'] * (1 / $entriesPerPage)));
758
            $entries['remaining-pages'] = max(0, $entries['total-pages'] - $page);
759
760
            return $entries;
761
        }
762
    }
763
764
    /**
765
     * Creates a new Entry object using this class as the parent.
766
     *
767
     * @return Entry
768
     */
769
    public static function create()
770
    {
771
        return new Entry;
772
    }
773
}
774