Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

868 lines
22 KiB

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