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
![]() |
|||||
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
|
|||||
120 | $field = array(); |
||||
121 | } |
||||
122 | |||||
123 | $did_lock = false; |
||||
124 | $exception = null; |
||||
125 | try { |
||||
0 ignored issues
–
show
|
|||||
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
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);
![]() |
|||||
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 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 ( 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: 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. ![]() |
|||||
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
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
![]() |
|||||
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
|
|||||
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
|
|||||
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
|
|||||
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
|
|||||
340 | $field->entryDataCleanup($chunk); |
||||
341 | } |
||||
342 | } |
||||
343 | |||||
344 | Symphony::Database()->delete('tbl_entries', " `id` IN ('$entry_list') "); |
||||
0 ignored issues
–
show
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);
![]() |
|||||
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
|
|||||
389 | { |
||||
390 | $sort = null; |
||||
391 | $sortSelectClause = null; |
||||
392 | |||||
393 | if (!$entry_id && !$section_id) { |
||||
0 ignored issues
–
show
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 For 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
![]() |
|||||
394 | return array(); |
||||
395 | } |
||||
396 | |||||
397 | if (!$section_id) { |
||||
0 ignored issues
–
show
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 For 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
![]() |
|||||
398 | $section_id = self::fetchEntrySectionID($entry_id); |
||||
0 ignored issues
–
show
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
![]() |
|||||
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
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
![]() |
|||||
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
|
|||||
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
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
![]() |
|||||
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 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 ( 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: 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. ![]() |
|||||
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
|
|||||
477 | $sortSelectClause ? ', ' . $sortSelectClause : '', |
||||
0 ignored issues
–
show
|
|||||
478 | $joins, |
||||
479 | $entry_id ? "AND `e`.`id` IN ('".implode("', '", $entry_id)."') " : '', |
||||
0 ignored issues
–
show
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
![]() |
|||||
480 | $section_id ? sprintf("AND `e`.`section_id` = %d", $section_id) : '', |
||||
0 ignored issues
–
show
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 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 ( 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: 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. ![]() |
|||||
481 | $where, |
||||
482 | $sort, |
||||
483 | $limit ? sprintf('LIMIT %d, %d', $start, $limit) : '' |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
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
|
|||||
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
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);
![]() 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);
![]() |
|||||
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
|
|||||
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
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
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. ![]() 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 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;
}
![]() |
|||||
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 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 ( 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: 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. ![]() |
|||||
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
|
|||||
659 | { |
||||
660 | if (is_null($section_id)) { |
||||
661 | return false; |
||||
0 ignored issues
–
show
|
|||||
662 | } |
||||
663 | |||||
664 | $section = SectionManager::fetch($section_id); |
||||
665 | |||||
666 | if (!is_object($section)) { |
||||
667 | return false; |
||||
0 ignored issues
–
show
|
|||||
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 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 ( 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: 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. ![]() |
|||||
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
|
|||||
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
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) { }
![]() |
|||||
721 | { |
||||
722 | if ($entriesPerPage != null && !is_string($entriesPerPage) && !is_numeric($entriesPerPage)) { |
||||
0 ignored issues
–
show
|
|||||
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
|
|||||
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 |