]> git.mxchange.org Git - friendica.git/blob - src/Database/DBA.php
bump version 2023.12
[friendica.git] / src / Database / DBA.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Database;
23
24 use Friendica\DI;
25 use mysqli;
26 use mysqli_result;
27 use mysqli_stmt;
28 use PDO;
29 use PDOStatement;
30
31 /**
32  * This class is for the low level database stuff that does driver specific things.
33  */
34 class DBA
35 {
36         /**
37          * Lowest possible date value
38          */
39         const NULL_DATE     = '0001-01-01';
40         /**
41          * Lowest possible datetime value
42          */
43         const NULL_DATETIME = '0001-01-01 00:00:00';
44
45         /**
46          * Lowest possible datetime(6) value
47          */
48         const NULL_DATETIME6 = '0001-01-01 00:00:00.000000';
49
50         public static function connect(): bool
51         {
52                 return DI::dba()->connect();
53         }
54
55         /**
56          * Disconnects the current database connection
57          */
58         public static function disconnect()
59         {
60                 DI::dba()->disconnect();
61         }
62
63         /**
64          * Perform a reconnect of an existing database connection
65          */
66         public static function reconnect(): bool
67         {
68                 return DI::dba()->reconnect();
69         }
70
71         /**
72          * Return the database object.
73          * @return PDO|mysqli
74          */
75         public static function getConnection()
76         {
77                 return DI::dba()->getConnection();
78         }
79
80         /**
81          * Return the database driver string
82          *
83          * @return string with either "pdo" or "mysqli"
84          */
85         public static function getDriver(): string
86         {
87                 return DI::dba()->getDriver();
88         }
89
90         /**
91          * Returns the MySQL server version string
92          *
93          * This function discriminate between the deprecated mysql API and the current
94          * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
95          *
96          * @return string
97          */
98         public static function serverInfo(): string
99         {
100                 return DI::dba()->serverInfo();
101         }
102
103         /**
104          * Returns the selected database name
105          *
106          * @return string
107          * @throws \Exception
108          */
109         public static function databaseName(): string
110         {
111                 return DI::dba()->databaseName();
112         }
113
114         /**
115          * Escape all SQL unsafe data
116          *
117          * @param string $str
118          * @return string escaped string
119          */
120         public static function escape(string $str): string
121         {
122                 return DI::dba()->escape($str);
123         }
124
125         /**
126          * Checks if the database is connected
127          *
128          * @return boolean is the database connected?
129          */
130         public static function connected(): bool
131         {
132                 return DI::dba()->connected();
133         }
134
135         /**
136          * Replaces ANY_VALUE() function by MIN() function,
137          * if the database server does not support ANY_VALUE().
138          *
139          * Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
140          * ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
141          * A standard fall-back is to use MIN().
142          *
143          * @param string $sql An SQL string without the values
144          * @return string The input SQL string modified if necessary.
145          */
146         public static function anyValueFallback(string $sql): string
147         {
148                 return DI::dba()->anyValueFallback($sql);
149         }
150
151         /**
152          * beautifies the query - useful for "SHOW PROCESSLIST"
153          *
154          * This is safe when we bind the parameters later.
155          * The parameter values aren't part of the SQL.
156          *
157          * @param string $sql An SQL string without the values
158          * @return string The input SQL string modified if necessary.
159          */
160         public static function cleanQuery(string $sql): string
161         {
162                 $search = ["\t", "\n", "\r", "  "];
163                 $replace = [' ', ' ', ' ', ' '];
164                 do {
165                         $oldsql = $sql;
166                         $sql = str_replace($search, $replace, $sql);
167                 } while ($oldsql != $sql);
168
169                 return $sql;
170         }
171
172         /**
173          * Convert parameter array to an universal form
174          * @param array $args Parameter array
175          * @return array universalized parameter array
176          */
177         public static function getParam(array $args): array
178         {
179                 unset($args[0]);
180
181                 // When the second function parameter is an array then use this as the parameter array
182                 if ((count($args) > 0) && (is_array($args[1]))) {
183                         return $args[1];
184                 } else {
185                         return $args;
186                 }
187         }
188
189         /**
190          * Executes a prepared statement that returns data
191          * Example: $r = p("SELECT * FROM `post` WHERE `guid` = ?", $guid);
192          *
193          * Please only use it with complicated queries.
194          * For all regular queries please use DBA::select or DBA::exists
195          *
196          * @param string $sql SQL statement
197          * @return bool|object statement object or result object
198          * @throws \Exception
199          */
200         public static function p(string $sql)
201         {
202                 $params = self::getParam(func_get_args());
203
204                 return DI::dba()->p($sql, $params);
205         }
206
207         /**
208          * Executes a prepared statement like UPDATE or INSERT that doesn't return data
209          *
210          * Please use DBA::delete, DBA::insert, DBA::update, ... instead
211          *
212          * @param string $sql SQL statement
213          * @return boolean Was the query successful? False is returned only if an error occurred
214          * @throws \Exception
215          */
216         public static function e(string $sql): bool
217         {
218                 $params = self::getParam(func_get_args());
219
220                 return DI::dba()->e($sql, $params);
221         }
222
223         /**
224          * Check if data exists
225          *
226          * @param string $table     Table name in format schema.table (where schema is optional)
227          * @param array  $condition Array of fields for condition
228          * @return boolean Are there rows for that condition?
229          * @throws \Exception
230          */
231         public static function exists(string $table, array $condition): bool
232         {
233                 return DI::dba()->exists($table, $condition);
234         }
235
236         /**
237          * Fetches the first row
238          *
239          * Please use DBA::selectFirst or DBA::exists whenever this is possible.
240          *
241          * @param string $sql SQL statement
242          * @return array first row of query
243          * @throws \Exception
244          */
245         public static function fetchFirst(string $sql)
246         {
247                 $params = self::getParam(func_get_args());
248
249                 return DI::dba()->fetchFirst($sql, $params);
250         }
251
252         /**
253          * Returns the number of affected rows of the last statement
254          *
255          * @return int Number of rows
256          */
257         public static function affectedRows(): int
258         {
259                 return DI::dba()->affectedRows();
260         }
261
262         /**
263          * Returns the number of columns of a statement
264          *
265          * @param object Statement object
266          * @return int Number of columns
267          */
268         public static function columnCount($stmt): int
269         {
270                 return DI::dba()->columnCount($stmt);
271         }
272         /**
273          * Returns the number of rows of a statement
274          *
275          * @param PDOStatement|mysqli_result|mysqli_stmt Statement object
276          * @return int Number of rows
277          */
278         public static function numRows($stmt): int
279         {
280                 return DI::dba()->numRows($stmt);
281         }
282
283         /**
284          * Fetch a single row
285          *
286          * @param mixed $stmt statement object
287          * @return array current row
288          */
289         public static function fetch($stmt)
290         {
291                 return DI::dba()->fetch($stmt);
292         }
293
294         /**
295          * Insert a row into a table
296          *
297          * @param string $table          Table name in format schema.table (where schema is optional)
298          * @param array  $param          parameter array
299          * @param int    $duplicate_mode What to do on a duplicated entry
300          * @return boolean was the insert successful?
301          * @throws \Exception
302          */
303         public static function insert(string $table, array $param, int $duplicate_mode = Database::INSERT_DEFAULT): bool
304         {
305                 return DI::dba()->insert($table, $param, $duplicate_mode);
306         }
307
308         /**
309          * Inserts a row with the provided data in the provided table.
310          * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
311          *
312          * @param string $table Table name in format schema.table (where schema is optional)
313          * @param array  $param parameter array
314          * @return boolean was the insert successful?
315          * @throws \Exception
316          */
317         public static function replace(string $table, array $param): bool
318         {
319                 return DI::dba()->replace($table, $param);
320         }
321
322         /**
323          * Fetch the id of the last insert command
324          *
325          * @return integer Last inserted id
326          */
327         public static function lastInsertId(): int
328         {
329                 return DI::dba()->lastInsertId();
330         }
331
332         /**
333          * Locks a table for exclusive write access
334          *
335          * This function can be extended in the future to accept a table array as well.
336          *
337          * @param string $table Table name in format schema.table (where schema is optional)
338          * @return boolean was the lock successful?
339          * @throws \Exception
340          */
341         public static function lock(string $table): bool
342         {
343                 return DI::dba()->lock($table);
344         }
345
346         /**
347          * Unlocks all locked tables
348          *
349          * @return boolean was the unlock successful?
350          * @throws \Exception
351          */
352         public static function unlock(): bool
353         {
354                 return DI::dba()->unlock();
355         }
356
357         /**
358          * Starts a transaction
359          *
360          * @return boolean Was the command executed successfully?
361          */
362         public static function transaction(): bool
363         {
364                 return DI::dba()->transaction();
365         }
366
367         /**
368          * Does a commit
369          *
370          * @return boolean Was the command executed successfully?
371          */
372         public static function commit(): bool
373         {
374                 return DI::dba()->commit();
375         }
376
377         /**
378          * Does a rollback
379          *
380          * @return boolean Was the command executed successfully?
381          */
382         public static function rollback(): bool
383         {
384                 return DI::dba()->rollback();
385         }
386
387         /**
388          * Delete a row from a table
389          *
390          * @param string $table      Table name
391          * @param array  $conditions Field condition(s)
392          *
393          * @return boolean was the delete successful?
394          * @throws \Exception
395          */
396         public static function delete(string $table, array $conditions, array $options = []): bool
397         {
398                 return DI::dba()->delete($table, $conditions, $options);
399         }
400
401         /**
402          * Updates rows in the database.
403          *
404          * When $old_fields is set to an array,
405          * the system will only do an update if the fields in that array changed.
406          *
407          * Attention:
408          * Only the values in $old_fields are compared.
409          * This is an intentional behaviour.
410          *
411          * Example:
412          * We include the timestamp field in $fields but not in $old_fields.
413          * Then the row will only get the new timestamp when the other fields had changed.
414          *
415          * When $old_fields is set to a boolean value the system will do this compare itself.
416          * When $old_fields is set to "true" the system will do an insert if the row doesn't exists.
417          *
418          * Attention:
419          * Only set $old_fields to a boolean value when you are sure that you will update a single row.
420          * When you set $old_fields to "true" then $fields must contain all relevant fields!
421          *
422          * @param string        $table      Table name in format schema.table (where schema is optional)
423          * @param array         $fields     contains the fields that are updated
424          * @param array         $condition  condition array with the key values
425          * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate, false = don't update identical fields)
426          * @param array         $params     Parameters: "ignore" If set to "true" then the update is done with the ignore parameter
427          *
428          * @return boolean was the update successful?
429          * @throws \Exception
430          */
431         public static function update(string $table, array $fields, array $condition, $old_fields = [], array $params = []): bool
432         {
433                 return DI::dba()->update($table, $fields, $condition, $old_fields, $params);
434         }
435
436         /**
437          * Retrieve a single record from a table and returns it in an associative array
438          *
439          * @param string|array $table     Table name in format schema.table (where schema is optional)
440          * @param array        $fields
441          * @param array        $condition
442          * @param array        $params
443          * @return bool|array
444          * @throws \Exception
445          * @see   self::select
446          */
447         public static function selectFirst($table, array $fields = [], array $condition = [], array $params = [])
448         {
449                 return DI::dba()->selectFirst($table, $fields, $condition, $params);
450         }
451
452         /**
453          * Select rows from a table and fills an array with the data
454          *
455          * @param string $table     Table name in format schema.table (where schema is optional)
456          * @param array  $fields    Array of selected fields, empty for all
457          * @param array  $condition Array of fields for condition
458          * @param array  $params    Array of several parameters
459          *
460          * @return array Data array
461          * @throws \Exception
462          * @see   self::select
463          */
464         public static function selectToArray(string $table, array $fields = [], array $condition = [], array $params = [])
465         {
466                 return DI::dba()->selectToArray($table, $fields, $condition, $params);
467         }
468
469         /**
470          * Select rows from a table
471          *
472          * @param string $table     Table name in format schema.table (where schema is optional)
473          * @param array  $fields    Array of selected fields, empty for all
474          * @param array  $condition Array of fields for condition
475          * @param array  $params    Array of several parameters
476          *
477          * @return boolean|object
478          *
479          * Example:
480          * $table = "post";
481          * $fields = array("id", "uri", "uid", "network");
482          *
483          * $condition = array("uid" => 1, "network" => 'dspr');
484          * or:
485          * $condition = array("`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr');
486          *
487          * $params = array("order" => array("id", "received" => true), "limit" => 10);
488          *
489          * $data = DBA::select($table, $fields, $condition, $params);
490          * @throws \Exception
491          */
492         public static function select(string $table, array $fields = [], array $condition = [], array $params = [])
493         {
494                 return DI::dba()->select($table, $fields, $condition, $params);
495         }
496
497         /**
498          * Counts the rows from a table satisfying the provided condition
499          *
500          * @param string $table     Table name in format schema.table (where schema is optional)
501          * @param array  $condition array of fields for condition
502          * @param array  $params    Array of several parameters
503          *
504          * @return int
505          *
506          * Example:
507          * $table = "post";
508          *
509          * $condition = ["uid" => 1, "network" => 'dspr'];
510          * or:
511          * $condition = ["`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr'];
512          *
513          * $count = DBA::count($table, $condition);
514          * @throws \Exception
515          */
516         public static function count(string $table, array $condition = [], array $params = []): int
517         {
518                 return DI::dba()->count($table, $condition, $params);
519         }
520
521         /**
522          * Build the table query substring from one or more tables, with or without a schema.
523          *
524          * Expected formats:
525          * - [table]
526          * - [table1, table2, ...]
527          * - [schema1 => table1, schema2 => table2, table3, ...]
528          *
529          * @param array $tables Table names
530          * @return string
531          */
532         public static function buildTableString(array $tables): string
533         {
534                 // Quote each entry
535                 return implode(',', array_map([self::class, 'quoteIdentifier'], $tables));
536         }
537
538         /**
539          * Escape an identifier (table or field name) optional with a schema like ((schema.)table.)field
540          *
541          * @param string $identifier Table, field name
542          * @return string Quotes table or field name
543          */
544         public static function quoteIdentifier(string $identifier): string
545         {
546                 return implode(
547                         '.',
548                         array_map(
549                                 function (string $identifier) { return '`' . str_replace('`', '``', $identifier) . '`'; },
550                                 explode('.', $identifier)
551                         )
552                 );
553         }
554
555         /**
556          * Returns the SQL condition string built from the provided condition array
557          *
558          * This function operates with two modes.
559          * - Supplied with a field/value associative array, it builds simple strict
560          *   equality conditions linked by AND.
561          * - Supplied with a flat list, the first element is the condition string and
562          *   the following arguments are the values to be interpolated
563          *
564          * $condition = ["uid" => 1, "network" => 'dspr'];
565          * or:
566          * $condition = ["`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr'];
567          *
568          * In either case, the provided array is left with the parameters only
569          *
570          * @param array $condition
571          * @return string
572          */
573         public static function buildCondition(array &$condition = []): string
574         {
575                 $condition = self::collapseCondition($condition);
576
577                 $condition_string = '';
578                 if (count($condition) > 0) {
579                         $condition_string = " WHERE (" . array_shift($condition) . ")";
580                 }
581
582                 return $condition_string;
583         }
584
585         /**
586          * Collapse an associative array condition into a SQL string + parameters condition array.
587          *
588          * ['uid' => 1, 'network' => ['dspr', 'apub']]
589          *
590          * gets transformed into
591          *
592          * ["`uid` = ? AND `network` IN (?, ?)", 1, 'dspr', 'apub']
593          *
594          * @param array $condition
595          * @return array
596          */
597         public static function collapseCondition(array $condition): array
598         {
599                 // Ensures an always true condition is returned
600                 if (count($condition) < 1) {
601                         return ['1'];
602                 }
603
604                 reset($condition);
605                 $first_key = key($condition);
606
607                 if (is_int($first_key)) {
608                         // Already collapsed
609                         return $condition;
610                 }
611
612                 $values = [];
613                 $condition_string = "";
614                 foreach ($condition as $field => $value) {
615                         if ($condition_string != "") {
616                                 $condition_string .= " AND ";
617                         }
618
619                         if (is_array($value)) {
620                                 if (count($value)) {
621                                         /* Workaround for MySQL Bug #64791.
622                                          * Never mix data types inside any IN() condition.
623                                          * In case of mixed types, cast all as string.
624                                          * Logic needs to be consistent with DBA::p() data types.
625                                          */
626                                         $is_int = false;
627                                         $is_alpha = false;
628                                         foreach ($value as $single_value) {
629                                                 if (is_int($single_value)) {
630                                                         $is_int = true;
631                                                 } else {
632                                                         $is_alpha = true;
633                                                 }
634                                         }
635
636                                         if ($is_int && $is_alpha) {
637                                                 foreach ($value as &$ref) {
638                                                         if (is_int($ref)) {
639                                                                 $ref = (string)$ref;
640                                                         }
641                                                 }
642                                                 unset($ref); //Prevent accidental re-use.
643                                         }
644
645                                         $values = array_merge($values, array_values($value));
646                                         $placeholders = substr(str_repeat("?, ", count($value)), 0, -2);
647                                         $condition_string .= self::quoteIdentifier($field) . " IN (" . $placeholders . ")";
648                                 } else {
649                                         // Empty value array isn't supported by IN and is logically equivalent to no match
650                                         $condition_string .= "FALSE";
651                                 }
652                         } elseif (is_null($value)) {
653                                 $condition_string .= self::quoteIdentifier($field) . " IS NULL";
654                         } else {
655                                 $values[$field] = $value;
656                                 $condition_string .= self::quoteIdentifier($field) . " = ?";
657                         }
658                 }
659
660                 $condition = array_merge([$condition_string], array_values($values));
661
662                 return $condition;
663         }
664
665         /**
666          * Merges the provided conditions into a single collapsed one
667          *
668          * @param array ...$conditions One or more condition arrays
669          * @return array A collapsed condition
670          * @see DBA::collapseCondition() for the condition array formats
671          */
672         public static function mergeConditions(array ...$conditions): array
673         {
674                 if (count($conditions) == 1) {
675                         return current($conditions);
676                 }
677
678                 $conditionStrings = [];
679                 $result = [];
680
681                 foreach ($conditions as $key => $condition) {
682                         if (!$condition) {
683                                 continue;
684                         }
685
686                         $condition = self::collapseCondition($condition);
687
688                         $conditionStrings[] = array_shift($condition);
689                         // The result array holds the eventual parameter values
690                         $result = array_merge($result, $condition);
691                 }
692
693                 if (count($conditionStrings)) {
694                         // We prepend the condition string at the end to form a collapsed condition array again
695                         array_unshift($result, implode(' AND ', $conditionStrings));
696                 }
697
698                 return $result;
699         }
700
701         /**
702          * Returns the SQL parameter string built from the provided parameter array
703          *
704          * Expected format for each key:
705          *
706          * group_by:
707          *  - list of column names
708          *
709          * order:
710          *  - numeric keyed column name => ASC
711          *  - associative element with boolean value => DESC (true), ASC (false)
712          *  - associative element with string value => 'ASC' or 'DESC' literally
713          *
714          * limit:
715          *  - single numeric value => count
716          *  - list with two numeric values => offset, count
717          *
718          * @param array $params
719          * @return string
720          */
721         public static function buildParameter(array $params = []): string
722         {
723                 $groupby_string = '';
724                 if (!empty($params['group_by'])) {
725                         $groupby_string = " GROUP BY " . implode(', ', array_map([self::class, 'quoteIdentifier'], $params['group_by']));
726                 }
727
728                 $order_string = '';
729                 if (isset($params['order'])) {
730                         $order_string = " ORDER BY ";
731                         foreach ($params['order'] as $fields => $order) {
732                                 if ($order === 'RAND()') {
733                                         $order_string .= "RAND(), ";
734                                 } elseif (!is_int($fields)) {
735                                         if ($order !== 'DESC' && $order !== 'ASC') {
736                                                 $order = $order ? 'DESC' : 'ASC';
737                                         }
738
739                                         $order_string .= self::quoteIdentifier($fields) . " " . $order . ", ";
740                                 } else {
741                                         $order_string .= self::quoteIdentifier($order) . ", ";
742                                 }
743                         }
744                         $order_string = substr($order_string, 0, -2);
745                 }
746
747                 $limit_string = '';
748                 if (isset($params['limit']) && is_numeric($params['limit'])) {
749                         $limit_string = " LIMIT " . intval($params['limit']);
750                 }
751
752                 if (isset($params['limit']) && is_array($params['limit'])) {
753                         $limit_string = " LIMIT " . intval($params['limit'][0]) . ", " . intval($params['limit'][1]);
754                 }
755
756                 return $groupby_string . $order_string . $limit_string;
757         }
758
759         /**
760          * Fills an array with data from a query
761          *
762          * @param object $stmt     statement object
763          * @param bool   $do_close Close database connection after last row
764          * @param int    $count    maximum number of rows to be fetched
765          *
766          * @return array Data array
767          */
768         public static function toArray($stmt, bool $do_close = true, int $count = 0): array
769         {
770                 return DI::dba()->toArray($stmt, $do_close, $count);
771         }
772
773         /**
774          * Cast field types according to the table definition
775          *
776          * @param string $table
777          * @param array  $fields
778          * @return array casted fields
779          */
780         public static function castFields(string $table, array $fields): array
781         {
782                 return DI::dba()->castFields($table, $fields);
783         }
784
785         /**
786          * Returns the error number of the last query
787          *
788          * @return string Error number (0 if no error)
789          */
790         public static function errorNo(): int
791         {
792                 return DI::dba()->errorNo();
793         }
794
795         /**
796          * Returns the error message of the last query
797          *
798          * @return string Error message ('' if no error)
799          */
800         public static function errorMessage(): string
801         {
802                 return DI::dba()->errorMessage();
803         }
804
805         /**
806          * Closes the current statement
807          *
808          * @param object $stmt statement object
809          * @return boolean was the close successful?
810          */
811         public static function close($stmt): bool
812         {
813                 return DI::dba()->close($stmt);
814         }
815
816         /**
817          * Return a list of database processes
818          *
819          * @return array
820          *      'list' => List of processes, separated in their different states
821          *      'amount' => Number of concurrent database processes
822          * @throws \Exception
823          */
824         public static function processlist(): array
825         {
826                 return DI::dba()->processlist();
827         }
828
829         /**
830          * Optimizes tables
831          *
832          * @param string $table a given table
833          *
834          * @return bool True, if successfully optimized, otherwise false
835          * @throws \Exception
836          */
837         public static function optimizeTable(string $table): bool
838         {
839                 return DI::dba()->optimizeTable($table);
840         }
841
842         /**
843          * Kill sleeping database processes
844          */
845         public static function deleteSleepingProcesses()
846         {
847                 DI::dba()->deleteSleepingProcesses();
848         }
849
850         /**
851          * Fetch a database variable
852          *
853          * @param string $name
854          * @return string content
855          */
856         public static function getVariable(string $name)
857         {
858                 return DI::dba()->getVariable($name);
859         }
860
861         /**
862          * Checks if $array is a filled array with at least one entry.
863          *
864          * @param mixed $array A filled array with at least one entry
865          * @return boolean Whether $array is a filled array or an object with rows
866          */
867         public static function isResult($array): bool
868         {
869                 return DI::dba()->isResult($array);
870         }
871
872         /**
873          * Escapes a whole array
874          *
875          * @param mixed   $arr           Array with values to be escaped
876          * @param boolean $add_quotation add quotation marks for string values
877          * @return void
878          */
879         public static function escapeArray(&$arr, bool $add_quotation = false)
880         {
881                 DI::dba()->escapeArray($arr, $add_quotation);
882         }
883 }