yf_db classDatabase abstraction layer.
| File | plugins/db/classes/yf_db.class.php |
|---|---|
| Use | _class('db') |
A site sets them in its config with $PROJECT_CONF['db']['NAME'] = ...;
| Property | Default | Description |
|---|---|---|
$ALLOW_AUTO_CREATE_DBbool |
false |
Needed for installation and repairing process |
$ALLOW_CACHE_QUERIESbool |
false |
Allow to cache specified queries results |
$AUTO_CONNECTbool |
false |
Auto-connect on/off |
$CACHE_QUERIES_LIMITbool |
100 |
Max number of cached queries |
$CACHE_TABLE_NAMESbool |
false |
Use tables names caching |
$CONNECTION_REQUIREDbool |
false |
Connection required or not (else E_USER_WARNING will be thrown not E_USER_ERROR) |
$DB_CHARSETstring |
'' |
|
$DB_DRIVERS_DIRstring |
'classes/db/' |
Folder where databases drivers are stored |
$DB_HOSTstring |
'' |
|
$DB_NAMEstring |
'' |
|
$DB_PERSISTbool |
false |
|
$DB_PORTint |
'' |
|
$DB_PREFIXstring |
null |
Tables names prefix |
$DB_PSWDstring |
'' |
|
$DB_REPLICATION_SLAVEbool |
false |
In case of true - we will try to avoid any data/structure modification queries to not break replication |
$DB_SOCKETstring |
'' |
|
$DB_SSLbool |
false |
|
$DB_TYPEstring |
'mysql' |
Type of database (default) |
$DB_USERstring |
'' |
|
$ERROR_AUTO_REPAIRbool |
true |
Auto-repairing on error (table not exists) on/off |
$ERROR_BACKTRACEbool |
true |
Use backtrace in error message |
$ESCAPE_BUILDER_VALUESbool |
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_ALLstring |
'db_queries.log' |
Log queries file name |
$FILE_NAME_LOG_SLOWstring |
'slow_queries.log' |
Log queries file name |
$FIX_DATA_SAFEbool |
true |
update_safe, insert_safe, update_batch_safe: use additional checking for exising table fields |
$FIX_DATA_SAFE_SILENTbool |
false |
Trigger to act silently or not in *_safe methods |
$GATHER_AFFECTED_ROWSbool |
true |
Gather affected rows stats (will be used only when DEBUG_MODE is enabled) |
$INSTRUMENT_QUERIESbool |
false |
Add additional engine details to the SQL as comment for later use |
$IS_PRIMARY_CONNECTION |
null |
|
$LOGGED_QUERIES_LIMITbool |
1000 |
Max number of logged queries (set to 0 to unlimited) |
$LOG_ALL_QUERIESbool |
false |
Store db queries to file |
$LOG_SLOW_QUERIESbool |
false |
Store db queries to file |
$NO_AUTO_CONNECT |
null |
|
$NO_CACHEbool |
false |
Switch caching on/off |
$NUM_QUERIESint |
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_REVISIONSbool |
false |
Use sql query revisions for update/insert/replace/delete |
$QUERY_REVISIONS_METHODS |
[] |
|
$QUERY_REVISIONS_TABLES |
[] |
|
$RECONNECT_CONSOLE_TRIESint |
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_DELAYint |
1 |
Time to wait between reconnects (in seconds) |
$RECONNECT_DELAY_LOG_INCbool |
1 |
Use logarithmic increase or reconnect time |
$RECONNECT_LOCK_FILE_NAMEstring |
'db_cannot_connect_[DB_HOST]_[DB_NAME]_[DB_USER]_[DB_PORT]'... |
|
$RECONNECT_LOCK_TIMEOUTint |
30 |
Time in seconds between unlock reconnect |
$RECONNECT_MYSQL_ERRORSarray |
|
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_TRIESint |
3 |
Num tries to reconnect in common mode (will be useful if db server is overloaded) (Set to '0' for disabling) |
$RECONNECT_USE_LOCKINGbool |
false |
Use locking for reconnects or not |
$REMOVED_DRIVERSarray |
[] |
DB_TYPE values deleted in yf 3.9: name => reason, a configured one warns before the usual "wrong driver" failure |
$REPAIRABLE_MYSQL_ERRORSarray |
[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_INFObool |
true |
Currently only in DEBUG_MODE |
$SHOW_QUERY_WARNINGSbool |
true |
Currently only in DEBUG_MODE |
$SLOW_QUERIES_TIME_LIMITfloat |
0.20000000000000001 |
|
$SPLIT_WHERE_VALUES_BY_COMMAbool |
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_CACHEbool |
false |
Adding SQL_NO_CACHE to SELECT queries: useful to find long running queries |
$TABLE_NAMES_CACHE_TTLint |
3600 |
Tables cache lifetime (while developing need to be short) (else need to be very large) |
$USE_QUERY_BACKTRACEbool |
true |
Use backtrace to get where query was called from (will be used only when DEBUG_MODE is enabled) |
$USE_SHUTDOWN_QUERIESbool |
true |
Allow to use shutdown queries or not |
$_LOGarray |
[] |
Query log array |
$_SHUTDOWN_QUERIES |
[] |
|
$_connect_start_time |
null |
|
$_connectedbool |
false |
@conf_skip Internal var (default value) |
$_connection_time |
null |
|
$_db_results_cache |
[] |
|
$_found_tables |
[] |
|
$_innodb_has_fulltextmixed |
null |
@conf_skip Set by yf_db_installer_mysql::innodb_has_fulltext() |
$_instrument_itemsarray |
[] |
|
$_last_affected_rows |
null |
|
$_last_failed_query_error |
null |
|
$_last_insert_id |
null |
|
$_last_query_error |
null |
|
$_need_sys_prefixarray |
[] |
Filled automatically from generated file |
$_queries_logged |
null |
|
$_repairs_by_sql |
[] |
|
$_shutdown_executed |
null |
|
$_tried_to_connectbool |
false |
@conf_skip Internal var (default value) |
$dbmixed |
null |
@conf_skip Driver instance |
$installer |
null |
|
$migrator |
null |
|
$utils |
null |
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();
}
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);
}
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();
}
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;
}
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();
}
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;
}
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;
}
public function error()
Return database error.
public function error()
{
if ( ! is_object($this->db)) {
return false;
}
return $this->db->error();
}
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);
}
public function escape($string)
Alias.
public function escape($string)
{
return $this->es($string);
}
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);
}
public function escape_string($string)
Alias.
public function escape_string($string)
{
return $this->es($string);
}
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);
}
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);
}
public function fetch($result)
Alias.
public function fetch($result)
{
return $this->fetch_assoc($result);
}
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);
}
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);
}
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);
}
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);
}
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);
}
public function from()
Query builder shortcut.
public function from()
{
return call_user_func_array([$this->query_builder(), __FUNCTION__], func_get_args());
}
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);
}
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;
}
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);
}
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;
}
public function get_host_info()
public function get_host_info()
{
if ( ! $this->_connected && ! $this->connect()) {
return false;
}
return $this->db->get_host_info();
}
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;
}
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);
}
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();
}
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);
}
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);
}
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;
}
public function is_ready()
public function is_ready()
{
return (bool) $this->_connected;
}
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.
}
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);
}
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);
}
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);
}
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;
}
public function model($name, $params = [])
ORM shortcut.
public function model($name, $params = [])
{
$model = $this->_model_load($name);
$params && $model->_set_params($params);
return $model;
}
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);
}
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);
}
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);
}
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;
}
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;
}
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);
}
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);
}
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);
}
public function rollback()
Rollback a transaction.
public function rollback()
{
if ( ! $this->_connected && ! $this->connect()) {
return false;
}
return $this->db->rollback();
}
public function select()
Query builder shortcut.
public function select()
{
return call_user_func_array([$this->query_builder(), __FUNCTION__], func_get_args());
}
public function split_sql($sql)
public function split_sql($sql)
{
return $this->utils()->split_sql($sql);
}
public function table()
Query builder shortcut.
public function table()
{
return call_user_func_array([$this->query_builder(), __FUNCTION__], func_get_args());
}
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);
}
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);
}
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);
}
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);
}
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;
}
_ hooks and helpers; not routable as page actionspublic function __call($name, $args)
Catch missing method call.
public function __call($name, $args)
{
return main()->extend_call($this, $name, $args);
}
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;
}
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);
}
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;
}
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();
}
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);
}
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);
}
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;
}
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']));
}
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;
}
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;
}