Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F1032626
db_connection.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, Jun 19, 3:09 AM
Size
31 KB
Mime Type
text/x-php
Expires
Sat, Jun 21, 3:09 AM (6 m, 6 s)
Engine
blob
Format
Raw Data
Handle
667174
Attached To
rINP In-Portal
db_connection.php
View Options
<?php
/**
* @version $Id: db_connection.php 16758 2023-11-13 08:43:02Z alex $
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined
(
'FULL_PATH'
)
or
die
(
'restricted access!'
);
/**
* Multi database connection class
*
*/
class
kDBConnection
extends
kBase
implements
IDBConnection
{
/**
* Created connection handle
*
* @var mysqli
* @access protected
*/
protected
$connectionID
;
/**
* Remembers, that database connection was opened successfully
*
* @var bool
* @access public
*/
public
$connectionOpened
=
false
;
/**
* Connection parameters, that were used
*
* @var Array
* @access protected
*/
protected
$connectionParams
=
Array
(
'host'
=>
''
,
'user'
=>
''
,
'pass'
=>
''
,
'db'
=>
''
);
/**
* Index of database server
*
* @var int
* @access protected
*/
protected
$serverIndex
=
0
;
/**
* Handle of currently processed recordset
*
* @var mysqli_result
* @access protected
*/
protected
$queryID
=
null
;
/**
* Function to handle sql errors
*
* @var callable
*/
protected
$errorHandler
=
''
;
/**
* Error code
*
* @var int
* @access protected
*/
protected
$errorCode
=
0
;
/**
* Error message
*
* @var string
* @access protected
*/
protected
$errorMessage
=
''
;
/**
* Defines if database connection
* operations should generate debug
* information
*
* @var bool
* @access public
*/
public
$debugMode
=
false
;
/**
* Save query execution statistics
*
* @var bool
* @access protected
*/
protected
$_captureStatistics
=
false
;
/**
* Last query to database
*
* @var string
* @access public
*/
public
$lastQuery
=
''
;
/**
* Total processed queries count
*
* @var int
* @access protected
*/
protected
$_queryCount
=
0
;
/**
* Total time, used for serving queries
*
* @var Array
* @access protected
*/
protected
$_queryTime
=
0
;
/**
* Indicates, that next database query could be cached, when memory caching is enabled
*
* @var bool
* @access public
*/
public
$nextQueryCachable
=
false
;
/**
* The "no debugging" state of the SQL queries.
*
* @var boolean
*/
public
$noDebuggingState
=
false
;
/**
* For backwards compatibility with kDBLoadBalancer class
*
* @var bool
* @access public
*/
public
$nextQueryFromMaster
=
false
;
/**
* Initializes connection class with
* db type to used in future
*
* @param string $db_type
* @param string $error_handler
* @param int $server_index
* @access public
*/
public
function
__construct
(
$db_type
,
$error_handler
=
''
,
$server_index
=
0
)
{
if
(
class_exists
(
'kApplication'
)
)
{
// prevents "Fatal Error" on 2nd installation step (when database is empty)
parent
::
__construct
();
}
$this
->
serverIndex
=
$server_index
;
if
(
!
$error_handler
)
{
$this
->
setErrorHandler
(
array
(&
$this
,
'handleError'
));
}
else
{
$this
->
setErrorHandler
(
$error_handler
);
}
$this
->
_captureStatistics
=
defined
(
'DBG_CAPTURE_STATISTICS'
)
&&
DBG_CAPTURE_STATISTICS
&&
!(
defined
(
'ADMIN'
)
&&
ADMIN
);
}
/**
* Set's custom error
*
* @param int $code
* @param string $msg
* @access protected
*/
protected
function
setError
(
$code
,
$msg
)
{
$this
->
errorCode
=
$code
;
$this
->
errorMessage
=
$msg
;
}
/**
* Checks if previous query execution raised an error.
*
* @return bool
* @access public
*/
public
function
hasError
()
{
return
$this
->
errorCode
!=
0
;
}
/**
* Try to connect to database server using specified parameters and set database to $db if connection made.
*
* @param string $host
* @param string $user
* @param string $pass
* @param string $db
* @param bool $retry
*
* @return bool
* @access public
* @throws RuntimeException When connection failed.
*/
public
function
Connect
(
$host
,
$user
,
$pass
,
$db
,
$retry
=
false
)
{
$this
->
connectionParams
=
Array
(
'host'
=>
$host
,
'user'
=>
$user
,
'pass'
=>
$pass
,
'db'
=>
$db
);
$this
->
setError
(
0
,
''
);
// reset error
$this
->
connectionID
=
mysqli_connect
(
$host
,
$user
,
$pass
,
$db
);
$this
->
errorCode
=
mysqli_connect_errno
();
if
(
is_object
(
$this
->
connectionID
)
)
{
if
(
defined
(
'DBG_SQL_MODE'
)
)
{
$this
->
Query
(
'SET SQL_MODE = "'
.
DBG_SQL_MODE
.
'"'
);
}
if
(
defined
(
'SQL_COLLATION'
)
&&
defined
(
'SQL_CHARSET'
)
)
{
$this
->
Query
(
'SET NAMES
\'
'
.
SQL_CHARSET
.
'
\'
COLLATE
\'
'
.
SQL_COLLATION
.
'
\'
'
);
}
if
(
!
$this
->
hasError
()
)
{
$this
->
connectionOpened
=
true
;
return
true
;
}
}
$this
->
errorMessage
=
mysqli_connect_error
();
$error_msg
=
'Database connection failed, please check your connection settings.<br/>Error ('
.
$this
->
errorCode
.
'): '
.
$this
->
errorMessage
;
if
(
(
defined
(
'IS_INSTALL'
)
&&
IS_INSTALL
)
||
$retry
)
{
trigger_error
(
$error_msg
,
E_USER_WARNING
);
}
else
{
$this
->
Application
->
redirectToMaintenance
();
throw
new
RuntimeException
(
$error_msg
);
}
$this
->
connectionOpened
=
false
;
return
false
;
}
/**
* Checks if connection to database is opened.
*
* @return bool
* @access public
*/
public
function
connectionOpened
()
{
return
$this
->
connectionOpened
;
}
/**
* Setups the connection according given configuration.
*
* @param Array $config
* @return bool
* @access public
*/
public
function
setup
(
$config
)
{
if
(
is_object
(
$this
->
Application
)
)
{
$this
->
debugMode
=
$this
->
Application
->
isDebugMode
();
}
return
$this
->
Connect
(
$config
[
'Database'
][
'DBHost'
],
$config
[
'Database'
][
'DBUser'
],
$config
[
'Database'
][
'DBUserPassword'
],
$config
[
'Database'
][
'DBName'
]
);
}
/**
* Performs 3 reconnect attempts in case if connection to a DB was lost in the middle of script run (e.g. server restart)
*
* @return bool
* @access protected
*/
protected
function
ReConnect
()
{
$retry_count
=
0
;
$connected
=
false
;
$this
->
connectionID
->
close
();
while
(
$retry_count
<
3
)
{
sleep
(
5
);
// wait 5 seconds before each reconnect attempt
$connected
=
$this
->
Connect
(
$this
->
connectionParams
[
'host'
],
$this
->
connectionParams
[
'user'
],
$this
->
connectionParams
[
'pass'
],
$this
->
connectionParams
[
'db'
],
true
);
if
(
$connected
)
{
break
;
}
$retry_count
++;
}
return
$connected
;
}
/**
* Shows error message from previous operation
* if it failed
*
* @param string $sql
* @param string $key_field
* @param boolean|null $no_debug
* @return bool
* @access protected
*/
protected
function
showError
(
$sql
=
''
,
$key_field
=
null
,
$no_debug
=
null
)
{
static
$retry_count
=
0
;
if
(
$no_debug
===
null
)
{
$no_debug
=
$this
->
noDebuggingState
;
}
if
(
!
is_object
(
$this
->
connectionID
)
)
{
// no connection while doing mysql_query
$this
->
errorCode
=
mysqli_connect_errno
();
if
(
$this
->
hasError
()
)
{
$this
->
errorMessage
=
mysqli_connect_error
();
$ret
=
$this
->
callErrorHandler
(
$sql
);
if
(!
$ret
)
{
exit
;
}
}
return
false
;
}
// checking if there was an error during last mysql_query
$this
->
errorCode
=
$this
->
connectionID
->
errno
;
if
(
$this
->
hasError
()
)
{
$this
->
errorMessage
=
$this
->
connectionID
->
error
;
$ret
=
$this
->
callErrorHandler
(
$sql
);
if
(
(
$this
->
errorCode
==
2006
||
$this
->
errorCode
==
2013
)
&&
(
$retry_count
<
3
)
)
{
// #2006 - MySQL server has gone away
// #2013 - Lost connection to MySQL server during query
$retry_count
++;
if
(
$this
->
ReConnect
()
)
{
return
$this
->
Query
(
$sql
,
$key_field
,
$no_debug
);
}
}
if
(!
$ret
)
{
exit
;
}
}
else
{
$retry_count
=
0
;
}
return
false
;
}
/**
* Sends db error to a predefined error handler
*
* @param $sql
* @return bool
* @access protected
*/
protected
function
callErrorHandler
(
$sql
)
{
return
call_user_func
(
$this
->
errorHandler
,
$this
->
errorCode
,
$this
->
errorMessage
,
$sql
);
}
/**
* Default error handler for sql errors
*
* @param int $code
* @param string $msg
* @param string $sql
* @return bool
* @access public
*/
public
function
handleError
(
$code
,
$msg
,
$sql
)
{
echo
'<strong>Processing SQL</strong>: '
.
$sql
.
'<br/>'
;
echo
'<strong>Error ('
.
$code
.
'):</strong> '
.
$msg
.
'<br/>'
;
return
false
;
}
/**
* Returns first field of first line of recordset if query ok or false otherwise.
*
* @param string $sql
* @param int $offset
* @return string
* @access public
*/
public
function
GetOne
(
$sql
,
$offset
=
0
)
{
$row
=
$this
->
GetRow
(
$sql
,
$offset
);
if
(
!
$row
)
{
return
false
;
}
return
array_shift
(
$row
);
}
/**
* Returns first row of recordset if query ok, false otherwise.
*
* @param string $sql
* @param int $offset
* @return Array
* @access public
*/
public
function
GetRow
(
$sql
,
$offset
=
0
)
{
$sql
.=
' '
.
$this
->
getLimitClause
(
$offset
,
1
);
$ret
=
$this
->
Query
(
$sql
);
if
(
!
$ret
)
{
return
false
;
}
return
array_shift
(
$ret
);
}
/**
* Returns 1st column of recordset as one-dimensional array or false otherwise.
*
* Optional parameter $key_field can be used to set field name to be used as resulting array key.
*
* @param string $sql
* @param string $key_field
* @return Array
* @access public
*/
public
function
GetCol
(
$sql
,
$key_field
=
null
)
{
$rows
=
$this
->
Query
(
$sql
);
if
(
!
$rows
)
{
return
$rows
;
}
$i
=
0
;
$row_count
=
count
(
$rows
);
$ret
=
Array
();
if
(
isset
(
$key_field
)
)
{
while
(
$i
<
$row_count
)
{
$ret
[
$rows
[
$i
][
$key_field
]]
=
array_shift
(
$rows
[
$i
]);
$i
++;
}
}
else
{
while
(
$i
<
$row_count
)
{
$ret
[]
=
array_shift
(
$rows
[
$i
]);
$i
++;
}
}
return
$ret
;
}
/**
* Returns iterator for 1st column of a recordset or false in case of error.
*
* Optional parameter $key_field can be used to set field name to be used as resulting array key.
*
* @param string $sql
* @param string $key_field
* @return bool|kMySQLQueryCol
*/
public
function
GetColIterator
(
$sql
,
$key_field
=
null
)
{
return
$this
->
GetIterator
(
$sql
,
$key_field
,
false
,
'kMySQLQueryCol'
);
}
/**
* Queries db with $sql query supplied and returns rows selected if any, false otherwise.
*
* Optional parameter $key_field allows to set one of the query fields value as key in string array.
*
* @param string $sql
* @param string $key_field
* @param boolean|null $no_debug
* @return Array
* @access public
*/
public
function
Query
(
$sql
,
$key_field
=
null
,
$no_debug
=
null
)
{
if
(
$no_debug
===
null
)
{
$no_debug
=
$this
->
noDebuggingState
;
}
if
(
!
$no_debug
)
{
$this
->
_queryCount
++;
}
$this
->
lastQuery
=
$sql
;
// set 1st checkpoint: begin
$start_time
=
$this
->
_captureStatistics
?
microtime
(
true
)
:
0
;
// set 1st checkpoint: end
$this
->
setError
(
0
,
''
);
// reset error
$this
->
queryID
=
$this
->
connectionID
->
query
(
$sql
);
if
(
is_object
(
$this
->
queryID
)
)
{
$ret
=
Array
();
if
(
isset
(
$key_field
)
)
{
while
(
$row
=
$this
->
queryID
->
fetch_assoc
()
)
{
$ret
[
$row
[
$key_field
]]
=
$row
;
}
}
else
{
while
(
$row
=
$this
->
queryID
->
fetch_assoc
()
)
{
$ret
[]
=
$row
;
}
}
$this
->
Destroy
();
// set 2nd checkpoint: begin
if
(
$this
->
_captureStatistics
)
{
$query_time
=
microtime
(
true
)
-
$start_time
;
if
(
$query_time
>
DBG_MAX_SQL_TIME
&&
!
$no_debug
)
{
$this
->
Application
->
logSlowQuery
(
$sql
,
$query_time
);
}
$this
->
_queryTime
+=
$query_time
;
}
// set 2nd checkpoint: end
return
$ret
;
}
else
{
// set 2nd checkpoint: begin
if
(
$this
->
_captureStatistics
)
{
$this
->
_queryTime
+=
microtime
(
true
)
-
$start_time
;
}
// set 2nd checkpoint: end
}
return
$this
->
showError
(
$sql
,
$key_field
,
$no_debug
);
}
/**
* Returns iterator to a recordset, produced from running $sql query.
*
* Queries db with $sql query supplied and returns kMySQLQuery iterator or false in case of error.
* Optional parameter $key_field allows to set one of the query fields value as key in string array.
*
* @param string $sql
* @param string $key_field
* @param boolean|null $no_debug
* @param string $iterator_class
* @return kMySQLQuery|bool
* @access public
*/
public
function
GetIterator
(
$sql
,
$key_field
=
null
,
$no_debug
=
null
,
$iterator_class
=
'kMySQLQuery'
)
{
if
(
$no_debug
===
null
)
{
$no_debug
=
$this
->
noDebuggingState
;
}
if
(
!
$no_debug
)
{
$this
->
_queryCount
++;
}
$this
->
lastQuery
=
$sql
;
// set 1st checkpoint: begin
$start_time
=
$this
->
_captureStatistics
?
microtime
(
true
)
:
0
;
// set 1st checkpoint: end
$this
->
setError
(
0
,
''
);
// reset error
$this
->
queryID
=
$this
->
connectionID
->
query
(
$sql
);
if
(
is_object
(
$this
->
queryID
)
)
{
/** @var kMySQLQuery $ret */
$ret
=
new
$iterator_class
(
$this
->
queryID
,
$key_field
);
// set 2nd checkpoint: begin
if
(
$this
->
_captureStatistics
)
{
$query_time
=
microtime
(
true
)
-
$start_time
;
if
(
$query_time
>
DBG_MAX_SQL_TIME
&&
!
$no_debug
)
{
$this
->
Application
->
logSlowQuery
(
$sql
,
$query_time
);
}
$this
->
_queryTime
+=
$query_time
;
}
// set 2nd checkpoint: end
return
$ret
;
}
else
{
// set 2nd checkpoint: begin
if
(
$this
->
_captureStatistics
)
{
$this
->
_queryTime
+=
microtime
(
true
)
-
$start_time
;
}
// set 2nd checkpoint: end
}
return
$this
->
showError
(
$sql
,
$key_field
,
$no_debug
);
}
/**
* Free memory used to hold recordset handle.
*
* @access public
*/
public
function
Destroy
()
{
$this
->
queryID
->
free
();
unset
(
$this
->
queryID
);
}
/**
* Performs sql query, that will change database content.
*
* @param string $sql
* @return bool
* @access public
*/
public
function
ChangeQuery
(
$sql
)
{
$this
->
Query
(
$sql
);
return
!
$this
->
hasError
();
}
/**
* Returns auto increment field value from insert like operation if any, zero otherwise.
*
* @return int
* @access public
*/
public
function
getInsertID
()
{
return
$this
->
connectionID
->
insert_id
;
}
/**
* Returns row count affected by last query.
*
* @return int
* @access public
*/
public
function
getAffectedRows
()
{
return
$this
->
connectionID
->
affected_rows
;
}
/**
* Returns LIMIT sql clause part for specific db.
*
* @param int $offset
* @param int $rows
* @return string
* @access public
*/
public
function
getLimitClause
(
$offset
,
$rows
)
{
if
(
!(
$rows
>
0
)
)
{
return
''
;
}
return
'LIMIT '
.
$offset
.
','
.
$rows
;
}
/**
* If it's a string, adds quotes and backslashes. Otherwise returns as-is.
*
* @param mixed $string
* @return string
* @access public
*/
public
function
qstr
(
$string
)
{
if
(
is_null
(
$string
)
)
{
return
'NULL'
;
}
# This will also quote numeric values. This should be harmless,
# and protects against weird problems that occur when they really
# _are_ strings such as article titles and string->number->string
# conversion is not 1:1.
return
"'"
.
$this
->
connectionID
->
real_escape_string
(
$string
)
.
"'"
;
}
/**
* Calls "qstr" function for each given array element.
*
* @param Array $array
* @param string $function
* @return Array
*/
public
function
qstrArray
(
$array
,
$function
=
'qstr'
)
{
return
array_map
(
Array
(&
$this
,
$function
),
$array
);
}
/**
* Escapes string.
*
* @param mixed $string
* @return string
* @access public
*/
public
function
escape
(
$string
)
{
if
(
is_null
(
$string
)
)
{
return
'NULL'
;
}
$string
=
$this
->
connectionID
->
real_escape_string
(
$string
);
// prevent double-escaping of MySQL wildcard symbols ("%" and "_") in case if they were already escaped
return
str_replace
(
Array
(
'
\\\\
%'
,
'
\\\\
_'
),
Array
(
'
\\
%'
,
'
\\
_'
),
$string
);
}
/**
* Returns last error code occurred.
*
* @return int
* @access public
*/
public
function
getErrorCode
()
{
return
$this
->
errorCode
;
}
/**
* Returns last error message.
*
* @return string
* @access public
*/
public
function
getErrorMsg
()
{
return
$this
->
errorMessage
;
}
/**
* Performs insert of given data (useful with small number of queries)
* or stores it to perform multiple insert later (useful with large number of queries).
*
* @param Array $fields_hash
* @param string $table
* @param string $type
* @param bool $insert_now
* @return bool
* @access public
*/
public
function
doInsert
(
$fields_hash
,
$table
,
$type
=
'INSERT'
,
$insert_now
=
true
)
{
static
$value_sqls
=
Array
();
if
(
$insert_now
)
{
$fields_sql
=
'`'
.
implode
(
'`,`'
,
array_keys
(
$fields_hash
))
.
'`'
;
}
$values_sql
=
''
;
foreach
(
$fields_hash
as
$field_name
=>
$field_value
)
{
$values_sql
.=
$this
->
qstr
(
$field_value
)
.
','
;
}
// don't use preg here, as it may fail when string is too long
$value_sqls
[]
=
rtrim
(
$values_sql
,
','
);
$insert_result
=
true
;
if
(
$insert_now
)
{
$insert_count
=
count
(
$value_sqls
);
if
((
$insert_count
>
1
)
&&
(
$value_sqls
[
$insert_count
-
1
]
==
$value_sqls
[
$insert_count
-
2
]))
{
// last two records are the same
array_pop
(
$value_sqls
);
}
$sql
=
strtoupper
(
$type
)
.
' INTO '
.
$table
.
' ('
.
$fields_sql
.
') VALUES ('
.
implode
(
'),('
,
$value_sqls
)
.
')'
;
// Reset before query to prevent repeated call from error handler to insert 2 records instead of 1.
$value_sqls
=
array
();
$insert_result
=
$this
->
ChangeQuery
(
$sql
);
}
return
$insert_result
;
}
/**
* Update given field values to given record using $key_clause.
*
* @param Array $fields_hash
* @param string $table
* @param string $key_clause
* @return bool
* @access public
*/
public
function
doUpdate
(
$fields_hash
,
$table
,
$key_clause
)
{
if
(!
$fields_hash
)
return
true
;
$fields_sql
=
''
;
foreach
(
$fields_hash
as
$field_name
=>
$field_value
)
{
$fields_sql
.=
'`'
.
$field_name
.
'` = '
.
$this
->
qstr
(
$field_value
)
.
','
;
}
// don't use preg here, as it may fail when string is too long
$fields_sql
=
rtrim
(
$fields_sql
,
','
);
$sql
=
'UPDATE '
.
$table
.
' SET '
.
$fields_sql
.
' WHERE '
.
$key_clause
;
return
$this
->
ChangeQuery
(
$sql
);
}
/**
* Allows to detect table's presence in database.
*
* @param string $table_name
* @param bool $force
* @return bool
* @access public
*/
public
function
TableFound
(
$table_name
,
$force
=
false
)
{
static
$table_found
=
false
;
if
(
$table_found
===
false
)
{
$table_found
=
array_flip
(
$this
->
GetCol
(
'SHOW TABLES'
));
}
if
(
!
preg_match
(
'/^'
.
preg_quote
(
TABLE_PREFIX
,
'/'
)
.
'(.*)/'
,
$table_name
)
)
{
$table_name
=
TABLE_PREFIX
.
$table_name
;
}
if
(
$force
)
{
if
(
$this
->
Query
(
'SHOW TABLES LIKE '
.
$this
->
qstr
(
$table_name
))
)
{
$table_found
[
$table_name
]
=
1
;
}
else
{
unset
(
$table_found
[
$table_name
]);
}
}
return
isset
(
$table_found
[
$table_name
]);
}
/**
* Returns query processing statistics.
*
* @return Array
* @access public
*/
public
function
getQueryStatistics
()
{
return
Array
(
'time'
=>
$this
->
_queryTime
,
'count'
=>
$this
->
_queryCount
);
}
/**
* Get status information from SHOW STATUS in an associative array.
*
* @param string $which
* @return Array
* @access public
*/
public
function
getStatus
(
$which
=
'%'
)
{
$status
=
Array
();
$records
=
$this
->
Query
(
'SHOW STATUS LIKE "'
.
$which
.
'"'
);
foreach
(
$records
as
$record
)
{
$status
[
$record
[
'Variable_name'
]
]
=
$record
[
'Value'
];
}
return
$status
;
}
/**
* Get slave replication lag. It will only work if the DB user has the PROCESS privilege.
*
* @return int
* @access public
*/
public
function
getSlaveLag
()
{
try
{
$rows
=
$this
->
Query
(
'SHOW SLAVE STATUS'
);
}
catch
(
RuntimeException
$e
)
{
// When "SUPER" or "REPLICATION CLIENT" permission is missing.
return
0
;
}
// On the silenced error OR database server isn't configured for a replication.
if
(
$rows
===
false
||
count
(
$rows
)
!==
1
)
{
return
0
;
}
$row
=
reset
(
$rows
);
// When slave is too busy catching up with a master we'll get a NULL/empty string here.
return
is_numeric
(
$row
[
'Seconds_Behind_Master'
])
?
$row
[
'Seconds_Behind_Master'
]
:
false
;
}
/**
* Sets an error handler.
*
* @param callable $error_handler Error handler.
*
* @return void
*/
public
function
setErrorHandler
(
callable
$error_handler
)
{
$this
->
errorHandler
=
$error_handler
;
}
}
class
kDBConnectionDebug
extends
kDBConnection
{
protected
$_profileSQLs
=
false
;
/**
* Info about this database connection to show in debugger report
*
* @var string
* @access protected
*/
protected
$serverInfoLine
=
''
;
/**
* Initializes connection class with
* db type to used in future
*
* @param string $db_type
* @param string $error_handler
* @param int $server_index
* @access public
*/
public
function
__construct
(
$db_type
,
$error_handler
=
''
,
$server_index
=
0
)
{
parent
::
__construct
(
$db_type
,
$error_handler
,
$server_index
);
$this
->
_profileSQLs
=
defined
(
'DBG_SQL_PROFILE'
)
&&
DBG_SQL_PROFILE
;
}
/**
* Try to connect to database server
* using specified parameters and set
* database to $db if connection made
*
* @param string $host
* @param string $user
* @param string $pass
* @param string $db
* @param bool $force_new
* @param bool $retry
* @return bool
* @access public
*/
public
function
Connect
(
$host
,
$user
,
$pass
,
$db
,
$force_new
=
false
,
$retry
=
false
)
{
if
(
defined
(
'DBG_SQL_SERVERINFO'
)
&&
DBG_SQL_SERVERINFO
)
{
$this
->
serverInfoLine
=
$this
->
serverIndex
.
' ('
.
$host
.
')'
;
}
return
parent
::
Connect
(
$host
,
$user
,
$pass
,
$db
,
$force_new
,
$retry
);
}
/**
* Queries db with $sql query supplied and returns rows selected if any, false otherwise.
*
* Optional parameter $key_field allows to set one of the query fields value as key in string array.
*
* @param string $sql
* @param string $key_field
* @param boolean|null $no_debug
* @return Array
* @access public
*/
public
function
Query
(
$sql
,
$key_field
=
null
,
$no_debug
=
null
)
{
if
(
$no_debug
===
null
)
{
$no_debug
=
$this
->
noDebuggingState
;
}
if
(
$no_debug
)
{
return
parent
::
Query
(
$sql
,
$key_field
,
$no_debug
);
}
global
$debugger
;
$this
->
_queryCount
++;
$this
->
lastQuery
=
$sql
;
// set 1st checkpoint: begin
if
(
$this
->
_profileSQLs
)
{
$queryID
=
$debugger
->
generateID
();
$debugger
->
profileStart
(
'sql_'
.
$queryID
,
$debugger
->
formatSQL
(
$sql
));
}
// set 1st checkpoint: end
$this
->
setError
(
0
,
''
);
// reset error
$this
->
queryID
=
$this
->
connectionID
->
query
(
$sql
);
if
(
is_object
(
$this
->
queryID
)
)
{
$ret
=
Array
();
if
(
isset
(
$key_field
)
)
{
while
(
$row
=
$this
->
queryID
->
fetch_assoc
()
)
{
$ret
[
$row
[
$key_field
]]
=
$row
;
}
}
else
{
while
(
$row
=
$this
->
queryID
->
fetch_assoc
()
)
{
$ret
[]
=
$row
;
}
}
// set 2nd checkpoint: begin
if
(
$this
->
_profileSQLs
)
{
$current_element
=
current
(
$ret
);
$first_cell
=
count
(
$ret
)
==
1
&&
count
(
$current_element
)
==
1
?
current
(
$current_element
)
:
null
;
if
(
strlen
(
$first_cell
)
>
200
)
{
$first_cell
=
substr
(
$first_cell
,
0
,
50
)
.
' ...'
;
}
$debugger
->
profileFinish
(
'sql_'
.
$queryID
,
null
,
null
,
$this
->
getAffectedRows
(),
$first_cell
,
$this
->
_queryCount
,
$this
->
nextQueryCachable
,
$this
->
serverInfoLine
);
$debugger
->
profilerAddTotal
(
'sql'
,
'sql_'
.
$queryID
);
$this
->
nextQueryCachable
=
false
;
}
// set 2nd checkpoint: end
$this
->
Destroy
();
return
$ret
;
}
else
{
// set 2nd checkpoint: begin
if
(
$this
->
_profileSQLs
)
{
$debugger
->
profileFinish
(
'sql_'
.
$queryID
,
null
,
null
,
$this
->
getAffectedRows
(),
null
,
$this
->
_queryCount
,
$this
->
nextQueryCachable
,
$this
->
serverInfoLine
);
$debugger
->
profilerAddTotal
(
'sql'
,
'sql_'
.
$queryID
);
$this
->
nextQueryCachable
=
false
;
}
// set 2nd checkpoint: end
}
return
$this
->
showError
(
$sql
,
$key_field
);
}
/**
* Returns iterator to a recordset, produced from running $sql query.
*
* Queries db with $sql query supplied and returns kMySQLQuery iterator or false in case of error.
* Optional parameter $key_field allows to set one of the query fields value as key in string array.
*
* @param string $sql
* @param string $key_field
* @param boolean|null $no_debug
* @param string $iterator_class
* @return kMySQLQuery|bool
* @access public
*/
public
function
GetIterator
(
$sql
,
$key_field
=
null
,
$no_debug
=
null
,
$iterator_class
=
'kMySQLQuery'
)
{
if
(
$no_debug
===
null
)
{
$no_debug
=
$this
->
noDebuggingState
;
}
if
(
$no_debug
)
{
return
parent
::
GetIterator
(
$sql
,
$key_field
,
$no_debug
,
$iterator_class
);
}
global
$debugger
;
$this
->
_queryCount
++;
$this
->
lastQuery
=
$sql
;
// set 1st checkpoint: begin
if
(
$this
->
_profileSQLs
)
{
$queryID
=
$debugger
->
generateID
();
$debugger
->
profileStart
(
'sql_'
.
$queryID
,
$debugger
->
formatSQL
(
$sql
));
}
// set 1st checkpoint: end
$this
->
setError
(
0
,
''
);
// reset error
$this
->
queryID
=
$this
->
connectionID
->
query
(
$sql
);
if
(
is_object
(
$this
->
queryID
)
)
{
/** @var kMySQLQuery $ret */
$ret
=
new
$iterator_class
(
$this
->
queryID
,
$key_field
);
// set 2nd checkpoint: begin
if
(
$this
->
_profileSQLs
)
{
$current_row
=
$ret
->
current
();
if
(
count
(
$ret
)
==
1
&&
$ret
->
fieldCount
()
==
1
)
{
if
(
is_array
(
$current_row
)
)
{
$first_cell
=
current
(
$current_row
);
}
else
{
$first_cell
=
$current_row
;
}
}
else
{
$first_cell
=
null
;
}
if
(
strlen
(
$first_cell
)
>
200
)
{
$first_cell
=
substr
(
$first_cell
,
0
,
50
)
.
' ...'
;
}
$debugger
->
profileFinish
(
'sql_'
.
$queryID
,
null
,
null
,
$this
->
getAffectedRows
(),
$first_cell
,
$this
->
_queryCount
,
$this
->
nextQueryCachable
,
$this
->
serverInfoLine
);
$debugger
->
profilerAddTotal
(
'sql'
,
'sql_'
.
$queryID
);
$this
->
nextQueryCachable
=
false
;
}
// set 2nd checkpoint: end
return
$ret
;
}
else
{
// set 2nd checkpoint: begin
if
(
$this
->
_profileSQLs
)
{
$debugger
->
profileFinish
(
'sql_'
.
$queryID
,
null
,
null
,
$this
->
getAffectedRows
(),
null
,
$this
->
_queryCount
,
$this
->
nextQueryCachable
,
$this
->
serverInfoLine
);
$debugger
->
profilerAddTotal
(
'sql'
,
'sql_'
.
$queryID
);
$this
->
nextQueryCachable
=
false
;
}
// set 2nd checkpoint: end
}
return
$this
->
showError
(
$sql
,
$key_field
);
}
}
class
kMySQLQuery
implements
Iterator
,
Countable
,
SeekableIterator
{
/**
* Current index in recordset
*
* @var int
* @access protected
*/
protected
$position
=
-
1
;
/**
* Query resource
*
* @var mysqli_result
* @access protected
*/
protected
$result
;
/**
* Field to act as key in a resulting array
*
* @var string
* @access protected
*/
protected
$keyField
=
null
;
/**
* Data in current row of recordset
*
* @var Array
* @access protected
*/
protected
$rowData
=
Array
();
/**
* Row count in a result
*
* @var int
* @access protected
*/
protected
$rowCount
=
0
;
/**
* Creates new instance of a class
*
* @param mysqli_result $result
* @param null|string $key_field
*/
public
function
__construct
(
mysqli_result
$result
,
$key_field
=
null
)
{
$this
->
result
=
$result
;
$this
->
keyField
=
$key_field
;
$this
->
rowCount
=
$this
->
result
->
num_rows
;
$this
->
rewind
();
}
/**
* Moves recordset pointer to first element
*
* @return void
* @access public
* @implements Iterator::rewind
*/
public
function
rewind
()
{
$this
->
seek
(
0
);
}
/**
* Returns value at current position
*
* @return mixed
* @access public
* @implements Iterator::current
*/
function
current
()
{
return
$this
->
rowData
;
}
/**
* Returns key at current position
*
* @return mixed
* @access public
* @implements Iterator::key
*/
function
key
()
{
return
$this
->
keyField
?
$this
->
rowData
[
$this
->
keyField
]
:
$this
->
position
;
}
/**
* Moves recordset pointer to next position
*
* @return void
* @access public
* @implements Iterator::next
*/
function
next
()
{
$this
->
seek
(
$this
->
position
+
1
);
}
/**
* Detects if current position is within recordset bounds
*
* @return bool
* @access public
* @implements Iterator::valid
*/
public
function
valid
()
{
return
$this
->
position
<
$this
->
rowCount
;
}
/**
* Counts recordset rows
*
* @return int
* @access public
* @implements Countable::count
*/
public
function
count
()
{
return
$this
->
rowCount
;
}
/**
* Counts fields in current row
*
* @return int
* @access public
*/
public
function
fieldCount
()
{
return
count
(
$this
->
rowData
);
}
/**
* Moves cursor into given position within recordset
*
* @param int $position
* @throws OutOfBoundsException
* @access public
* @implements SeekableIterator::seek
*/
public
function
seek
(
$position
)
{
if
(
$this
->
position
==
$position
)
{
return
;
}
$this
->
position
=
$position
;
if
(
$this
->
valid
()
)
{
$this
->
result
->
data_seek
(
$this
->
position
);
$this
->
rowData
=
$this
->
result
->
fetch_assoc
();
}
/*if ( !$this->valid() ) {
throw new OutOfBoundsException('Invalid seek position (' . $position . ')');
}*/
}
/**
* Returns first recordset row
*
* @return Array
* @access public
*/
public
function
first
()
{
$this
->
seek
(
0
);
return
$this
->
rowData
;
}
/**
* Closes recordset and freese memory
*
* @return void
* @access public
*/
public
function
close
()
{
$this
->
result
->
free
();
unset
(
$this
->
result
);
}
/**
* Frees memory when object is destroyed
*
* @return void
* @access public
*/
public
function
__destruct
()
{
$this
->
close
();
}
/**
* Returns all keys
*
* @return Array
* @access public
*/
public
function
keys
()
{
$ret
=
Array
();
foreach
(
$this
as
$key
=>
$value
)
{
$ret
[]
=
$key
;
}
return
$ret
;
}
/**
* Returns all values
*
* @return Array
* @access public
*/
public
function
values
()
{
$ret
=
Array
();
foreach
(
$this
as
$value
)
{
$ret
[]
=
$value
;
}
return
$ret
;
}
/**
* Returns whole recordset as array
*
* @return Array
* @access public
*/
public
function
toArray
()
{
$ret
=
Array
();
foreach
(
$this
as
$key
=>
$value
)
{
$ret
[
$key
]
=
$value
;
}
return
$ret
;
}
}
class
kMySQLQueryCol
extends
kMySQLQuery
{
/**
* Returns value at current position
*
* @return mixed
* @access public
* @implements Iterator::current
*/
function
current
()
{
return
reset
(
$this
->
rowData
);
}
/**
* Returns first column of first recordset row
*
* @return string
* @access public
*/
public
function
first
()
{
$this
->
seek
(
0
);
return
reset
(
$this
->
rowData
);
}
}
Event Timeline
Log In to Comment