<?php
/**
- * @copyright Copyright (C) 2020, Friendica
+ * @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
*/
const NULL_DATETIME = '0001-01-01 00:00:00';
- public static function connect()
+ /**
+ * Lowest possible datetime(6) value
+ */
+ const NULL_DATETIME6 = '0001-01-01 00:00:00.000000';
+
+ public static function connect(): bool
{
return DI::dba()->connect();
}
/**
* Perform a reconnect of an existing database connection
*/
- public static function reconnect()
+ public static function reconnect(): bool
{
return DI::dba()->reconnect();
}
return DI::dba()->getConnection();
}
+ /**
+ * Return the database driver string
+ *
+ * @return string with either "pdo" or "mysqli"
+ */
+ public static function getDriver(): string
+ {
+ return DI::dba()->getDriver();
+ }
+
/**
* Returns the MySQL server version string
*
*
* @return string
*/
- public static function serverInfo()
+ public static function serverInfo(): string
{
return DI::dba()->serverInfo();
}
* @return string
* @throws \Exception
*/
- public static function databaseName()
+ public static function databaseName(): string
{
return DI::dba()->databaseName();
}
* @param string $str
* @return string escaped string
*/
- public static function escape($str)
+ public static function escape(string $str): string
{
return DI::dba()->escape($str);
}
*
* @return boolean is the database connected?
*/
- public static function connected()
+ public static function connected(): bool
{
return DI::dba()->connected();
}
* @param string $sql An SQL string without the values
* @return string The input SQL string modified if necessary.
*/
- public static function anyValueFallback($sql)
+ public static function anyValueFallback(string $sql): string
{
return DI::dba()->anyValueFallback($sql);
}
* @param string $sql An SQL string without the values
* @return string The input SQL string modified if necessary.
*/
- public static function cleanQuery($sql)
+ public static function cleanQuery(string $sql): string
{
$search = ["\t", "\n", "\r", " "];
$replace = [' ', ' ', ' ', ' '];
* @param array $args Parameter array
* @return array universalized parameter array
*/
- public static function getParam($args)
+ public static function getParam(array $args): array
{
unset($args[0]);
/**
* Executes a prepared statement that returns data
- * Example: $r = p("SELECT * FROM `item` WHERE `guid` = ?", $guid);
+ * Example: $r = p("SELECT * FROM `post` WHERE `guid` = ?", $guid);
*
* Please only use it with complicated queries.
* For all regular queries please use DBA::select or DBA::exists
* @return bool|object statement object or result object
* @throws \Exception
*/
- public static function p($sql)
+ public static function p(string $sql)
{
$params = self::getParam(func_get_args());
* Please use DBA::delete, DBA::insert, DBA::update, ... instead
*
* @param string $sql SQL statement
- * @return boolean Was the query successfull? False is returned only if an error occurred
+ * @return boolean Was the query successful? False is returned only if an error occurred
* @throws \Exception
*/
- public static function e($sql) {
-
+ public static function e(string $sql): bool
+ {
$params = self::getParam(func_get_args());
return DI::dba()->e($sql, $params);
/**
* Check if data exists
*
- * @param string|array $table Table name or array [schema => table]
- * @param array $condition array of fields for condition
- *
+ * @param string $table Table name in format schema.table (where schema is optional)
+ * @param array $condition Array of fields for condition
* @return boolean Are there rows for that condition?
* @throws \Exception
*/
- public static function exists($table, $condition)
+ public static function exists(string $table, array $condition): bool
{
return DI::dba()->exists($table, $condition);
}
* @return array first row of query
* @throws \Exception
*/
- public static function fetchFirst($sql)
+ public static function fetchFirst(string $sql)
{
$params = self::getParam(func_get_args());
*
* @return int Number of rows
*/
- public static function affectedRows()
+ public static function affectedRows(): int
{
return DI::dba()->affectedRows();
}
* @param object Statement object
* @return int Number of columns
*/
- public static function columnCount($stmt)
+ public static function columnCount($stmt): int
{
return DI::dba()->columnCount($stmt);
}
* @param PDOStatement|mysqli_result|mysqli_stmt Statement object
* @return int Number of rows
*/
- public static function numRows($stmt)
+ public static function numRows($stmt): int
{
return DI::dba()->numRows($stmt);
}
/**
* Insert a row into a table
*
- * @param string|array $table Table name or array [schema => table]
- * @param array $param parameter array
- * @param bool $on_duplicate_update Do an update on a duplicate entry
+ * @param string $table Table name in format schema.table (where schema is optional)
+ * @param array $param parameter array
+ * @param int $duplicate_mode What to do on a duplicated entry
+ * @return boolean was the insert successful?
+ * @throws \Exception
+ */
+ public static function insert(string $table, array $param, int $duplicate_mode = Database::INSERT_DEFAULT): bool
+ {
+ return DI::dba()->insert($table, $param, $duplicate_mode);
+ }
+
+ /**
+ * Inserts a row with the provided data in the provided table.
+ * If the data corresponds to an existing row through a UNIQUE or PRIMARY index constraints, it updates the row instead.
*
+ * @param string $table Table name in format schema.table (where schema is optional)
+ * @param array $param parameter array
* @return boolean was the insert successful?
* @throws \Exception
*/
- public static function insert($table, $param, $on_duplicate_update = false)
+ public static function replace(string $table, array $param): bool
{
- return DI::dba()->insert($table, $param, $on_duplicate_update);
+ return DI::dba()->replace($table, $param);
}
/**
*
* @return integer Last inserted id
*/
- public static function lastInsertId()
+ public static function lastInsertId(): int
{
return DI::dba()->lastInsertId();
}
*
* This function can be extended in the future to accept a table array as well.
*
- * @param string|array $table Table name or array [schema => table]
- *
+ * @param string $table Table name in format schema.table (where schema is optional)
* @return boolean was the lock successful?
* @throws \Exception
*/
- public static function lock($table)
+ public static function lock(string $table): bool
{
return DI::dba()->lock($table);
}
* @return boolean was the unlock successful?
* @throws \Exception
*/
- public static function unlock()
+ public static function unlock(): bool
{
return DI::dba()->unlock();
}
*
* @return boolean Was the command executed successfully?
*/
- public static function transaction()
+ public static function transaction(): bool
{
return DI::dba()->transaction();
}
*
* @return boolean Was the command executed successfully?
*/
- public static function commit()
+ public static function commit(): bool
{
return DI::dba()->commit();
}
*
* @return boolean Was the command executed successfully?
*/
- public static function rollback()
+ public static function rollback(): bool
{
return DI::dba()->rollback();
}
/**
* Delete a row from a table
*
- * @param string|array $table Table name
- * @param array $conditions Field condition(s)
- * @param array $options
- * - cascade: If true we delete records in other tables that depend on the one we're deleting through
- * relations (default: true)
+ * @param string $table Table name
+ * @param array $conditions Field condition(s)
*
* @return boolean was the delete successful?
* @throws \Exception
*/
- public static function delete($table, array $conditions, array $options = [])
+ public static function delete(string $table, array $conditions, array $options = []): bool
{
return DI::dba()->delete($table, $conditions, $options);
}
* Only set $old_fields to a boolean value when you are sure that you will update a single row.
* When you set $old_fields to "true" then $fields must contain all relevant fields!
*
- * @param string|array $table Table name or array [schema => table]
+ * @param string $table Table name in format schema.table (where schema is optional)
* @param array $fields contains the fields that are updated
* @param array $condition condition array with the key values
- * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate)
+ * @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)
+ * @param array $params Parameters: "ignore" If set to "true" then the update is done with the ignore parameter
*
- * @return boolean was the update successfull?
+ * @return boolean was the update successful?
* @throws \Exception
*/
- public static function update($table, $fields, $condition, $old_fields = [])
+ public static function update(string $table, array $fields, array $condition, $old_fields = [], array $params = []): bool
{
- return DI::dba()->update($table, $fields, $condition, $old_fields);
+ return DI::dba()->update($table, $fields, $condition, $old_fields, $params);
}
/**
* Retrieve a single record from a table and returns it in an associative array
*
- * @param string|array $table Table name or array [schema => table]
+ * @param string|array $table Table name in format schema.table (where schema is optional)
* @param array $fields
* @param array $condition
* @param array $params
* @throws \Exception
* @see self::select
*/
- public static function selectFirst($table, array $fields = [], array $condition = [], $params = [])
+ public static function selectFirst($table, array $fields = [], array $condition = [], array $params = [])
{
return DI::dba()->selectFirst($table, $fields, $condition, $params);
}
/**
* Select rows from a table and fills an array with the data
*
- * @param string|array $table Table name or array [schema => table]
- * @param array $fields Array of selected fields, empty for all
- * @param array $condition Array of fields for condition
- * @param array $params Array of several parameters
+ * @param string $table Table name in format schema.table (where schema is optional)
+ * @param array $fields Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
*
* @return array Data array
* @throws \Exception
* @see self::select
*/
- public static function selectToArray($table, array $fields = [], array $condition = [], array $params = [])
+ public static function selectToArray(string $table, array $fields = [], array $condition = [], array $params = [])
{
return DI::dba()->selectToArray($table, $fields, $condition, $params);
}
/**
* Select rows from a table
*
- * @param string|array $table Table name or array [schema => table]
- * @param array $fields Array of selected fields, empty for all
- * @param array $condition Array of fields for condition
- * @param array $params Array of several parameters
+ * @param string $table Table name in format schema.table (where schema is optional)
+ * @param array $fields Array of selected fields, empty for all
+ * @param array $condition Array of fields for condition
+ * @param array $params Array of several parameters
*
* @return boolean|object
*
* Example:
- * $table = "item";
+ * $table = "post";
* $fields = array("id", "uri", "uid", "network");
*
* $condition = array("uid" => 1, "network" => 'dspr');
* $data = DBA::select($table, $fields, $condition, $params);
* @throws \Exception
*/
- public static function select($table, array $fields = [], array $condition = [], array $params = [])
+ public static function select(string $table, array $fields = [], array $condition = [], array $params = [])
{
return DI::dba()->select($table, $fields, $condition, $params);
}
/**
* Counts the rows from a table satisfying the provided condition
*
- * @param string|array $table Table name or array [schema => table]
- * @param array $condition array of fields for condition
- * @param array $params Array of several parameters
+ * @param string $table Table name in format schema.table (where schema is optional)
+ * @param array $condition array of fields for condition
+ * @param array $params Array of several parameters
*
* @return int
*
* Example:
- * $table = "item";
+ * $table = "post";
*
* $condition = ["uid" => 1, "network" => 'dspr'];
* or:
* $count = DBA::count($table, $condition);
* @throws \Exception
*/
- public static function count($table, array $condition = [], array $params = [])
+ public static function count(string $table, array $condition = [], array $params = []): int
{
return DI::dba()->count($table, $condition, $params);
}
* Build the table query substring from one or more tables, with or without a schema.
*
* Expected formats:
- * - table
+ * - [table]
* - [table1, table2, ...]
* - [schema1 => table1, schema2 => table2, table3, ...]
*
- * @param string|array $tables
+ * @param array $tables Table names
* @return string
*/
- public static function buildTableString($tables)
+ public static function buildTableString(array $tables): string
{
- if (is_string($tables)) {
- $tables = [$tables];
- }
-
- $quotedTables = [];
-
- foreach ($tables as $schema => $table) {
- if (is_numeric($schema)) {
- $quotedTables[] = self::quoteIdentifier($table);
- } else {
- $quotedTables[] = self::quoteIdentifier($schema) . '.' . self::quoteIdentifier($table);
- }
- }
-
- return implode(', ', $quotedTables);
+ // Quote each entry
+ return implode(',', array_map([self::class, 'quoteIdentifier'], $tables));
}
/**
- * Escape an identifier (table or field name)
+ * Escape an identifier (table or field name) optional with a schema like ((schema.)table.)field
*
- * @param $identifier
- * @return string
+ * @param string $identifier Table, field name
+ * @return string Quotes table or field name
*/
- public static function quoteIdentifier($identifier)
+ public static function quoteIdentifier(string $identifier): string
{
- return '`' . str_replace('`', '``', $identifier) . '`';
+ return implode(
+ '.',
+ array_map(
+ function (string $identifier) { return '`' . str_replace('`', '``', $identifier) . '`'; },
+ explode('.', $identifier)
+ )
+ );
}
/**
* Returns the SQL condition string built from the provided condition array
*
* This function operates with two modes.
- * - Supplied with a filed/value associative array, it builds simple strict
+ * - Supplied with a field/value associative array, it builds simple strict
* equality conditions linked by AND.
* - Supplied with a flat list, the first element is the condition string and
* the following arguments are the values to be interpolated
* @param array $condition
* @return string
*/
- public static function buildCondition(array &$condition = [])
+ public static function buildCondition(array &$condition = []): string
{
$condition = self::collapseCondition($condition);
-
+
$condition_string = '';
if (count($condition) > 0) {
$condition_string = " WHERE (" . array_shift($condition) . ")";
* @param array $condition
* @return array
*/
- public static function collapseCondition(array $condition)
+ public static function collapseCondition(array $condition): array
{
// Ensures an always true condition is returned
if (count($condition) < 1) {
return $condition;
}
+ /**
+ * Merges the provided conditions into a single collapsed one
+ *
+ * @param array ...$conditions One or more condition arrays
+ * @return array A collapsed condition
+ * @see DBA::collapseCondition() for the condition array formats
+ */
+ public static function mergeConditions(array ...$conditions): array
+ {
+ if (count($conditions) == 1) {
+ return current($conditions);
+ }
+
+ $conditionStrings = [];
+ $result = [];
+
+ foreach ($conditions as $key => $condition) {
+ if (!$condition) {
+ continue;
+ }
+
+ $condition = self::collapseCondition($condition);
+
+ $conditionStrings[] = array_shift($condition);
+ // The result array holds the eventual parameter values
+ $result = array_merge($result, $condition);
+ }
+
+ if (count($conditionStrings)) {
+ // We prepend the condition string at the end to form a collapsed condition array again
+ array_unshift($result, implode(' AND ', $conditionStrings));
+ }
+
+ return $result;
+ }
+
/**
* Returns the SQL parameter string built from the provided parameter array
*
* @param array $params
* @return string
*/
- public static function buildParameter(array $params = [])
+ public static function buildParameter(array $params = []): string
{
$groupby_string = '';
if (!empty($params['group_by'])) {
- $groupby_string = " GROUP BY " . implode(', ', array_map(['self', 'quoteIdentifier'], $params['group_by']));
+ $groupby_string = " GROUP BY " . implode(', ', array_map([self::class, 'quoteIdentifier'], $params['group_by']));
}
$order_string = '';
if (isset($params['order'])) {
$order_string = " ORDER BY ";
- foreach ($params['order'] AS $fields => $order) {
+ foreach ($params['order'] as $fields => $order) {
if ($order === 'RAND()') {
$order_string .= "RAND(), ";
} elseif (!is_int($fields)) {
/**
* Fills an array with data from a query
*
- * @param object $stmt statement object
- * @param bool $do_close
+ * @param object $stmt statement object
+ * @param bool $do_close Close database connection after last row
+ * @param int $count maximum number of rows to be fetched
+ *
* @return array Data array
*/
- public static function toArray($stmt, $do_close = true)
+ public static function toArray($stmt, bool $do_close = true, int $count = 0): array
{
- return DI::dba()->toArray($stmt, $do_close);
+ return DI::dba()->toArray($stmt, $do_close, $count);
+ }
+
+ /**
+ * Cast field types according to the table definition
+ *
+ * @param string $table
+ * @param array $fields
+ * @return array casted fields
+ */
+ public static function castFields(string $table, array $fields): array
+ {
+ return DI::dba()->castFields($table, $fields);
}
/**
*
* @return string Error number (0 if no error)
*/
- public static function errorNo()
+ public static function errorNo(): int
{
return DI::dba()->errorNo();
}
*
* @return string Error message ('' if no error)
*/
- public static function errorMessage()
+ public static function errorMessage(): string
{
return DI::dba()->errorMessage();
}
* @param object $stmt statement object
* @return boolean was the close successful?
*/
- public static function close($stmt)
+ public static function close($stmt): bool
{
return DI::dba()->close($stmt);
}
* 'amount' => Number of concurrent database processes
* @throws \Exception
*/
- public static function processlist()
+ public static function processlist(): array
{
return DI::dba()->processlist();
}
+ /**
+ * Optimizes tables
+ *
+ * @param string $table a given table
+ *
+ * @return bool True, if successfully optimized, otherwise false
+ * @throws \Exception
+ */
+ public static function optimizeTable(string $table): bool
+ {
+ return DI::dba()->optimizeTable($table);
+ }
+
+ /**
+ * Kill sleeping database processes
+ */
+ public static function deleteSleepingProcesses()
+ {
+ DI::dba()->deleteSleepingProcesses();
+ }
+
/**
* Fetch a database variable
*
* Checks if $array is a filled array with at least one entry.
*
* @param mixed $array A filled array with at least one entry
- *
* @return boolean Whether $array is a filled array or an object with rows
*/
- public static function isResult($array)
+ public static function isResult($array): bool
{
return DI::dba()->isResult($array);
}
* @param boolean $add_quotation add quotation marks for string values
* @return void
*/
- public static function escapeArray(&$arr, $add_quotation = false)
+ public static function escapeArray(&$arr, bool $add_quotation = false)
{
DI::dba()->escapeArray($arr, $add_quotation);
}