yf_db class

Database abstraction layer.

Fileplugins/db/classes/yf_db.class.php
Use_class('db')

Public properties

A site sets them in its config with $PROJECT_CONF['db']['NAME'] = ...;

PropertyDefaultDescription
$ALLOW_AUTO_CREATE_DB
bool
false Needed for installation and repairing process
$ALLOW_CACHE_QUERIES
bool
false Allow to cache specified queries results
$AUTO_CONNECT
bool
false Auto-connect on/off
$CACHE_QUERIES_LIMIT
bool
100 Max number of cached queries
$CACHE_TABLE_NAMES
bool
false Use tables names caching
$CONNECTION_REQUIRED
bool
false Connection required or not (else E_USER_WARNING will be thrown not E_USER_ERROR)
$DB_CHARSET
string
''
$DB_DRIVERS_DIR
string
'classes/db/' Folder where databases drivers are stored
$DB_HOST
string
''
$DB_NAME
string
''
$DB_PERSIST
bool
false
$DB_PORT
int
''
$DB_PREFIX
string
null Tables names prefix
$DB_PSWD
string
''
$DB_REPLICATION_SLAVE
bool
false In case of true - we will try to avoid any data/structure modification queries to not break replication
$DB_SOCKET
string
''
$DB_SSL
bool
false
$DB_TYPE
string
'mysql' Type of database (default)
$DB_USER
string
''
$ERROR_AUTO_REPAIR
bool
true Auto-repairing on error (table not exists) on/off
$ERROR_BACKTRACE
bool
true Use backtrace in error message
$ESCAPE_BUILDER_VALUES
bool
false escape_val() (query builder, insert(), update(), replace()) runs the driver escape before quoting; *_safe() then skip es(). Off by default for sites that pre-escape, see https://github.com/yfix/yf/blob/master/docs/UPGRADING.md
$FILE_NAME_LOG_ALL
string
'db_queries.log' Log queries file name
$FILE_NAME_LOG_SLOW
string
'slow_queries.log' Log queries file name
$FIX_DATA_SAFE
bool
true update_safe, insert_safe, update_batch_safe: use additional checking for exising table fields
$FIX_DATA_SAFE_SILENT
bool
false Trigger to act silently or not in *_safe methods
$GATHER_AFFECTED_ROWS
bool
true Gather affected rows stats (will be used only when DEBUG_MODE is enabled)
$INSTRUMENT_QUERIES
bool
false Add additional engine details to the SQL as comment for later use
$IS_PRIMARY_CONNECTION null
$LOGGED_QUERIES_LIMIT
bool
1000 Max number of logged queries (set to 0 to unlimited)
$LOG_ALL_QUERIES
bool
false Store db queries to file
$LOG_SLOW_QUERIES
bool
false Store db queries to file
$NO_AUTO_CONNECT null
$NO_CACHE
bool
false Switch caching on/off
$NUM_QUERIES
int
0 @conf_skip Number of queries
$NUM_QUERY_ERRORS 0 Count of failed queries and the error of the latest failed one (not cleared by a later success), used by the migrator.
$QUERY_REVISIONS
bool
false Use sql query revisions for update/insert/replace/delete
$QUERY_REVISIONS_METHODS []
$QUERY_REVISIONS_TABLES []
$RECONNECT_CONSOLE_TRIES
int
1000 Num tries to reconnect inside CONSOLE MODE (will be useful if db server is overloaded and sometimes we lost connection to it) (Set to '0' for disabling)
$RECONNECT_DELAY
int
1 Time to wait between reconnects (in seconds)
$RECONNECT_DELAY_LOG_INC
bool
1 Use logarithmic increase or reconnect time
$RECONNECT_LOCK_FILE_NAME
string
'db_cannot_connect_[DB_HOST]_[DB_NAME]_[DB_USER]_[DB_PORT]'...
$RECONNECT_LOCK_TIMEOUT
int
30 Time in seconds between unlock reconnect
$RECONNECT_MYSQL_ERRORS
array
[1053, 1317, 2000, 2002, 2003, 2004, ... (14 items)]
array (
  0 => 1053,
  1 => 1317,
  2 => 2000,
  3 => 2002,
  4 => 2003,
  5 => 2004,
  6 => 2005,
  7 => 2006,
  8 => 2008,
  9 => 2012,
  10 => 2013,
  11 => 2020,
  12 => 2027,
  13 => 2055,
)
List of mysql error codes to use for reconnect tries. See also http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html
$RECONNECT_NUM_TRIES
int
3 Num tries to reconnect in common mode (will be useful if db server is overloaded) (Set to '0' for disabling)
$RECONNECT_USE_LOCKING
bool
false Use locking for reconnects or not
$REMOVED_DRIVERS
array
[] DB_TYPE values deleted in yf 3.9: name => reason, a configured one warns before the usual "wrong driver" failure
$REPAIRABLE_MYSQL_ERRORS
array
[1191, 2013, 1205, 1213, 1146, 1054] MySQL error codes the installer repair reacts to (1191 fulltext, 2013 lost connection, 1205 lock wait, 1213 deadlock, 1146 no table, 1054 no column); the fallback for a driver without repairable_error_codes()
$SHOW_QUERY_INFO
bool
true Currently only in DEBUG_MODE
$SHOW_QUERY_WARNINGS
bool
true Currently only in DEBUG_MODE
$SLOW_QUERIES_TIME_LIMIT
float
0.20000000000000001
$SPLIT_WHERE_VALUES_BY_COMMA
bool
1, b < 2') and in / not in lists (where('id', 'in', '1,2,3'), each item quoted); any other value * (where(field, [op,] value), [field, op, value], ['field' => value]) stays one quoted value, s... SECURITY: where() / where_or() / having() split every string argument at commas, values too, so where('name', 'like', "x' OR 1=1 -- ,y") put the value text into the SQL unquoted. false splits only condition strings ('a = 1, b < 2') and in / not in lists (where('id', 'in', '1,2,3'), each item quoted); any other value (where(field, [op,] value), [field, op, value], ['field' => value]) stays one quoted value, so where('id', '1,2,3') is no IN list any more. Default true keeps the old split (4.0 flips). A builder instance can override it with its own SPLIT_WHERE_VALUES_BY_COMMA.
$SQL_NO_CACHE
bool
false Adding SQL_NO_CACHE to SELECT queries: useful to find long running queries
$TABLE_NAMES_CACHE_TTL
int
3600 Tables cache lifetime (while developing need to be short) (else need to be very large)
$USE_QUERY_BACKTRACE
bool
true Use backtrace to get where query was called from (will be used only when DEBUG_MODE is enabled)
$USE_SHUTDOWN_QUERIES
bool
true Allow to use shutdown queries or not
$_LOG
array
[] Query log array
$_SHUTDOWN_QUERIES []
$_connect_start_time null
$_connected
bool
false @conf_skip Internal var (default value)
$_connection_time null
$_db_results_cache []
$_found_tables []
$_innodb_has_fulltext
mixed
null @conf_skip Set by yf_db_installer_mysql::innodb_has_fulltext()
$_instrument_items
array
[]
$_last_affected_rows null
$_last_failed_query_error null
$_last_insert_id null
$_last_query_error null
$_need_sys_prefix
array
[] Filled automatically from generated file
$_queries_logged null
$_repairs_by_sql []
$_shutdown_executed null
$_tried_to_connect
bool
false @conf_skip Internal var (default value)
$db
mixed
null @conf_skip Driver instance
$installer null
$migrator null
$utils null

Public methods 103

affected_rows

public function affected_rows()

Get number of affected rows.

    public function affected_rows()
    {
        if (isset($this->_last_affected_rows)) {
            return $this->_last_affected_rows;
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->affected_rows();
    }

all

public function all($sql, $key_name = null, $use_cache = true)

Alias.

    public function all($sql, $key_name = null, $use_cache = true)
    {
        return $this->query_fetch_all($sql, $key_name, $use_cache);
    }

begin

public function begin()

Begin a transaction, or if a transaction has already started, continue it.

    public function begin()
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        $this->_last_failed_query_error = null;
        return $this->db->begin();
    }

close

public function close()

Close connection to db.

    public function close()
    {
        $this->_connected = false;
        $this->_tried_to_connect = false;
        $result = $this->db->close();
        // null, not unset: a later read must not warn (is_object/isset see the same thing)
        $this->db = null;
        return $result;
    }

commit

public function commit()

End a transaction, or decrement the nest level if transactions are nested.

    public function commit()
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->commit();
    }

connect

public function connect($db_host = '', $db_user = '', $db_pswd = null, $db_name = null, $force = false, $params = [])

Connect db driver and then connect to db.

    public function connect($db_host = '', $db_user = '', $db_pswd = null, $db_name = null, $force = false, $params = [])
    {
        if (is_array($db_host)) {
            $params = $db_host;
            $db_host = '';
        }
        if ( ! is_array($params)) {
            $params = [];
        }
        if (($params['reconnect'] ?? false) || ($params['force'] ?? false)) {
            $force = true;
        }
        if ( ! empty($this->_tried_to_connect) && ! $force) {
            return $this->_connected;
        }
        $this->_connect_start_time = microtime(true);
        if ( ! ($params['reconnect'] ?? false)) {
            $this->_set_connect_params($db_host, $db_user, $db_pswd, $db_name, $force, $params);
        }
        if (isset($this->REMOVED_DRIVERS[strtolower($this->DB_TYPE)])) {
            trigger_error('DB: DB_TYPE "' . $this->DB_TYPE . '" ' . $this->REMOVED_DRIVERS[strtolower($this->DB_TYPE)] . ', use "mysqli"', E_USER_WARNING);
        }
        $driver_class_name = main()->load_class_file('db_driver_' . $this->DB_TYPE, $this->DB_DRIVERS_DIR);
        // Create new instanse of the driver class
        if ( ! empty($driver_class_name) && class_exists($driver_class_name) && ! is_object($this->db)) {
            if ($this->RECONNECT_USE_LOCKING) {
                $lock_file = $this->_get_reconnect_lock_path();
                if (file_exists($lock_file)) {
                    if ((time() - filemtime($lock_file)) > $this->RECONNECT_LOCK_TIMEOUT) {
                        unlink($lock_file);
                    } else {
                        return false;
                    }
                }
            }
            $driver_params = $this->_get_connect_params();
            // Try to connect several times
            $tries = $this->RECONNECT_NUM_TRIES;
            if (main()->is_console() && ! main()->is_unit_test()) {
                $tries = $this->RECONNECT_CONSOLE_TRIES;
            }
            for ($i = 1; $i <= $tries; $i++) {
                $this->db = new $driver_class_name($driver_params);
                if ( ! is_object($this->db) || ! ($this->db instanceof yf_db_driver)) {
                    if ($this->CONNECTION_REQUIRED) {
                        throw new RuntimeException('DB: Wrong driver');
                    }
                    trigger_error('DB: Wrong driver', E_USER_WARNING);
                    break;
                }
                $implemented = [];
                foreach (get_class_methods($this->db) as $method) {
                    if ($method[0] != '_') {
                        $implemented[$method] = $method;
                    }
                }
                $this->db->implemented = $implemented;
                // Stop after success
                if ( ! empty($this->db->db_connect_id)) {
                    break;
                    // Wait some time and try again (use logarithmic increase)
                }
                $multiplier = 1;
                if ($this->RECONNECT_DELAY_LOG_INC) {
                    $multiplier = $i + ($this->RECONNECT_DELAY <= 1 ? 1 : 0);
                }
                $sleep_time = $this->RECONNECT_DELAY * $multiplier;
                sleep($sleep_time);
            }
            if ($this->RECONNECT_USE_LOCKING && ! $this->db->db_connect_id) {
                file_put_contents($lock_file, gmdate('Y-m-d H:i:s') . ' GMT');
            }
        }
        $this->_tried_to_connect = true;
        if ( ! is_object($this->db) || ! $this->db->db_connect_id) {
            if ($this->CONNECTION_REQUIRED) {
                throw new RuntimeException('DB: ERROR CONNECTING TO DATABASE');
            }
            trigger_error('DB: ERROR CONNECTING TO DATABASE', E_USER_WARNING);
        } else {
            $this->db->SQL_NO_CACHE = $this->SQL_NO_CACHE;
            $this->_connected = true;
        }
        $this->_connection_time += (microtime(true) - $this->_connect_start_time);
        return $this->_connected;
    }

delete

public function delete($table, $where, $as_sql = false)

Helper.

    public function delete($table, $where, $as_sql = false)
    {
        $sql = $this->from($table)->delete($where, $_as_sql = true);
        if ( ! $sql) {
            return false;
        }
        if (MAIN_TYPE_ADMIN && $this->QUERY_REVISIONS && ! $as_sql) {
            $this->_save_query_revision(__FUNCTION__, $table, ['data' => $sql]);
        }
        return $as_sql ? $sql : $this->query($sql);
    }
public function enable_silent_mode()

'Silent' mode (logging off, tracing off, debugging off).

    public function enable_silent_mode()
    {
        $this->ALLOW_CACHE_QUERIES = false;
        $this->GATHER_AFFECTED_ROWS = false;
        $this->USE_SHUTDOWN_QUERIES = false;
        $this->LOG_ALL_QUERIES = false;
        $this->LOG_SLOW_QUERIES = false;
        $this->USE_QUERY_BACKTRACE = false;
        $this->ERROR_BACKTRACE = false;
        $this->LOGGED_QUERIES_LIMIT = 1;
    }

error

public function error()

Return database error.

    public function error()
    {
        if ( ! is_object($this->db)) {
            return false;
        }
        return $this->db->error();
    }

es

public function es($string)

Function escapes characters for using in query.

    public function es($string)
    {
        if ($string === null || $string === 'NULL') {
            return 'NULL';
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return $this->_mysql_escape_mimic($string);
        }
        // Helper method for passing here whole arrays as param
        if (is_array($string)) {
            foreach ((array) $string as $k => $v) {
                if ($v === null || $v === 'NULL') {
                    $string[$k] = 'NULL';
                } else {
                    $string[$k] = $this->real_escape_string($v);
                }
            }
            return $string;
        }
        return $this->db->real_escape_string($string);
    }
public function es_for_builder($data)

Escape only while the builder does not ($ESCAPE_BUILDER_VALUES off), identical to es() then. For data that goes straight into insert()/update()/replace()/update_batch() or where(). Keep es() for values concatenated into raw SQL strings.

  • $data mixed scalar or array
    public function es_for_builder($data)
    {
        return $this->ESCAPE_BUILDER_VALUES ? $data : $this->es($data);
    }

escape

public function escape($string)

Alias.

    public function escape($string)
    {
        return $this->es($string);
    }

escape_key

public function escape_key($data)
    public function escape_key($data)
    {
        if (is_array($data)) {
            $func = __FUNCTION__;
            foreach ((array) $data as $k => $v) {
                $data[$k] = $this->$func($v);
            }
            return $data;
        }
        if ( ! is_object($this->db)) {
            return '`' . str_replace('`', '``', trim($data, '`')) . '`';
        }
        return $this->db->escape_key($data);
    }

escape_string

public function escape_string($string)

Alias.

    public function escape_string($string)
    {
        return $this->es($string);
    }

escape_val

public function escape_val($data)
    public function escape_val($data)
    {
        if ($data === null || $data === 'NULL') {
            return 'NULL';
        } elseif (is_array($data)) {
            $func = __FUNCTION__;
            foreach ((array) $data as $k => $v) {
                $data[$k] = $this->$func($v);
            }
            return $data;
        }
        if ( ! is_object($this->db)) {
            return '\'' . ($this->ESCAPE_BUILDER_VALUES ? $this->_mysql_escape_mimic($data) : $data) . '\'';
        }
        $this->db->ESCAPE_BUILDER_VALUES = $this->ESCAPE_BUILDER_VALUES;
        return $this->db->escape_val($data);
    }

execute

public function execute($stmt, $params = [])

Execute prepared statement.

    public function execute($stmt, $params = [])
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        if ( ! is_object($this->db) || ! $this->db->implemented['execute']) {
            return false;
        }
        return $this->db->execute($stmt, $params);
    }

fetch

public function fetch($result)

Alias.

    public function fetch($result)
    {
        return $this->fetch_assoc($result);
    }

fetch_array

public function fetch_array($result)

Function return fetched array with both text and numeric indexes.

    public function fetch_array($result)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->fetch_array($result);
    }

fetch_assoc

public function fetch_assoc($result)

Function return fetched array with text indexes.

    public function fetch_assoc($result)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->fetch_assoc($result);
    }

fetch_object

public function fetch_object($result)

Function return fetched object with assoc var names.

    public function fetch_object($result)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->fetch_object($result);
    }

fetch_row

public function fetch_row($result)

Function return fetched array with numeric indexes.

    public function fetch_row($result)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->fetch_row($result);
    }

free_result

public function free_result($result)

Free result assosiated with a given query resource.

    public function free_result($result)
    {
        if ( ! $this->_connected && ! $this->connect() && empty($result)) {
            return false;
        }
        return $this->db->free_result($result);
    }

from

public function from()

Query builder shortcut.

    public function from()
    {
        return call_user_func_array([$this->query_builder(), __FUNCTION__], func_get_args());
    }

get

public function get($sql, $use_cache = true, $assoc = true, $return_sql = false)

Alias.

    public function get($sql, $use_cache = true, $assoc = true, $return_sql = false)
    {
        return $this->query_fetch($sql, $use_cache, $assoc, $return_sql);
    }

get_2d

public function get_2d($sql, $use_cache = true)

Alias, return 2d array, where key is first field and value is the second, Example: 'SELECT id, name FROM p_static_pages' => array('1' => 'page1', '2' => 'page2') Example: 'SELECT name FROM p_static_pages' => array('page1', 'page2').

    public function get_2d($sql, $use_cache = true)
    {
        if ( ! strlen($sql)) {
            return false;
        }
        $result = $this->query_fetch_all($sql, $use_cache, true);
        // Get 1st and 2nd keys from first sub-array
        $keys = [];
        if (is_array($result) && $result) {
            $keys = array_keys(current($result));
        }
        if ( ! $keys) {
            return false;
        }
        $out = [];
        foreach ((array) $result as $id => $data) {
            if (isset($keys[1])) {
                $out[$data[$keys[0]]] = $data[$keys[1]];
            } else {
                $out[] = $data[$keys[0]];
            }
        }
        return $out;
    }

get_all

public function get_all($sql, $key_name = null, $use_cache = true)

Alias.

    public function get_all($sql, $key_name = null, $use_cache = true)
    {
        return $this->query_fetch_all($sql, $key_name, $use_cache);
    }
public function get_all_cached($sql, $key_name = null, $cache_ttl = 600)

Alias.

    public function get_all_cached($sql, $key_name = null, $cache_ttl = 600)
    {
        return $this->query_fetch_all_cached($sql, $key_name, $cache_ttl);
    }

get_cached

public function get_cached($sql, $cache_ttl = 600)

Alias.

    public function get_cached($sql, $cache_ttl = 600)
    {
        return $this->query_fetch_cached($sql, $cache_ttl);
    }
public function get_deep_array($sql, $max_levels = 0, $use_cache = true)

Generate multi-level (up to 4) array from incoming query, useful to save some code on generating this often. Example: get_deep_array('SELECT department_id, user_id, name FROM t_personal', 2) => [ 25 => [ 654 => [ 'department_id' => 25, 'user_id' => 654, 'name' => 'Peter', ]]].

    public function get_deep_array($sql, $max_levels = 0, $use_cache = true)
    {
        if ( ! strlen($sql)) {
            return false;
        }
        if ( ! $max_levels || $max_levels > 4) {
            $max_levels = 4;
        }
        $out = [];
        $q = $this->query($sql);
        if ( ! $q) {
            return false;
        }
        $row = $this->fetch_assoc($q);
        $levels = count((array) $row);
        if ( ! is_array($row) || ! $levels) {
            return false;
        }
        if ($levels > $max_levels) {
            $levels = $max_levels;
        }
        $k = array_keys($row);
        $a = [];
        do {
            if ($levels == 1) {
                $a[$row[$k[0]]] = $row;
            } elseif ($levels == 2) {
                $a[$row[$k[0]]][$row[$k[1]]] = $row;
            } elseif ($levels == 3) {
                $a[$row[$k[0]]][$row[$k[1]]][$row[$k[2]]] = $row;
            } elseif ($levels == 4) {
                $a[$row[$k[0]]][$row[$k[1]]][$row[$k[2]]][$row[$k[3]]] = $row;
            }
        } while ($row = $this->fetch_assoc($q));
        return $a;
    }
public function get_driver_family($db_type = '')
    public function get_driver_family($db_type = '')
    {
        $db_type = strtolower($db_type ?: $this->DB_TYPE);
        // Get current abstract db type
        $families = [
            'mysql' => ['db_type', 'mysql', 'mysqli', 'pdo_mysql'],
            'pgsql' => ['pgsql', 'pdo_pgsql', 'postgre', 'postgres'],
        ];
        $name = null;
        foreach ($families as $family => $aliases) {
            if (in_array($db_type, $aliases)) {
                $name = $family;
                break;
            }
        }
        if ( ! $name) {
            $name = $db_type;
        }
        return $name;
    }

get_host_info

public function get_host_info()
    public function get_host_info()
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->get_host_info();
    }

get_one

public function get_one($sql, $use_cache = true, $return_sql = false)

Alias, return first value.

    public function get_one($sql, $use_cache = true, $return_sql = false)
    {
        if ( ! strlen($sql)) {
            return false;
        }
        $result = $this->query_fetch($sql, $use_cache, $assoc = true, $return_sql);
        if ( ! $result) {
            return false;
        }
        // Foreach needed here as we do not know first key name
        foreach (array_keys($result) as $key) {
            return $result[$key];
        }
        return false;
    }
public function get_server_version()
    public function get_server_version()
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->get_server_version();
    }
public function get_table_columns_cached($table, $no_cache = false)
    public function get_table_columns_cached($table, $no_cache = false)
    {
        $cache_name = __FUNCTION__ . '|' . $table . '|' . $this->DB_HOST . '|' . $this->DB_PORT . '|' . $this->DB_NAME . '|' . $this->DB_PREFIX;
        if ($this->NO_CACHE) {
            $no_cache = true;
        }
        $data = [];
        if ( ! $no_cache) {
            $data = cache()->get($cache_name);
        }
        if ( ! $data) {
            $data = $this->meta_columns($table);
            if ( ! $no_cache) {
                cache()->set($cache_name, $data);
            }
        }
        return $data;
    }

insert

public function insert($table, $data, $only_sql = false, $replace = false, $ignore = false, $on_duplicate_key_update = false, $extra = [])

Insert array of values into table.

    public function insert($table, $data, $only_sql = false, $replace = false, $ignore = false, $on_duplicate_key_update = false, $extra = [])
    {
        if ($this->DB_REPLICATION_SLAVE && ! $only_sql) {
            return false;
        }
        if ( ! strlen($table) || ! is_array($data)) {
            return false;
        }
        if ( ! is_array($extra)) {
            $extra = [];
        }
        if (is_array($only_sql)) {
            $extra += $only_sql;
            $only_sql = $extra['only_sql'];
        }
        isset($extra['only_sql']) && $only_sql = $extra['only_sql'];
        isset($extra['replace']) && $replace = $extra['replace'];
        isset($extra['ignore']) && $ignore = $extra['ignore'];
        isset($extra['on_duplicate_key_update']) && $on_duplicate_key_update = $extra['on_duplicate_key_update'];
        is_string($replace) && $replace = false;

        $sql = $this->query_builder()->compile_insert($table, $data, compact('replace', 'ignore', 'on_duplicate_key_update') + $extra);
        if ( ! $sql) {
            return false;
        }
        if ($only_sql) {
            return $sql;
        }
        if (MAIN_TYPE_ADMIN && $this->QUERY_REVISIONS) {
            $this->_save_query_revision(__FUNCTION__, $table, ['data' => $sql]);
        }
        return $this->query($sql);
    }

insert_id

public function insert_id()

Return last insert id.

    public function insert_id()
    {
        if (isset($this->_last_insert_id)) {
            return $this->_last_insert_id;
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->insert_id();
    }

insert_ignore

public function insert_ignore($table, $data, $only_sql = false, $replace = false, $extra = [])

Alias, forced to add INSERT IGNORE.

    public function insert_ignore($table, $data, $only_sql = false, $replace = false, $extra = [])
    {
        return $this->insert($table, $data, $only_sql, $replace, $ignore = true, $on_duplicate_key_update = false, $extra);
    }
public function insert_on_duplicate_key_update($table, $data, $only_sql = false, $replace = false, $extra = [])

Alias, forced to add INSERT ... ON DUPLICATE KEY UPDATE.

    public function insert_on_duplicate_key_update($table, $data, $only_sql = false, $replace = false, $extra = [])
    {
        $on_duplicate_key_update = $this->_supports_upsert();
        return $this->insert($table, $data, $only_sql, $replace, $ignore = false, $on_duplicate_key_update, $extra);
    }

insert_safe

public function insert_safe($table, $data, $only_sql = false, $replace = false, $ignore = false, $on_duplicate_key_update = false, $extra = [])

Alias of insert() with auto-escaping of data.

    public function insert_safe($table, $data, $only_sql = false, $replace = false, $ignore = false, $on_duplicate_key_update = false, $extra = [])
    {
        if ( ! is_array($extra)) {
            $extra = [];
        }
        if (is_array($only_sql)) {
            $extra += $only_sql;
            $only_sql = $extra['only_sql'];
        }
        $data = $this->_fix_data_safe($table, $data, $extra);
        return $this->insert($table, $this->_es_unless_builder_escapes($data), $only_sql, $replace, $ignore, $on_duplicate_key_update, $extra);
    }

installer

public function installer()
    public function installer()
    {
        if ( ! isset($this->installer)) {
            $cname = 'db_installer_' . $this->get_driver_family();
            $this->installer = _class($cname, 'classes/db/');
            $this->installer->db = $this;
        }
        return $this->installer;
    }

is_ready

public function is_ready()
    public function is_ready()
    {
        return (bool) $this->_connected;
    }

last_error

public function last_error()

Return error of the latest executed query. Difference from error() is that it will work correctly when repair enabled (it executes lot of self queries and cleaning db api latest error).

    public function last_error()
    {
        $var = $this->_last_query_error;
        return isset($var['code']) && ! empty($var['code']) ? $var : false;
        // TODO: use this only when repair table enabled and called.
    }

limit

public function limit($count, $offset = null)

Return database-specific limit of returned rows.

    public function limit($count, $offset = null)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            $sql = '';
            if ($count > 0) {
                $offset = ($offset > 0) ? $offset : 0;
                $sql = 'LIMIT ' . ($offset ? $offset . ', ' : '') . $count;
            }
            return $sql;
        }
        return $this->db->limit($count, $offset);
    }

meta_columns

public function meta_columns($table)

Return columns info for selected table.

    public function meta_columns($table)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        $table = $this->_escape_table_name($table);
        if ( ! strlen($table)) {
            return false;
        }
        return $this->utils()->meta_columns($table);
    }

meta_tables

public function meta_tables()

Return tables list for current database.

    public function meta_tables()
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->utils()->meta_tables($this->DB_PREFIX);
    }

migrator

public function migrator()
    public function migrator()
    {
        if ( ! isset($this->migrator)) {
            $cname = 'db_migrator_' . $this->get_driver_family();
            $this->migrator = _class($cname, 'classes/db/');
            $this->migrator->db = $this;
        }
        return $this->migrator;
    }

model

public function model($name, $params = [])

ORM shortcut.

    public function model($name, $params = [])
    {
        $model = $this->_model_load($name);
        $params && $model->_set_params($params);
        return $model;
    }

multi_query

public function multi_query($sql = [])
    public function multi_query($sql = [])
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        if ( ! is_object($this->db)) {
            return false;
        }
        if ( ! $this->db->HAS_MULTI_QUERY) {
            $result = [];
            foreach ((array) $sql as $k => $_sql) {
                $result[$k] = $this->query($_sql);
            }
            return $result;
        }
        return $this->db->multi_query($sql);
    }

num_rows

public function num_rows($result)

Function return number of rows in the query.

    public function num_rows($result)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->num_rows($result);
    }

prepare

public function prepare($sql, $params = [])

Prepare statement to execute.

    public function prepare($sql, $params = [])
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        if ( ! is_object($this->db) || ! $this->db->implemented['prepare']) {
            return false;
        }
        return $this->db->prepare($sql, $params);
    }

query

public function query($sql)

Function return resource ID of the query.

    public function query($sql)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        if ( ! is_object($this->db)) {
            return false;
        }
        $this->NUM_QUERIES++;
        $query_time_start = null;
        if (DEBUG_MODE) {
            $query_time_start = microtime(true);
            $no_cache_hint = $this->SQL_NO_CACHE ? (string) $this->_driver_hook('no_cache_hint', [], $this->get_driver_family() === 'mysql' ? 'SQL_NO_CACHE' : '') : '';
            if ($no_cache_hint !== '') {
                $q = strtoupper(substr(ltrim($sql), 0, 100));
                if (substr($q, 0, 6) == 'SELECT' && false === strpos($q, $no_cache_hint)) {
                    $sql = preg_replace('/^[\s\t]*(SELECT)[\s\t]+/ims', '$1 ' . $no_cache_hint . ' ', $sql);
                }
            }
        }
        if ($this->INSTRUMENT_QUERIES) {
            $sql = $this->_instrument_query($sql);
        }
        $query_allowed = true;
        if ($this->DB_REPLICATION_SLAVE && preg_match('/^[\s\t]*(UPDATE|INSERT|DELETE|ALTER|CREATE|RENAME|TRUNCATE)[\s\t]+/ims', $sql)) {
            $query_allowed = false;
        }
        $db_error = false;
        $result = null;
        if ($query_allowed) {
            try {
                $result = $this->db->query($sql);
            } catch (Exception $e) {
                $err = $e->getMessage();
            }
        }
        if ( ! $result && $query_allowed && ! $db_error) {
            $db_error = $this->db->error();
        }
        if ( ! $result && $query_allowed && $db_error) {
            // Try to reconnect if we see some these errors: http://dev.mysql.com/doc/refman/5.0/en/error-messages-client.html
            if ($this->_driver_hook('is_reconnect_error', [$db_error, $this->RECONNECT_MYSQL_ERRORS], $this->get_driver_family() === 'mysql' && in_array($db_error['code'], $this->RECONNECT_MYSQL_ERRORS))) {
                $this->_driver_hook('prepare_reconnect', [], null);
                $this->db = null;
                $reconnect_successful = $this->connect(['reconnect' => true]);
                if ($reconnect_successful) {
                    $result = $this->db->query($sql);
                }
            }
        }
        $log_allowed = (DEBUG_MODE || $this->LOG_ALL_QUERIES || $this->LOG_SLOW_QUERIES);
        if ($log_allowed) {
            $log_id = $this->_query_log($sql, $this->USE_QUERY_BACKTRACE ? $this->_trace_string() : [], $db_error);
        }
        if ( ! $result && $query_allowed && $db_error && $this->ERROR_AUTO_REPAIR) {
            $result = $this->_repair_table($sql, $db_error);
        }
        if ( ! $result && $db_error) {
            $this->_query_show_error($sql, $db_error, (DEBUG_MODE && $this->ERROR_BACKTRACE) ? $this->_trace_string() : '');
            $this->_last_query_error = $db_error;
            $this->NUM_QUERY_ERRORS++;
            // the cause of an aborted transaction stays visible, not the "commands ignored" errors that follow it
            if ( ! $this->_last_failed_query_error || ! $this->_driver_hook('is_follow_up_error', [$db_error], false)) {
                $this->_last_failed_query_error = $db_error;
            }
        } else {
            $this->_last_query_error = null;
        }
        $need_insert_id = false;
        $_sql_type = strtoupper(rtrim(substr(ltrim($sql), 0, 7)));
        if (in_array($_sql_type, ['INSERT', 'UPDATE', 'REPLACE'])) {
            $need_insert_id = true;
        }
        $this->_last_insert_id = $result && $need_insert_id ? (int) $this->db->insert_id() : null;
        if ($this->GATHER_AFFECTED_ROWS) {
            $this->_last_affected_rows = $result ? (int) $this->db->affected_rows() : null;
        }
        // This part needed to update debug log after executing query, but ensure correct order of queries
        if ($log_allowed) {
            $warnings = null;
            $info = null;
            if (DEBUG_MODE && $this->SHOW_QUERY_WARNINGS && method_exists($this->db, 'get_last_warnings')) {
                $warnings = $this->db->get_last_warnings();
            }
            if (DEBUG_MODE && $this->SHOW_QUERY_INFO && method_exists($this->db, 'get_last_query_info')) {
                $info = $this->db->get_last_query_info();
            }
            $this->_update_query_log($log_id, $result, $query_time_start, $warnings, $info);
        }
        return $result;
    }

query_builder

public function query_builder()
    public function query_builder()
    {
        $cname = 'db_query_builder_' . $this->get_driver_family();
        $qb = clone _class($cname, 'classes/db/');
        $qb->db = $this;
        return $qb;
    }

query_fetch

public function query_fetch($sql, $use_cache = true, $assoc = true, $return_sql = false)

Execute database query and fetch result as assoc array (for queries that returns only 1 row).

    public function query_fetch($sql, $use_cache = true, $assoc = true, $return_sql = false)
    {
        if ( ! strlen($sql)) {
            return false;
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        $storage = &$this->_db_results_cache;
        if ($use_cache && $this->ALLOW_CACHE_QUERIES && ! $this->NO_CACHE && isset($storage[$sql])) {
            return $storage[$sql];
        }
        $data = null;
        if ($this->_driver_hook('auto_limit_one', [], $this->get_driver_family() === 'mysql') && strtoupper(substr(ltrim($sql), 0, 6)) === 'SELECT') {
            $sql = rtrim(rtrim(rtrim($sql), ';'));
            if ( ! preg_match('~\s+LIMIT\s+[0-9,\s]+$~ims', strtoupper($sql))) {
                $sql .= ' LIMIT 1';
            }
        }
        // Mostly for unit tests, not a real use case
        if ($return_sql) {
            return $sql;
        }
        $q = $this->query($sql);
        if ( ! empty($q)) {
            if ($assoc) {
                $data = @$this->db->fetch_assoc($q);
            } else {
                $data = @$this->db->fetch_row($q);
            }
            $this->free_result($q);
            // Store result in variable cache
            if ($use_cache && $this->ALLOW_CACHE_QUERIES && ! $this->NO_CACHE && ! isset($storage[$sql])) {
                $storage[$sql] = $data;
                // Permanently turn off queries cache (and free some memory) if case of limit reached
                if ($this->CACHE_QUERIES_LIMIT && count((array) $storage) > $this->CACHE_QUERIES_LIMIT) {
                    $this->ALLOW_CACHE_QUERIES = false;
                    $storage = null;
                }
            }
        }
        return $data;
    }
public function query_fetch_all($sql, $key_name = null, $use_cache = true)

Execute database query and fetch result into assotiative array.

    public function query_fetch_all($sql, $key_name = null, $use_cache = true)
    {
        if ( ! strlen($sql)) {
            return false;
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        $params = [];
        if (is_array($use_cache)) {
            $params = $use_cache;
            $use_cache = isset($params['use_cache']) ? $params['use_cache'] : true;
        }
        $storage = &$this->_db_results_cache;
        if ($use_cache && $this->ALLOW_CACHE_QUERIES && ! $this->NO_CACHE && isset($storage[$sql])) {
            if ($params['as_objects'] ?? false) {
                foreach ((array) $storage[$sql] as $k => $v) {
                    $storage[$sql][$k] = (object) $v;
                }
            }
            return $storage[$sql];
        }
        $data = null;
        $q = $this->query($sql);
        if ($q) {
            // If $key_name is specified - then save to $data using it as key
            while ($a = @$this->db->fetch_assoc($q)) {
                if ($key_name != null && $key_name != '-1') {
                    $data[$a[$key_name]] = $a;
                } elseif (isset($a['id']) && $key_name != '-1') {
                    $data[$a['id']] = $a;
                } else {
                    $data[] = $a;
                }
            }
            @$this->free_result($q);
        }
        // Store result in variable cache
        if ($use_cache && $this->ALLOW_CACHE_QUERIES && ! $this->NO_CACHE && ! isset($storage[$sql])) {
            $storage[$sql] = $data;
            // Permanently turn off queries cache (and free some memory) if case of limit reached
            if ($this->CACHE_QUERIES_LIMIT && count((array) $storage) > $this->CACHE_QUERIES_LIMIT) {
                $this->ALLOW_CACHE_QUERIES = false;
                $storage = null;
            }
        }
        if ($params['as_objects'] ?? false) {
            foreach ((array) $data as $k => $v) {
                $data[$k] = (object) $v;
            }
        }
        return $data;
    }
public function query_fetch_all_cached($sql, $key_name = null, $cache_ttl = 600)

Alias with core cache.

    public function query_fetch_all_cached($sql, $key_name = null, $cache_ttl = 600)
    {
        $cache_key = 'SQL_' . __FUNCTION__ . '_' . $this->DB_HOST . '_' . $this->DB_NAME . '_' . abs(crc32($sql));
        $use_cache = true;
        if ($this->NO_CACHE) {
            $use_cache = false;
        }
        $data = [];
        if ($use_cache) {
            $data = cache_get($cache_key);
        }
        if ( ! $data) {
            $data = $this->query_fetch_all($sql, $key_name);
            if ($use_cache) {
                cache_set($cache_key, $data);
            }
        }
        return $data;
    }
public function query_fetch_assoc($sql, $use_cache = true)

Alias.

    public function query_fetch_assoc($sql, $use_cache = true)
    {
        return $this->query_fetch($sql, $use_cache, true);
    }
public function query_fetch_cached($sql, $cache_ttl = 600)

Execute database query and fetch result as assoc array (for queries that returns only 1 row).

    public function query_fetch_cached($sql, $cache_ttl = 600)
    {
        $cache_key = 'SQL_' . __FUNCTION__ . '_' . $this->DB_HOST . '_' . $this->DB_NAME . '_' . abs(crc32($sql));
        $use_cache = true;
        if ($this->NO_CACHE) {
            $use_cache = false;
        }
        $data = [];
        if ($use_cache) {
            $data = cache_get($cache_key);
        }
        if ( ! $data) {
            $data = $this->query_fetch($sql);
            if ($use_cache) {
                cache_set($cache_key, $data);
            }
        }
        return $data;
    }
public function query_fetch_row($sql, $use_cache = true)

Same as 'query_fetch' except fetching as row not assoc.

    public function query_fetch_row($sql, $use_cache = true)
    {
        return $this->query_fetch($sql, $use_cache, false);
    }
public function query_num_rows($sql)

Execute database query and the calculate number of rows.

    public function query_num_rows($sql)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        $Q = $this->query($sql);
        $result = $this->db->num_rows($Q);
        $this->free_result($Q);
        return $result;
    }
public function real_escape_string($string)

Alias.

    public function real_escape_string($string)
    {
        return $this->es($string);
    }

replace

public function replace($table, $data, $only_sql = false)

Replace array of values into table.

    public function replace($table, $data, $only_sql = false)
    {
        $replace = $this->_supports_upsert();
        return $this->insert($table, $data, $only_sql, $replace);
    }

replace_safe

public function replace_safe($table, $data, $only_sql = false, $extra = [])

Alias of replace() with data auto-escape.

    public function replace_safe($table, $data, $only_sql = false, $extra = [])
    {
        $replace = $this->_supports_upsert();
        return $this->insert_safe($table, $data, $only_sql, $replace, $ignore = false, $on_duplicate_key_update = false, $extra);
    }

rollback

public function rollback()

Rollback a transaction.

    public function rollback()
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->db->rollback();
    }

select

public function select()

Query builder shortcut.

    public function select()
    {
        return call_user_func_array([$this->query_builder(), __FUNCTION__], func_get_args());
    }

split_sql

public function split_sql($sql)
    public function split_sql($sql)
    {
        return $this->utils()->split_sql($sql);
    }

table

public function table()

Query builder shortcut.

    public function table()
    {
        return call_user_func_array([$this->query_builder(), __FUNCTION__], func_get_args());
    }

transaction

public function transaction($callback)

Transaction wrapper. Examples: db()->transaction(function($db) { $user = $db->from('user')->first(); $user['verified'] = true; return $db->update('user', $user); }).

    public function transaction($callback)
    {
        if ( ! is_callable($callback)) {
            return false;
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        $this->begin();
        $rolled_back = false;
        try {
            $result = $callback($this);
        } catch (Exception $e) {
            $result = false;
            $this->rollback();
            $rolled_back = true;
            throw $e;
        }
        if ($result === false || $rolled_back) {
            ! $rolled_back && $this->rollback();
            return false;
        }
        return $this->commit();
    }
public function unbuffered_query($sql)

Function execute unbuffered query.

    public function unbuffered_query($sql)
    {
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        return $this->query($sql);
    }

update

public function update($table, $data, $where, $only_sql = false, $extra = [])

Update table with given values.

    public function update($table, $data, $where, $only_sql = false, $extra = [])
    {
        if ($this->DB_REPLICATION_SLAVE && ! $only_sql) {
            return false;
        }
        if (empty($table) || empty($data) || empty($where)) {
            return false;
        }
        if ( ! is_array($extra)) {
            $extra = [];
        }
        if (is_array($only_sql)) {
            $extra += $only_sql;
            $only_sql = $extra['only_sql'];
        }
        isset($extra['only_sql']) && $only_sql = $extra['only_sql'];
        $sql = $this->query_builder()->compile_update($table, $data, $where, $extra);
        if ( ! $sql) {
            return false;
        }
        if ($only_sql) {
            return $sql;
        }
        if (MAIN_TYPE_ADMIN && $this->QUERY_REVISIONS) {
            $this->_save_query_revision(__FUNCTION__, $table, ['data' => $sql]);
        }
        return $this->query($sql);
    }

update_batch

public function update_batch($table, $data, $index = null, $only_sql = false)
    public function update_batch($table, $data, $index = null, $only_sql = false)
    {
        if ($this->DB_REPLICATION_SLAVE && ! $only_sql) {
            return false;
        }
        if ( ! $this->_connected && ! $this->connect()) {
            return false;
        }
        if ( ! is_object($this->db)) {
            return false;
        }
        if ( ! $index) {
            $index = 'id';
        }
        if ( ! strlen($table) || ! $data || ! is_array($data) || ! $index) {
            return false;
        }
        $params = [];
        if (MAIN_TYPE_ADMIN && $this->QUERY_REVISIONS) {
            $_this = $this;
            $fname = __FUNCTION__;
            $params['slice_callback'] = function ($_data) use ($_this, $fname, $table, $index) {
                $_this->_save_query_revision($fname, $table, ['data' => $_data, 'index' => $index]);
            };
        }
        return $this->query_builder()->update_batch($table, $data, $index, $only_sql, $params);
    }
public function update_batch_safe($table, $data, $index = null, $only_sql = false)
    public function update_batch_safe($table, $data, $index = null, $only_sql = false)
    {
        $data = $this->_fix_data_safe($table, $data);
        return $this->update_batch($table, $this->_es_unless_builder_escapes($data), $index, $only_sql);
    }

update_safe

public function update_safe($table, $data, $where, $only_sql = false, $extra = [])

Alias of update() with data auto-escape.

    public function update_safe($table, $data, $where, $only_sql = false, $extra = [])
    {
        $data = $this->_fix_data_safe($table, $data, $extra);
        return $this->update($table, $this->_es_unless_builder_escapes($data), $where, $only_sql);
    }

utils

public function utils()
    public function utils()
    {
        if ( ! isset($this->utils)) {
            $cname = 'db_utils_' . $this->get_driver_family();
            $this->utils = _class($cname, 'classes/db/');
            $this->utils->db = $this;
        }
        return $this->utils;
    }

Methods starting with _ hooks and helpers; not routable as page actions

__call

public function __call($name, $args)

Catch missing method call.

    public function __call($name, $args)
    {
        return main()->extend_call($this, $name, $args);
    }

__construct

public function __construct($db_type = '', $db_prefix = null, $db_replication_slave = null)

Constructor.

    public function __construct($db_type = '', $db_prefix = null, $db_replication_slave = null)
    {
        global $DEBUG;

        $this->_load_tables_with_sys_prefix();
        // Type/driver of database server
        $this->DB_TYPE = ! empty($db_type) ? $db_type : DB_TYPE;
        if ( ! defined('DB_PREFIX') && empty($db_prefix)) {
            define('DB_PREFIX', '');
        }
        $this->DB_PREFIX = ! empty($db_prefix) ? $db_prefix : DB_PREFIX;
        // Check if this is primary database connection
        $debug_index = is_array($DEBUG['db_instances'] ?? null) ? count($DEBUG['db_instances']) : 0;
        if ($debug_index < 1) {
            $this->IS_PRIMARY_CONNECTION = true;
        } else {
            $this->IS_PRIMARY_CONNECTION = false;
        }
        // Trying to override replication slave setting
        if (isset($db_replication_slave)) {
            $this->DB_REPLICATION_SLAVE = (bool) $db_replication_slave;
        } elseif ($this->IS_PRIMARY_CONNECTION && defined('DB_REPLICATION_SLAVE')) {
            $this->DB_REPLICATION_SLAVE = (bool) DB_REPLICATION_SLAVE;
        }
        // Track db class instances
        $DEBUG['db_instances'][$debug_index] = &$this;
        if (defined('DEBUG_MODE') && DEBUG_MODE) {
            $DEBUG['db_instances_trace'][$debug_index] = $this->_trace_string();
        }
    }
public function _add_shutdown_query($sql = '')

Add query to shutdown array.

    public function _add_shutdown_query($sql = '')
    {
        if (empty($sql) || strlen($sql) < 5) {
            return false;
        }
        // If shutdown execution is disabled - then execute this query immediatelly
        if ( ! $this->USE_SHUTDOWN_QUERIES) {
            return $this->query($sql);
        }
        // Add query to the array
        $this->_SHUTDOWN_QUERIES[] = $sql;

        return true;
    }

_driver_hook

public function _driver_hook($method, array $args, $fallback)

Ask the connected driver (yf_db_driver hooks); $fallback when there is no driver object or it lacks the hook.

  • $args array
    public function _driver_hook($method, array $args, $fallback)
    {
        if (is_object($this->db) && method_exists($this->db, $method)) {
            return $this->db->$method(...$args);
        }
        return $fallback;
    }
public function _es_unless_builder_escapes($data)

*_safe() pre-escape only while the builder does not, otherwise values would be escaped twice.

    public function _es_unless_builder_escapes($data)
    {
        return $this->es_for_builder($data);
    }
public function _escape_table_name($name = '')
    public function _escape_table_name($name = '')
    {
        $name = trim($name);
        if ( ! strlen($name)) {
            return false;
        }
        $db = '';
        $table = '';
        if (strpos($name, '.') !== false) {
            list($db, $table) = explode('.', $name);
            $db = trim($db);
            $table = trim($table);
        } else {
            $table = $name;
        }
        if ( ! strlen($table)) {
            return false;
        }
        $table = $this->_fix_table_name($table);
        return (strlen($db) ? $this->escape_key($db) . '.' : '') . $this->escape_key($table);
    }
public function _execute_shutdown_queries()

Execute shutdown queries.

    public function _execute_shutdown_queries()
    {
        if ( ! $this->USE_SHUTDOWN_QUERIES || isset($this->_shutdown_executed)) {
            return false;
        }
        foreach ((array) $this->_SHUTDOWN_QUERIES as $sql) {
            if (is_string($sql) && strlen($sql) > 5) {
                $this->query($sql);
            }
        }
        // Prevent executing this method more than once
        $this->_shutdown_executed = true;
    }
public function _fix_data_safe($table, $data = [], $extra = [])
    public function _fix_data_safe($table, $data = [], $extra = [])
    {
        if ( ! $this->FIX_DATA_SAFE) {
            return $data;
        }
        $cols = $this->get_table_columns_cached($table, $extra['no_cache'] ?? null);
        if ( ! $cols) {
            $msg = __CLASS__ . '->' . __FUNCTION__ . ': columns for table ' . $table . ' is empty, truncating data array';
            if ( ! ($extra['silent'] ?? null) && ! $this->FIX_DATA_SAFE_SILENT) {
                trigger_error($msg, E_USER_WARNING);
            }
            return false;
        }
        $is_data_3d = false;
        // Try to check if array is two-dimensional
        foreach ((array) $data as $cur_row) {
            $is_data_3d = is_array($cur_row) ? 1 : 0;
            break;
        }
        $not_existing_cols = [];
        $virtual_cols = [];
        $fixed_nulls = [];
        if ($is_data_3d) {
            foreach ((array) $data as $k => $_data) {
                foreach ((array) $_data as $name => $v) {
                    if ( ! isset($cols[$name])) {
                        $not_existing_cols[$name] = $name;
                        unset($data[$k][$name]);
                    } elseif (isset($cols[$name]['virtual']) && $cols[$name]['virtual']) {
                        $virtual_cols[$name] = $name;
                        unset($data[$k][$name]);
                    } elseif (($v === null || $v === 'NULL') && ! $cols[$name]['nullable']) {
                        $fixed_nulls[$name] = $name;
                        unset($data[$k][$name]);
                    }
                }
            }
        } else {
            foreach ((array) $data as $name => $v) {
                if ( ! isset($cols[$name])) {
                    $not_existing_cols[$name] = $name;
                    unset($data[$name]);
                } elseif (isset($cols[$name]['virtual']) && $cols[$name]['virtual']) {
                    $virtual_cols[$name] = $name;
                    unset($data[$name]);
                } elseif (($v === null || $v === 'NULL') && ! $cols[$name]['nullable']) {
                    $fixed_nulls[$name] = $name;
                    unset($data[$name]);
                }
            }
        }
        if ( ! ($extra['silent'] ?? null) && ! $this->FIX_DATA_SAFE_SILENT) {
            if ($not_existing_cols) {
                trigger_error(__CLASS__ . '->' . __FUNCTION__ . ': not existing columns for table "' . $table . '", columns: ' . implode(', ', $not_existing_cols), E_USER_NOTICE);
            }
            if ($virtual_cols) {
                trigger_error(__CLASS__ . '->' . __FUNCTION__ . ': removed virtual columns for table "' . $table . '", columns: ' . implode(', ', $virtual_cols), E_USER_NOTICE);
            }
            if ($fixed_nulls) {
                trigger_error(__CLASS__ . '->' . __FUNCTION__ . ': fixed nulls for table "' . $table . '", columns: ' . implode(', ', $fixed_nulls), E_USER_NOTICE);
            }
        }
        return $data;
    }
public function _fix_table_name($name = '')

Try to fix table name.

    public function _fix_table_name($name = '')
    {
        $name = trim($name);
        if ( ! strlen($name)) {
            return false;
        }
        $db = '';
        $table = '';
        if (strpos($name, '.') !== false) {
            list($db, $name) = explode('.', $name);
            $db = trim($db);
            $name = trim($name);
        }
        if ( ! strlen($name)) {
            return '';
        }
        if (substr($name, 0, strlen('dbt_')) == 'dbt_') {
            $name = substr($name, strlen('dbt_'));
        }
        $name_wo_db_prefix = $name;
        $plen = strlen($this->DB_PREFIX);
        if ($plen && substr($name, 0, $plen) === $this->DB_PREFIX) {
            $name_wo_db_prefix = substr($name, $plen);
        }
        return ($db ? $db . '.' : '') . $this->DB_PREFIX . (in_array($name_wo_db_prefix, $this->_need_sys_prefix) ? 'sys_' : '') . $name_wo_db_prefix;
    }
public function _get_connect_params()
    public function _get_connect_params()
    {
        return [
            'host' => $this->DB_HOST,
            'user' => $this->DB_USER,
            'pswd' => $this->DB_PSWD,
            'name' => $this->DB_NAME,
            'persist' => $this->DB_PERSIST,
            'ssl' => $this->DB_SSL,
            'port' => $this->DB_PORT,
            'socket' => $this->DB_SOCKET,
            'charset' => $this->DB_CHARSET,
            'allow_auto_create_db' => $this->ALLOW_AUTO_CREATE_DB,
        ];
    }
public function _get_debug_item($name = '')

Get debug item value.

    public function _get_debug_item($name = '')
    {
        if ( ! $this->INSTRUMENT_QUERIES) {
            return '';
        }
        return $this->_instrument_items[$name];
    }
public function _get_reconnect_lock_path()

Get reconnect lock file name.

    public function _get_reconnect_lock_path()
    {
        $pairs = [
            '[DB_HOST]' => $this->DB_HOST,
            '[DB_NAME]' => $this->DB_NAME,
            '[DB_USER]' => $this->DB_USER,
            '[DB_PORT]' => $this->DB_PORT,
        ];
        return STORAGE_PATH . str_replace(array_keys($pairs), array_values($pairs), $this->RECONNECT_LOCK_FILE_NAME);
    }
public function _get_unique_tmp_table_name()

Create unique temporary table name.

    public function _get_unique_tmp_table_name()
    {
        return $this->DB_PREFIX . 'tmp__' . substr(abs(crc32(rand() . microtime(true))), 0, 8);
    }

_init

public function _init()

Framework constructor.

    public function _init()
    {
        // Perform auto-connection to db if needed
        if (($this->AUTO_CONNECT || MAIN_TYPE == 'admin') && ! $this->NO_AUTO_CONNECT) {
            $this->connect();
        }
        $this->_set_debug_items();
        if (main()->is_console()) {
            $this->enable_silent_mode();
        }
        // Set shutdown function
        if ($this->USE_SHUTDOWN_QUERIES) {
            register_shutdown_function([&$this, '_execute_shutdown_queries']);
        }
        if ($this->LOG_ALL_QUERIES || $this->LOG_SLOW_QUERIES) {
            register_shutdown_function([$this, '_log_queries']);
        }
        // Turn off tables repairing if we are dealing with slave server
        if ($this->DB_REPLICATION_SLAVE) {
            $this->ERROR_AUTO_REPAIR = false;
        }
    }
public function _instrument_query($sql = '', $keys = ['request_id', 'session_id', 'SESSION_user_id', 'GET_object', 'GET_action'])

Add instrumentation info to the query for highload SQL debug and profile.

    public function _instrument_query($sql = '', $keys = ['request_id', 'session_id', 'SESSION_user_id', 'GET_object', 'GET_action'])
    {
        $header = '';
        if ( ! $sql) {
            return '';
        }
        $trace = trace($skip_before = 3, $skip_after = 2);
        $trace = str_replace(["\t", "\n", "\0"], '', $trace);
        $header = '-- ' . $trace . "\t";
        foreach ((array) $keys as $x => $key) {
            $val = $this->_get_debug_item($key);
            if ( ! $val) {
                continue;
            }
            $val = str_replace(["\t", "\n", "\0"], '', $val);
            // all other chars are safe in comments
            $key = strtolower(str_replace([': ', "\t", "\n", "\0"], '', $key));
            // Add the requested instrumentation keys
            $header .= "\t" . $key . ': ' . $this->es($val);
        }
        return $header . PHP_EOL . $sql;
    }
public function _load_tables_with_sys_prefix()
    public function _load_tables_with_sys_prefix()
    {
        if ($this->_need_sys_prefix) {
            return $this->_need_sys_prefix;
        }
        $paths = [
            'app' => APP_PATH . 'share/db_sys_prefix_tables.php',
            'yf' => YF_PATH . 'plugins/db/share/db_sys_prefix_tables.php',
        ];
        $data = [];
        foreach ($paths as $path) {
            if (file_exists($path)) {
                $_data = require $path;
                if ($_data && is_array($_data)) {
                    $data += $_data;
                }
            }
        }
        $this->_need_sys_prefix = $data;
        return (array) $data;
    }

_log_queries

public function _log_queries()

Do Log.

    public function _log_queries()
    {
        // Restore startup working directory
        @chdir(main()->_CWD);

        if ( ! isset($this->_queries_logged)) {
            $this->_queries_logged = true;
        } else {
            return false;
        }
        _class_safe('logs')->store_db_queries_log();
    }

_model_load

public function _model_load($name)

Load new model object.

    public function _model_load($name)
    {
        $main = main();
        $model_class = $name . '_model';
        $custom_storages = &$main->_custom_class_storages;
        $wildcard = '*_model';
        if ( ! isset($custom_storages[$wildcard]['yf'])) {
            $yf_models_basic_storages = [
                'project_app_path' => [APP_PATH . 'models/'],
                'project_plugins' => [PROJECT_PATH . 'plugins/*/share/models/'],
                'project' => [PROJECT_PATH . 'share/models/'],
                'yf_plugins' => [YF_PATH . 'plugins/*/share/models/'],
                'yf' => [YF_PATH . 'share/models/'],
            ];
            foreach ($yf_models_basic_storages as $k => $v) {
                if ( ! isset($custom_storages[$wildcard][$k])) {
                    $custom_storages[$wildcard][$k] = $v;
                }
            }
        }
        $obj = _class_safe($model_class);
        // Special case to use preloaded models, try to load class name without postfix *_model
        if ( ! is_object($obj) || ! ($obj instanceof yf_model)) {
            $obj = _class_safe($name);
        }
        if ( ! is_object($obj) || ! ($obj instanceof yf_model)) {
            throw new Exception('Not able to load model: ' . $name);
            return false;
        }
        $model_obj = clone $obj;
        $model_obj->set_db_object($this);
        return $model_obj;
    }
public function _mysql_escape_mimic($string)
    public function _mysql_escape_mimic($string)
    {
        if (is_array($string)) {
            return array_map([$this, __FUNCTION__], $string);
        }
        if ($string === null || $string === 'NULL') {
            return 'NULL';
        } elseif (is_float($string)) {
            return str_replace(',', '.', $string);
        } elseif (is_int($string)) {
            return $string;
        } elseif (is_bool($string)) {
            return (int) $string;
        }
        return str_replace(['\\', "\0", "\n", "\r", "'", '"', "\x1a"], ['\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'], $string);
    }

_query_log

public function _query_log($sql, $_trace = [], $db_error = false)
    public function _query_log($sql, $_trace = [], $db_error = false)
    {
        $_log_allowed = true;
        // Save memory on high number of query log entries
        if ($this->LOGGED_QUERIES_LIMIT && count((array) $this->_LOG) >= $this->LOGGED_QUERIES_LIMIT) {
            $_log_allowed = false;
        }
        if ( ! $_log_allowed) {
            return false;
        }
        $warnings = null;
        $info = null;
        $this->_LOG[] = [
            'sql' => $sql,
            'rows' => '',
            'insert_id' => '',
            'error' => $db_error,
            'info' => $info,
            'time' => '',
            'trace' => $_trace,
        ];
        return count((array) $this->_LOG) - 1;
    }
public function _query_show_error($sql, $db_error, $_trace = '')
    public function _query_show_error($sql, $db_error, $_trace = '')
    {
        $old_db_error = $db_error;
        $db_error = $this->db->error();
        if (empty($db_error) || empty($db_error['message'])) {
            $db_error = $old_db_error;
        }
        $msg = 'DB: QUERY ERROR: ' . $sql . ';' . PHP_EOL . 'CAUSE: ' . ($db_error['message'] ?? '')
            . (($db_error['code'] ?? null) ? ' (code:' . $db_error['code'] . ')' : '')
            . (($db_error['offset'] ?? null) ? ' (offset:' . $db_error['offset'] . ')' : '')
            . (main()->USE_CUSTOM_ERRORS ? '' : $_trace . PHP_EOL);
        trigger_error($msg, E_USER_WARNING);
    }

_real_name

public function _real_name($name)

Get real table name from its short variant.

    public function _real_name($name)
    {
        $name = trim($name);
        if ( ! strlen($name)) {
            return false;
        }
        $db = '';
        $table = '';
        if (strpos($name, '.') !== false) {
            list($db, $table) = explode('.', $name);
            $db = trim($db);
            $table = trim($table);
        } else {
            $table = $name;
        }
        if (isset($this->_found_tables[$name])) {
            return $this->_found_tables[$name];
        }
        $name = (in_array($name, $this->_need_sys_prefix) ? 'sys_' : '') . $name;
        $plen = strlen($this->DB_PREFIX);
        if ($plen && substr($name, 0, $plen) !== $this->DB_PREFIX) {
            return ($db ? $db . '.' : '') . $this->DB_PREFIX . $name;
        }
        return ($db ? $db . '.' : '') . $name;
    }

_repair_table

public function _repair_table($sql, $db_error)

Trying to repair given table structure (and possibly data).

    public function _repair_table($sql, $db_error)
    {
        if (empty($db_error) || ! $this->ERROR_AUTO_REPAIR) {
            return false;
        }
        $driver_family = $this->get_driver_family();
        $code = $db_error['code'];
        $repairable_codes = $this->_driver_hook('repairable_error_codes', [], $driver_family === 'mysql' ? $this->REPAIRABLE_MYSQL_ERRORS : null);
        if (is_array($repairable_codes) && ! in_array($code, $repairable_codes)) {
            return false;
        }
        return _class('db_installer_' . $driver_family, 'classes/db/')->repair($sql, $db_error, $this);
    }
public function _save_query_revision($method, $table, $params = [])
    public function _save_query_revision($method, $table, $params = [])
    {
        if (($allowed_methods = $this->QUERY_REVISIONS_METHODS)) {
            if ( ! in_array($method, $allowed_methods)) {
                return false;
            }
        }
        if (($allowed_tables = $this->QUERY_REVISIONS_TABLES)) {
            if ( ! in_array($table, $allowed_tables)) {
                return false;
            }
        }
        $to_insert = [
            'date' => date('Y-m-d H:i:s'),
            'data_new' => is_array($params['data']) ? 'json:' . json_encode($params['data']) : (string) $params['data'],
            'data_old' => is_array($params['data_old']) ? 'json:' . json_encode($params['data_old']) : (string) $params['data_old'],
            'data_diff' => is_array($params['data_diff']) ? 'json:' . json_encode($params['data_diff']) : (string) $params['data_diff'],
            'user_id' => main()->ADMIN_ID,
            'user_group' => main()->ADMIN_GROUP,
            'site_id' => conf('SITE_ID'),
            'server_id' => conf('SERVER_ID'),
            'ip' => common()->get_ip(),
            'query_method' => $method,
            'query_table' => $table,
            'extra' => json_encode([
                'get_object' => ($_GET['object'] ?? null),
                'get_action' => ($_GET['action'] ?? null),
                'get_id' => ($_GET['id'] ?? null),
                'get_page' => ($_GET['page'] ?? null),
                'trace' => array_slice(explode(PHP_EOL, main()->trace_string()), 1, 5),
            ]),
            'url' => (main()->is_https() ? 'https://' : 'http://') . ($_SERVER['HTTP_HOST'] ?? null) . ($_SERVER['REQUEST_URI'] ?? null),
        ];
        $sql = $this->insert_safe('sys_db_revisions', $to_insert, $only_sql = true);
        $this->_add_shutdown_query($sql);
    }
public function _set_connect_params($db_host = '', $db_user = '', $db_pswd = null, $db_name = null, $force = false, $params = [])
    public function _set_connect_params($db_host = '', $db_user = '', $db_pswd = null, $db_name = null, $force = false, $params = [])
    {
        if (is_array($db_host)) {
            $params = $db_host;
            $db_host = '';
        }
        if ( ! is_array($params)) {
            $params = [];
        }
        if (($params['reconnect'] ?? false) || ($params['force'] ?? false)) {
            $force = true;
        }
        $this->DB_HOST = ($params['host'] ?? $db_host) ?: (defined('DB_HOST') ? DB_HOST : 'localhost');
        $this->DB_USER = ($params['user'] ?? $db_user) ?: (defined('DB_USER') ? DB_USER : 'root');
        // db_pswd can be empty string
        $_db_pswd = $params['pswd'] ?? $db_pswd;
        $this->DB_PSWD = $_db_pswd ?? (defined('DB_PSWD') ? DB_PSWD : '');
        // db_name can be empty string - means we working in special mode, just connecting to server
        $_db_name = $params['name'] ?? $db_name;
        $this->DB_NAME = $_db_name ?? (defined('DB_NAME') ? DB_NAME : '');
        $this->DB_PORT = $params['port'] ?? (defined('DB_PORT') ? constant('DB_PORT') : '');
        $this->DB_SOCKET = $params['socket'] ?? (defined('DB_SOCKET') ? constant('DB_SOCKET') : '');
        $this->DB_SSL = $params['ssl'] ?? (defined('DB_SSL') ? constant('DB_SSL') : false);
        $this->DB_CHARSET = $params['charset'] ?? (defined('DB_CHARSET') ? constant('DB_CHARSET') : '');
        if (isset($params['prefix'])) {
            $this->DB_PREFIX = $params['prefix'];
        }
        $allow_auto_create_db = $params['auto_create_db'] ?? false;
        if ($allow_auto_create_db !== null) {
            $this->ALLOW_AUTO_CREATE_DB = $allow_auto_create_db;
        }
    }
public function _set_debug_items()

Special init for the debug info items.

    public function _set_debug_items()
    {
        if ( ! $this->INSTRUMENT_QUERIES) {
            return false;
        }
        $cpu_usage = function_exists('getrusage') ? getrusage() : [];
        $ip = common()->get_ip();
        $this->_instrument_items = [
            'memory_usage' => function_exists('memory_get_usage') ? memory_get_usage() : '',
            'cpu_user' => $cpu_usage['ru_utime.tv_sec'] * 1e6 + $cpu_usage['ru_utime.tv_usec'],
            'cpu_system' => $cpu_usage['ru_stime.tv_sec'] * 1e6 + $cpu_usage['ru_stime.tv_usec'],
            'get_object' => ($_GET['object'] ?? null),
            'get_action' => ($_GET['action'] ?? null),
            'get_id' => ($_GET['id'] ?? null),
            'get_page' => ($_GET['page'] ?? null),
            'user_id' => ($_SESSION['user_id'] ?? null),
            'user_group' => ($_SESSION['user_group'] ?? null),
            'session_id' => session_id(),
            'request_id' => md5(($_SERVER['REMOTE_PORT'] ?? null) . $ip . ($_SERVER['REQUEST_URI'] ?? null) . microtime(true)),
            'request_method' => ($_SERVER['REQUEST_METHOD'] ?? null),
            'request_uri' => ($_SERVER['REQUEST_URI'] ?? null),
            'http_host' => ($_SERVER['HTTP_HOST'] ?? null),
            'ip' => $ip,
        ];
        return true;
    }
public function _supports_upsert()

REPLACE / INSERT ... ON DUPLICATE KEY UPDATE: native on mysql, compiled to ON CONFLICT by the pgsql builder.

    public function _supports_upsert()
    {
        return (bool) $this->_driver_hook('supports_upsert', [], in_array($this->get_driver_family(), ['mysql', 'pgsql']));
    }

_trace

public function _trace()

Simple trace without dumping whole objects.

    public function _trace()
    {
        $trace = [];
        foreach (debug_backtrace() as $k => $v) {
            if ( ! $k) {
                continue;
            }
            $v['object'] = isset($v['object']) && is_object($v['object']) ? get_class($v['object']) : null;
            $trace[$k - 1] = $v;
        }
        return $trace;
    }

_trace_string

public function _trace_string()

Print nice.

    public function _trace_string()
    {
        $e = new Exception();
        return implode(PHP_EOL, array_slice(explode(PHP_EOL, $e->getTraceAsString()), 1, -1));
    }
public function _update_query_log($log_id, $result, $query_time_start = 0, $warnings = null, $info = null)
    public function _update_query_log($log_id, $result, $query_time_start = 0, $warnings = null, $info = null)
    {
        if ( ! isset($this->_LOG[$log_id])) {
            return false;
        }
        $log = &$this->_LOG[$log_id];
        $time = (float) microtime(true) - (float) $query_time_start;
        $sql = $log['sql'];
        $rows = null;
        if ($this->GATHER_AFFECTED_ROWS && $result) {
            $_sql_type = strtoupper(rtrim(substr(ltrim($sql), 0, 7)));
            if (substr($_sql_type, 0, 4) === 'SHOW') {
                $_sql_type = 'SHOW';
            }
            $rows = null;
            $rows = $this->_last_affected_rows;
        }
        $log['time'] = $time;
        $log['rows'] = $rows;
        $log['warning'] = $warnings;
        $log['info'] = $info;
        $log['insert_id'] = $this->_last_insert_id;
    }