Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Q
question2answer
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
outils
question2answer
Commits
769b2d38
Commit
769b2d38
authored
Jan 12, 2017
by
Scott
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Coding style (qa-db.php)
parent
b507ca2f
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
630 additions
and
585 deletions
+630
-585
qa-db.php
qa-include/qa-db.php
+630
-585
No files found.
qa-include/qa-db.php
View file @
769b2d38
...
...
@@ -20,414 +20,440 @@
More about this license: http://www.question2answer.org/license.php
*/
if
(
!
defined
(
'QA_VERSION'
))
{
// don't allow this page to be requested directly from browser
header
(
'Location: ../'
);
exit
;
}
/**
* Indicates to the Q2A database layer that database connections are permitted fro this point forwards
* (before this point, some plugins may not have had a chance to override some database access functions).
*/
function
qa_db_allow_connect
()
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
global
$qa_db_allow_connect
;
$qa_db_allow_connect
=
true
;
}
/**
* Connect to the Q2A database, select the right database, optionally install the $failhandler (and call it if necessary).
* Uses mysqli as of Q2A 1.7.
*/
function
qa_db_connect
(
$failhandler
=
null
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
global
$qa_db_connection
,
$qa_db_fail_handler
,
$qa_db_allow_connect
;
if
(
!
defined
(
'QA_VERSION'
))
{
// don't allow this page to be requested directly from browser
header
(
'Location: ../'
);
exit
;
}
if
(
!
$qa_db_allow_connect
)
qa_fatal_error
(
'It appears that a plugin is trying to access the database, but this is not allowed until Q2A initialization is complete.'
);
if
(
isset
(
$failhandler
))
$qa_db_fail_handler
=
$failhandler
;
// set this even if connection already opened
/**
* Indicates to the Q2A database layer that database connections are permitted fro this point forwards
* (before this point, some plugins may not have had a chance to override some database access functions).
*/
function
qa_db_allow_connect
()
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
if
(
$qa_db_connection
instanceof
mysqli
)
return
;
global
$qa_db_allow_connect
;
$host
=
QA_FINAL_MYSQL_HOSTNAME
;
$port
=
null
;
$qa_db_allow_connect
=
true
;
}
if
(
defined
(
'QA_FINAL_WORDPRESS_INTEGRATE_PATH'
))
{
// Wordpress allows setting port inside DB_HOST constant, like 127.0.0.1:3306
$host_and_port
=
explode
(
':'
,
$host
);
if
(
count
(
$host_and_port
)
>=
2
)
{
$host
=
$host_and_port
[
0
];
$port
=
$host_and_port
[
1
];
}
}
elseif
(
defined
(
'QA_FINAL_MYSQL_PORT'
))
{
$port
=
QA_FINAL_MYSQL_PORT
;
}
if
(
QA_PERSISTENT_CONN_DB
)
$host
=
'p:'
.
$host
;
/**
* Connect to the Q2A database, select the right database, optionally install the $failhandler (and call it if necessary).
* Uses mysqli as of Q2A 1.7.
* @param null $failhandler
* @return mixed|void
*/
function
qa_db_connect
(
$failhandler
=
null
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
// in mysqli we connect and select database in constructor
if
(
$port
!==
null
)
$db
=
new
mysqli
(
$host
,
QA_FINAL_MYSQL_USERNAME
,
QA_FINAL_MYSQL_PASSWORD
,
QA_FINAL_MYSQL_DATABASE
,
$port
);
else
$db
=
new
mysqli
(
$host
,
QA_FINAL_MYSQL_USERNAME
,
QA_FINAL_MYSQL_PASSWORD
,
QA_FINAL_MYSQL_DATABASE
);
global
$qa_db_connection
,
$qa_db_fail_handler
,
$qa_db_allow_connect
;
// must use procedural `mysqli_connect_error` here prior to 5.2.9
$conn_error
=
mysqli_connect_error
();
if
(
$conn_error
)
qa_db_fail_error
(
'connect'
,
$db
->
connect_errno
,
$conn_error
);
if
(
!
$qa_db_allow_connect
)
qa_fatal_error
(
'It appears that a plugin is trying to access the database, but this is not allowed until Q2A initialization is complete.'
);
// From Q2A 1.5, we explicitly set the character encoding of the MySQL connection, instead of using lots of "SELECT BINARY col"-style queries.
// Testing showed that overhead is minimal, so this seems worth trading off against the benefit of more straightforward queries, especially
// for plugin developers.
if
(
!
$db
->
set_charset
(
'utf8'
))
qa_db_fail_error
(
'set_charset'
,
$db
->
errno
,
$db
->
error
);
if
(
isset
(
$failhandler
))
$qa_db_fail_handler
=
$failhandler
;
// set this even if connection already opened
qa_report_process_stage
(
'db_connected'
);
$qa_db_connection
=
$db
;
}
if
(
$qa_db_connection
instanceof
mysqli
)
return
;
$host
=
QA_FINAL_MYSQL_HOSTNAME
;
$port
=
null
;
/**
* If a DB error occurs, call the installed fail handler (if any) otherwise report error and exit immediately.
*/
function
qa_db_fail_error
(
$type
,
$errno
=
null
,
$error
=
null
,
$query
=
null
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
global
$qa_db_fail_handler
;
@
error_log
(
'PHP Question2Answer MySQL '
.
$type
.
' error '
.
$errno
.
': '
.
$error
.
(
isset
(
$query
)
?
(
' - Query: '
.
$query
)
:
''
));
if
(
function_exists
(
$qa_db_fail_handler
))
$qa_db_fail_handler
(
$type
,
$errno
,
$error
,
$query
);
else
{
echo
sprintf
(
'<hr><div style="color: red">Database %s<p>%s</p><code>%s</code></div>'
,
htmlspecialchars
(
$type
.
' error '
.
$errno
),
nl2br
(
htmlspecialchars
(
$error
)),
nl2br
(
htmlspecialchars
(
$query
))
);
qa_exit
(
'error'
);
if
(
defined
(
'QA_FINAL_WORDPRESS_INTEGRATE_PATH'
))
{
// Wordpress allows setting port inside DB_HOST constant, like 127.0.0.1:3306
$host_and_port
=
explode
(
':'
,
$host
);
if
(
count
(
$host_and_port
)
>=
2
)
{
$host
=
$host_and_port
[
0
];
$port
=
$host_and_port
[
1
];
}
}
elseif
(
defined
(
'QA_FINAL_MYSQL_PORT'
))
{
$port
=
QA_FINAL_MYSQL_PORT
;
}
/**
* Return the current connection to the Q2A database, connecting if necessary and $connect is true.
*/
function
qa_db_connection
(
$connect
=
true
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
global
$qa_db_connection
;
if
(
$connect
&&
!
(
$qa_db_connection
instanceof
mysqli
))
{
qa_db_connect
();
if
(
!
(
$qa_db_connection
instanceof
mysqli
))
qa_fatal_error
(
'Failed to connect to database'
);
}
return
$qa_db_connection
;
if
(
QA_PERSISTENT_CONN_DB
)
$host
=
'p:'
.
$host
;
// in mysqli we connect and select database in constructor
if
(
$port
!==
null
)
$db
=
new
mysqli
(
$host
,
QA_FINAL_MYSQL_USERNAME
,
QA_FINAL_MYSQL_PASSWORD
,
QA_FINAL_MYSQL_DATABASE
,
$port
);
else
$db
=
new
mysqli
(
$host
,
QA_FINAL_MYSQL_USERNAME
,
QA_FINAL_MYSQL_PASSWORD
,
QA_FINAL_MYSQL_DATABASE
);
// must use procedural `mysqli_connect_error` here prior to 5.2.9
$conn_error
=
mysqli_connect_error
();
if
(
$conn_error
)
qa_db_fail_error
(
'connect'
,
$db
->
connect_errno
,
$conn_error
);
// From Q2A 1.5, we explicitly set the character encoding of the MySQL connection, instead of using lots of "SELECT BINARY col"-style queries.
// Testing showed that overhead is minimal, so this seems worth trading off against the benefit of more straightforward queries, especially
// for plugin developers.
if
(
!
$db
->
set_charset
(
'utf8'
))
qa_db_fail_error
(
'set_charset'
,
$db
->
errno
,
$db
->
error
);
qa_report_process_stage
(
'db_connected'
);
$qa_db_connection
=
$db
;
}
/**
* If a DB error occurs, call the installed fail handler (if any) otherwise report error and exit immediately.
* @param $type
* @param int $errno
* @param string $error
* @param string $query
* @return mixed
*/
function
qa_db_fail_error
(
$type
,
$errno
=
null
,
$error
=
null
,
$query
=
null
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
global
$qa_db_fail_handler
;
@
error_log
(
'PHP Question2Answer MySQL '
.
$type
.
' error '
.
$errno
.
': '
.
$error
.
(
isset
(
$query
)
?
(
' - Query: '
.
$query
)
:
''
));
if
(
function_exists
(
$qa_db_fail_handler
))
$qa_db_fail_handler
(
$type
,
$errno
,
$error
,
$query
);
else
{
echo
sprintf
(
'<hr><div style="color: red">Database %s<p>%s</p><code>%s</code></div>'
,
htmlspecialchars
(
$type
.
' error '
.
$errno
),
nl2br
(
htmlspecialchars
(
$error
)),
nl2br
(
htmlspecialchars
(
$query
))
);
qa_exit
(
'error'
);
}
}
/**
* Disconnect from the Q2A databas
e.
*/
function
qa_db_disconnect
()
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
global
$qa_db_connection
;
/**
* Return the current connection to the Q2A database, connecting if necessary and $connect is tru
e.
* @param bool $connect
* @return mixed
*/
function
qa_db_connection
(
$connect
=
true
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
if
(
$qa_db_connection
instanceof
mysqli
)
{
qa_report_process_stage
(
'db_disconnect'
);
global
$qa_db_connection
;
if
(
!
QA_PERSISTENT_CONN_DB
)
{
if
(
!
$qa_db_connection
->
close
())
qa_fatal_error
(
'Database disconnect failed'
);
}
if
(
$connect
&&
!
(
$qa_db_connection
instanceof
mysqli
))
{
qa_db_connect
();
$qa_db_connection
=
null
;
}
if
(
!
(
$qa_db_connection
instanceof
mysqli
))
qa_fatal_error
(
'Failed to connect to database'
);
}
return
$qa_db_connection
;
}
/**
* Run the raw $query, call the global failure handler if necessary, otherwise return the result resource.
* If appropriate, also track the resources used by database queries, and the queries themselves, for performance debugging.
*/
function
qa_db_query_raw
(
$query
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
if
(
QA_DEBUG_PERFORMANCE
)
{
global
$qa_usage
;
/**
* Disconnect from the Q2A database.
*/
function
qa_db_disconnect
()
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
// time the query
$oldtime
=
array_sum
(
explode
(
' '
,
microtime
()));
$result
=
qa_db_query_execute
(
$query
);
$usedtime
=
array_sum
(
explode
(
' '
,
microtime
()))
-
$oldtime
;
global
$qa_db_connection
;
// fetch counts
$gotrows
=
$gotcolumns
=
null
;
if
(
$result
instanceof
mysqli_result
)
{
$gotrows
=
$result
->
num_rows
;
$gotcolumns
=
$result
->
field_count
;
}
$qa_usage
->
logDatabaseQuery
(
$query
,
$usedtime
,
$gotrows
,
$gotcolumns
);
}
else
$result
=
qa_db_query_execute
(
$query
);
// @error_log('Question2Answer MySQL query: '.$query);
if
(
$qa_db_connection
instanceof
mysqli
)
{
qa_report_process_stage
(
'db_disconnect'
);
if
(
$result
===
false
)
{
$db
=
qa_db_connection
();
qa_db_fail_error
(
'query'
,
$db
->
errno
,
$db
->
error
,
$query
);
if
(
!
QA_PERSISTENT_CONN_DB
)
{
if
(
!
$qa_db_connection
->
close
())
qa_fatal_error
(
'Database disconnect failed'
);
}
return
$result
;
$qa_db_connection
=
null
;
}
/**
* Lower-level function to execute a query, which automatically retries if there is a MySQL deadlock error.
*/
function
qa_db_query_execute
(
$query
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
$db
=
qa_db_connection
();
for
(
$attempt
=
0
;
$attempt
<
100
;
$attempt
++
)
{
$result
=
$db
->
query
(
$query
);
if
(
$result
===
false
&&
$db
->
errno
==
1213
)
usleep
(
10000
);
// deal with InnoDB deadlock errors by waiting 0.01s then retrying
else
break
;
}
/**
* Run the raw $query, call the global failure handler if necessary, otherwise return the result resource.
* If appropriate, also track the resources used by database queries, and the queries themselves, for performance debugging.
* @param $query
* @return mixed
*/
function
qa_db_query_raw
(
$query
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
if
(
QA_DEBUG_PERFORMANCE
)
{
global
$qa_usage
;
// time the query
$oldtime
=
array_sum
(
explode
(
' '
,
microtime
()));
$result
=
qa_db_query_execute
(
$query
);
$usedtime
=
array_sum
(
explode
(
' '
,
microtime
()))
-
$oldtime
;
// fetch counts
$gotrows
=
$gotcolumns
=
null
;
if
(
$result
instanceof
mysqli_result
)
{
$gotrows
=
$result
->
num_rows
;
$gotcolumns
=
$result
->
field_count
;
}
return
$result
;
}
$qa_usage
->
logDatabaseQuery
(
$query
,
$usedtime
,
$gotrows
,
$gotcolumns
)
;
}
else
$result
=
qa_db_query_execute
(
$query
);
/**
* Return $string escaped for use in queries to the Q2A database (to which a connection must have been made).
*/
function
qa_db_escape_string
(
$string
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
// @error_log('Question2Answer MySQL query: '.$query);
if
(
$result
===
false
)
{
$db
=
qa_db_connection
();
return
$db
->
real_escape_string
(
$string
);
qa_db_fail_error
(
'query'
,
$db
->
errno
,
$db
->
error
,
$query
);
}
return
$result
;
}
/**
* Return $argument escaped for MySQL. Add quotes around it if $alwaysquote is true or it's not numeric.
* If $argument is an array, return a comma-separated list of escaped elements, with or without $arraybrackets.
*/
function
qa_db_argument_to_mysql
(
$argument
,
$alwaysquote
,
$arraybrackets
=
false
)
{
if
(
is_array
(
$argument
))
{
$parts
=
array
();
foreach
(
$argument
as
$subargument
)
$parts
[]
=
qa_db_argument_to_mysql
(
$subargument
,
$alwaysquote
,
true
);
/**
* Lower-level function to execute a query, which automatically retries if there is a MySQL deadlock error.
* @param $query
* @return mixed
*/
function
qa_db_query_execute
(
$query
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
if
(
$arraybrackets
)
$result
=
'('
.
implode
(
','
,
$parts
)
.
')'
;
else
$result
=
implode
(
','
,
$parts
);
$db
=
qa_db_connection
();
}
elseif
(
isset
(
$argument
))
{
if
(
$alwaysquote
||
!
is_numeric
(
$argument
))
$result
=
"'"
.
qa_db_escape_string
(
$argument
)
.
"'"
;
else
$result
=
qa_db_escape_string
(
$argument
);
}
else
$result
=
'NULL'
;
for
(
$attempt
=
0
;
$attempt
<
100
;
$attempt
++
)
{
$result
=
$db
->
query
(
$query
);
return
$result
;
if
(
$result
===
false
&&
$db
->
errno
==
1213
)
usleep
(
10000
);
// deal with InnoDB deadlock errors by waiting 0.01s then retrying
else
break
;
}
return
$result
;
}
/**
* Return $string escaped for use in queries to the Q2A database (to which a connection must have been made).
* @param $string
* @return mixed
*/
function
qa_db_escape_string
(
$string
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
$db
=
qa_db_connection
();
return
$db
->
real_escape_string
(
$string
);
}
/**
* Return $argument escaped for MySQL. Add quotes around it if $alwaysquote is true or it's not numeric.
* If $argument is an array, return a comma-separated list of escaped elements, with or without $arraybrackets.
* @param $argument
* @param $alwaysquote
* @param bool $arraybrackets
* @return mixed|string
*/
function
qa_db_argument_to_mysql
(
$argument
,
$alwaysquote
,
$arraybrackets
=
false
)
{
if
(
is_array
(
$argument
))
{
$parts
=
array
();
foreach
(
$argument
as
$subargument
)
$parts
[]
=
qa_db_argument_to_mysql
(
$subargument
,
$alwaysquote
,
true
);
if
(
$arraybrackets
)
$result
=
'('
.
implode
(
','
,
$parts
)
.
')'
;
else
$result
=
implode
(
','
,
$parts
);
/**
* Return the full name (with prefix) of database table $rawname, usually if it used after a ^ symbol.
*/
function
qa_db_add_table_prefix
(
$rawname
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
$prefix
=
QA_MYSQL_TABLE_PREFIX
;
if
(
defined
(
'QA_MYSQL_USERS_PREFIX'
))
{
switch
(
strtolower
(
$rawname
))
{
case
'users'
:
case
'userlogins'
:
case
'userprofile'
:
case
'userfields'
:
case
'messages'
:
case
'cookies'
:
case
'blobs'
:
case
'cache'
:
case
'userlogins_ibfk_1'
:
// also special cases for constraint names
case
'userprofile_ibfk_1'
:
$prefix
=
QA_MYSQL_USERS_PREFIX
;
break
;
}
}
elseif
(
isset
(
$argument
))
{
if
(
$alwaysquote
||
!
is_numeric
(
$argument
))
$result
=
"'"
.
qa_db_escape_string
(
$argument
)
.
"'"
;
else
$result
=
qa_db_escape_string
(
$argument
);
}
else
$result
=
'NULL'
;
return
$result
;
}
/**
* Return the full name (with prefix) of database table $rawname, usually if it used after a ^ symbol.
* @param $rawname
* @return string
*/
function
qa_db_add_table_prefix
(
$rawname
)
{
if
(
qa_to_override
(
__FUNCTION__
))
{
$args
=
func_get_args
();
return
qa_call_override
(
__FUNCTION__
,
$args
);
}
$prefix
=
QA_MYSQL_TABLE_PREFIX
;
if
(
defined
(
'QA_MYSQL_USERS_PREFIX'
))
{
switch
(
strtolower
(
$rawname
))
{
case
'users'
:
case
'userlogins'
:
case
'userprofile'
:
case
'userfields'
:
case
'messages'
:
case
'cookies'
:
case
'blobs'
:
case
'cache'
:
case
'userlogins_ibfk_1'
:
// also special cases for constraint names
case
'userprofile_ibfk_1'
:
$prefix
=
QA_MYSQL_USERS_PREFIX
;
break
;
}
return
$prefix
.
$rawname
;
}
/**
* Callback function to add table prefixes, as used in qa_db_apply_sub().
*/
function
qa_db_prefix_callback
(
$matches
)
{
return
qa_db_add_table_prefix
(
$matches
[
1
]);
}
/**
* Substitute ^, $ and # symbols in $query. ^ symbols are replaced with the table prefix set in qa-config.php.
* $ and # symbols are replaced in order by the corresponding element in $arguments (if the element is an array,
* it is converted recursively into comma-separated list). Each element in $arguments is escaped.
* $ is replaced by the argument in quotes (even if it's a number), # only adds quotes if the argument is non-numeric.
* It's important to use $ when matching a textual column since MySQL won't use indexes to compare text against numbers.
*/
function
qa_db_apply_sub
(
$query
,
$arguments
)
{
$query
=
preg_replace_callback
(
'/\^([A-Za-z_0-9]+)/'
,
'qa_db_prefix_callback'
,
$query
);
if
(
!
is_array
(
$arguments
))
return
$query
;
$countargs
=
count
(
$arguments
);
$offset
=
0
;
for
(
$argument
=
0
;
$argument
<
$countargs
;
$argument
++
)
{
$stringpos
=
strpos
(
$query
,
'$'
,
$offset
);
$numberpos
=
strpos
(
$query
,
'#'
,
$offset
);
if
(
$stringpos
===
false
||
(
$numberpos
!==
false
&&
$numberpos
<
$stringpos
))
{
$alwaysquote
=
false
;
$position
=
$numberpos
;
}
else
{
$alwaysquote
=
true
;
$position
=
$stringpos
;
}
if
(
!
is_numeric
(
$position
))
qa_fatal_error
(
'Insufficient parameters in query: '
.
$query
);
$value
=
qa_db_argument_to_mysql
(
$arguments
[
$argument
],
$alwaysquote
);
$query
=
substr_replace
(
$query
,
$value
,
$position
,
1
);
$offset
=
$position
+
strlen
(
$value
);
// allows inserting strings which contain #/$ character
}
return
$prefix
.
$rawname
;
}
/**
* Callback function to add table prefixes, as used in qa_db_apply_sub().
* @param $matches
* @return string
*/
function
qa_db_prefix_callback
(
$matches
)
{
return
qa_db_add_table_prefix
(
$matches
[
1
]);
}
/**
* Substitute ^, $ and # symbols in $query. ^ symbols are replaced with the table prefix set in qa-config.php.
* $ and # symbols are replaced in order by the corresponding element in $arguments (if the element is an array,
* it is converted recursively into comma-separated list). Each element in $arguments is escaped.
* $ is replaced by the argument in quotes (even if it's a number), # only adds quotes if the argument is non-numeric.
* It's important to use $ when matching a textual column since MySQL won't use indexes to compare text against numbers.
* @param $query
* @param $arguments
* @return mixed
*/
function
qa_db_apply_sub
(
$query
,
$arguments
)
{
$query
=
preg_replace_callback
(
'/\^([A-Za-z_0-9]+)/'
,
'qa_db_prefix_callback'
,
$query
);
if
(
!
is_array
(
$arguments
))
return
$query
;
}
/**
* Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
*/
function
qa_db_query_sub
(
$query
)
// arguments for substitution retrieved using func_get_args()
{
$funcargs
=
func_get_args
();
return
qa_db_query_raw
(
qa_db_apply_sub
(
$query
,
array_slice
(
$funcargs
,
1
)));
}
/**
* Return the number of rows in $result. (Simple wrapper for mysqli_result::num_rows.)
*/
function
qa_db_num_rows
(
$result
)
{
if
(
$result
instanceof
mysqli_result
)
return
$result
->
num_rows
;
return
0
;
}
/**
* Return the value of the auto-increment column for the last inserted row.
*/
function
qa_db_last_insert_id
()
{
$db
=
qa_db_connection
();
return
$db
->
insert_id
;
}
/**
* Return the number of rows affected by the last query.
*/
function
qa_db_affected_rows
()
{
$db
=
qa_db_connection
();
return
$db
->
affected_rows
;
}
$countargs
=
count
(
$arguments
);
$offset
=
0
;
for
(
$argument
=
0
;
$argument
<
$countargs
;
$argument
++
)
{
$stringpos
=
strpos
(
$query
,
'$'
,
$offset
);
$numberpos
=
strpos
(
$query
,
'#'
,
$offset
);
/**
* For the previous INSERT ... ON DUPLICATE KEY UPDATE query, return whether an insert operation took place.
*/
function
qa_db_insert_on_duplicate_inserted
()
{
return
(
qa_db_affected_rows
()
==
1
)
;
}
if
(
$stringpos
===
false
||
(
$numberpos
!==
false
&&
$numberpos
<
$stringpos
))
{
$alwaysquote
=
false
;
$position
=
$numberpos
;
}
else
{
$alwaysquote
=
true
;
$position
=
$stringpos
;
}
if
(
!
is_numeric
(
$position
))
qa_fatal_error
(
'Insufficient parameters in query: '
.
$query
);
/**
* Return a random integer (as a string) for use in a BIGINT column.
* Actual limit is 18,446,744,073,709,551,615 - we aim for 18,446,743,999,999,999,999.
*/
function
qa_db_random_bigint
()
{
return
sprintf
(
'%d%06d%06d'
,
mt_rand
(
1
,
18446743
),
mt_rand
(
0
,
999999
),
mt_rand
(
0
,
999999
));
$value
=
qa_db_argument_to_mysql
(
$arguments
[
$argument
],
$alwaysquote
);
$query
=
substr_replace
(
$query
,
$value
,
$position
,
1
);
$offset
=
$position
+
strlen
(
$value
);
// allows inserting strings which contain #/$ character
}
/**
* Return an array of the names of all tables in the Q2A database, converted to lower case.
* No longer used by Q2A and shouldn't be needed.
*/
function
qa_db_list_tables_lc
()
{
return
array_map
(
'strtolower'
,
qa_db_list_tables
());
}
/**
* Return an array of the names of all tables in the Q2A database.
*/
function
qa_db_list_tables
()
{
return
qa_db_read_all_values
(
qa_db_query_raw
(
'SHOW TABLES'
));
}
return
$query
;
}
/**
* Run $query after substituting ^, # and $ symbols, and return the result resource (or call fail handler).
* @param $query
* @return mixed
*/
function
qa_db_query_sub
(
$query
)
// arguments for substitution retrieved using func_get_args()
{
$funcargs
=
func_get_args
();
return
qa_db_query_raw
(
qa_db_apply_sub
(
$query
,
array_slice
(
$funcargs
,
1
)));
}
/**
* Return the number of rows in $result. (Simple wrapper for mysqli_result::num_rows.)
* @param $result
* @return int
*/
function
qa_db_num_rows
(
$result
)
{
if
(
$result
instanceof
mysqli_result
)
return
$result
->
num_rows
;
return
0
;
}
/**
* Return the value of the auto-increment column for the last inserted row.
*/
function
qa_db_last_insert_id
()
{
$db
=
qa_db_connection
();
return
$db
->
insert_id
;
}
/**
* Return the number of rows affected by the last query.
*/
function
qa_db_affected_rows
()
{
$db
=
qa_db_connection
();
return
$db
->
affected_rows
;
}
/**
* For the previous INSERT ... ON DUPLICATE KEY UPDATE query, return whether an insert operation took place.
*/
function
qa_db_insert_on_duplicate_inserted
()
{
return
(
qa_db_affected_rows
()
==
1
);
}
/**
* Return a random integer (as a string) for use in a BIGINT column.
* Actual limit is 18,446,744,073,709,551,615 - we aim for 18,446,743,999,999,999,999.
*/
function
qa_db_random_bigint
()
{
return
sprintf
(
'%d%06d%06d'
,
mt_rand
(
1
,
18446743
),
mt_rand
(
0
,
999999
),
mt_rand
(
0
,
999999
));
}
/**
* Return an array of the names of all tables in the Q2A database, converted to lower case.
* No longer used by Q2A and shouldn't be needed.
*/
function
qa_db_list_tables_lc
()
{
return
array_map
(
'strtolower'
,
qa_db_list_tables
());
}
/**
* Return an array of the names of all tables in the Q2A database.
*/
function
qa_db_list_tables
()
{
return
qa_db_read_all_values
(
qa_db_query_raw
(
'SHOW TABLES'
));
}
/*
...
...
@@ -476,300 +502,319 @@
*/
/**
* Return the data specified by a single $selectspec - see long comment above.
*/
function
qa_db_single_select
(
$selectspec
)
{
// check for cached results
if
(
isset
(
$selectspec
[
'caching'
]))
{
$cacheHandler
=
Q2A_Storage_CacheManager
::
getInstance
();
$cacheKey
=
'query:'
.
$selectspec
[
'caching'
][
'key'
];
if
(
$cacheHandler
->
isEnabled
())
{
$queryData
=
$cacheHandler
->
get
(
$cacheKey
);
if
(
$queryData
!==
null
)
return
$queryData
;
}
/**
* Return the data specified by a single $selectspec - see long comment above.
* @param $selectspec
* @return array|mixed
*/
function
qa_db_single_select
(
$selectspec
)
{
// check for cached results
if
(
isset
(
$selectspec
[
'caching'
]))
{
$cacheHandler
=
Q2A_Storage_CacheManager
::
getInstance
();
$cacheKey
=
'query:'
.
$selectspec
[
'caching'
][
'key'
];
if
(
$cacheHandler
->
isEnabled
())
{
$queryData
=
$cacheHandler
->
get
(
$cacheKey
);
if
(
$queryData
!==
null
)
return
$queryData
;
}
}
$query
=
'SELECT '
;
$query
=
'SELECT '
;
foreach
(
$selectspec
[
'columns'
]
as
$columnas
=>
$columnfrom
)
$query
.=
$columnfrom
.
(
is_int
(
$columnas
)
?
''
:
(
' AS '
.
$columnas
))
.
', '
;
foreach
(
$selectspec
[
'columns'
]
as
$columnas
=>
$columnfrom
)
$query
.=
$columnfrom
.
(
is_int
(
$columnas
)
?
''
:
(
' AS '
.
$columnas
))
.
', '
;
$results
=
qa_db_read_all_assoc
(
qa_db_query_raw
(
qa_db_apply_sub
(
substr
(
$query
,
0
,
-
2
)
.
(
strlen
(
@
$selectspec
[
'source'
])
?
(
' FROM '
.
$selectspec
[
'source'
])
:
''
),
$results
=
qa_db_read_all_assoc
(
qa_db_query_raw
(
qa_db_apply_sub
(
substr
(
$query
,
0
,
-
2
)
.
(
strlen
(
@
$selectspec
[
'source'
])
?
(
' FROM '
.
$selectspec
[
'source'
])
:
''
),
@
$selectspec
[
'arguments'
])
),
@
$selectspec
[
'arraykey'
]);
// arrayvalue is applied in qa_db_post_select()
),
@
$selectspec
[
'arraykey'
]);
// arrayvalue is applied in qa_db_post_select()
qa_db_post_select
(
$results
,
$selectspec
);
// post-processing
qa_db_post_select
(
$results
,
$selectspec
);
// post-processing
// save cached results
if
(
isset
(
$selectspec
[
'caching'
]))
{
if
(
$cacheHandler
->
isEnabled
())
{
$cacheHandler
->
set
(
$cacheKey
,
$results
,
$selectspec
[
'caching'
][
'ttl'
]);
}
// save cached results
if
(
isset
(
$selectspec
[
'caching'
]))
{
if
(
$cacheHandler
->
isEnabled
())
{
$cacheHandler
->
set
(
$cacheKey
,
$results
,
$selectspec
[
'caching'
][
'ttl'
]);
}
return
$results
;
}
return
$results
;
}
/**
* Return the data specified by each element of $selectspecs, where the keys of the
* returned array match the keys of the supplied $selectspecs array. See long comment above.
*/
function
qa_db_multi_select
(
$selectspecs
)
{
if
(
!
count
(
$selectspecs
))
return
array
();
// Perform simple queries if the database is local or there are only 0 or 1 selectspecs
/**
* Return the data specified by each element of $selectspecs, where the keys of the
* returned array match the keys of the supplied $selectspecs array. See long comment above.
* @param array $selectspecs
* @return array
*/
function
qa_db_multi_select
(
$selectspecs
)
{
if
(
!
count
(
$selectspecs
))
return
array
();
if
(
!
QA_OPTIMIZE_DISTANT_DB
||
(
count
(
$selectspecs
)
<=
1
))
{
$outresults
=
array
();
// Perform simple queries if the database is local or there are only 0 or 1 selectspecs
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
$outresults
[
$selectkey
]
=
qa_db_single_select
(
$selectspec
);
if
(
!
QA_OPTIMIZE_DISTANT_DB
||
(
count
(
$selectspecs
)
<=
1
))
{
$outresults
=
array
(
);
return
$outresults
;
}
// Otherwise, parse columns for each spec to deal with columns without an 'AS' specification
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
$outresults
[
$selectkey
]
=
qa_db_single_select
(
$selectspec
);
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
{
$selectspecs
[
$selectkey
][
'outcolumns'
]
=
array
();
$selectspecs
[
$selectkey
][
'autocolumn'
]
=
array
();
return
$outresults
;
}
foreach
(
$selectspec
[
'columns'
]
as
$columnas
=>
$columnfrom
)
{
if
(
is_int
(
$columnas
))
{
$periodpos
=
strpos
(
$columnfrom
,
'.'
);
$columnas
=
is_numeric
(
$periodpos
)
?
substr
(
$columnfrom
,
$periodpos
+
1
)
:
$columnfrom
;
$selectspecs
[
$selectkey
][
'autocolumn'
][
$columnas
]
=
true
;
}
// Otherwise, parse columns for each spec to deal with columns without an 'AS' specification
if
(
isset
(
$selectspecs
[
$selectkey
][
'outcolumns'
][
$columnas
]))
qa_fatal_error
(
'Duplicate column name in qa_db_multi_select()'
);
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
{
$selectspecs
[
$selectkey
][
'outcolumns'
]
=
array
();
$selectspecs
[
$selectkey
][
'autocolumn'
]
=
array
();
$selectspecs
[
$selectkey
][
'outcolumns'
][
$columnas
]
=
$columnfrom
;
foreach
(
$selectspec
[
'columns'
]
as
$columnas
=>
$columnfrom
)
{
if
(
is_int
(
$columnas
))
{
$periodpos
=
strpos
(
$columnfrom
,
'.'
);
$columnas
=
is_numeric
(
$periodpos
)
?
substr
(
$columnfrom
,
$periodpos
+
1
)
:
$columnfrom
;
$selectspecs
[
$selectkey
][
'autocolumn'
][
$columnas
]
=
true
;
}
if
(
isset
(
$selectspec
[
'arraykey'
]))
if
(
!
isset
(
$selectspecs
[
$selectkey
][
'outcolumns'
][
$selectspec
[
'arraykey'
]]))
qa_fatal_error
(
'Used arraykey not in columns in qa_db_multi_select()'
);
if
(
isset
(
$selectspecs
[
$selectkey
][
'outcolumns'
][
$columnas
]))
qa_fatal_error
(
'Duplicate column name in qa_db_multi_select()'
);
if
(
isset
(
$selectspec
[
'arrayvalue'
]))
if
(
!
isset
(
$selectspecs
[
$selectkey
][
'outcolumns'
][
$selectspec
[
'arrayvalue'
]]))
qa_fatal_error
(
'Used arrayvalue not in columns in qa_db_multi_select()'
);
$selectspecs
[
$selectkey
][
'outcolumns'
][
$columnas
]
=
$columnfrom
;
}
// Work out the full list of columns used
$outcolumns
=
array
();
foreach
(
$selectspecs
as
$selectspec
)
$outcolumns
=
array_unique
(
array_merge
(
$outcolumns
,
array_keys
(
$selectspec
[
'outcolumns'
])));
// Build the query based on this full list
if
(
isset
(
$selectspec
[
'arraykey'
]))
if
(
!
isset
(
$selectspecs
[
$selectkey
][
'outcolumns'
][
$selectspec
[
'arraykey'
]]))
qa_fatal_error
(
'Used arraykey not in columns in qa_db_multi_select()'
);
$query
=
''
;
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
{
$subquery
=
"(SELECT '"
.
qa_db_escape_string
(
$selectkey
)
.
"'"
.
(
empty
(
$query
)
?
' AS selectkey'
:
''
);
foreach
(
$outcolumns
as
$columnas
)
{
$subquery
.=
', '
.
(
isset
(
$selectspec
[
'outcolumns'
][
$columnas
])
?
$selectspec
[
'outcolumns'
][
$columnas
]
:
'NULL'
);
if
(
empty
(
$query
)
&&
!
isset
(
$selectspec
[
'autocolumn'
][
$columnas
]))
$subquery
.=
' AS '
.
$columnas
;
}
if
(
strlen
(
@
$selectspec
[
'source'
]))
$subquery
.=
' FROM '
.
$selectspec
[
'source'
];
$subquery
.=
')'
;
if
(
strlen
(
$query
))
$query
.=
' UNION ALL '
;
$query
.=
qa_db_apply_sub
(
$subquery
,
@
$selectspec
[
'arguments'
]);
}
if
(
isset
(
$selectspec
[
'arrayvalue'
]))
if
(
!
isset
(
$selectspecs
[
$selectkey
][
'outcolumns'
][
$selectspec
[
'arrayvalue'
]]))
qa_fatal_error
(
'Used arrayvalue not in columns in qa_db_multi_select()'
);
}
//
Perform query and extract results
//
Work out the full list of columns used
$rawresults
=
qa_db_read_all_assoc
(
qa_db_query_raw
(
$query
));
$outcolumns
=
array
();
foreach
(
$selectspecs
as
$selectspec
)
$outcolumns
=
array_unique
(
array_merge
(
$outcolumns
,
array_keys
(
$selectspec
[
'outcolumns'
])));
$outresults
=
array
();
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
$outresults
[
$selectkey
]
=
array
();
// Build the query based on this full list
foreach
(
$rawresults
as
$rawresult
)
{
$selectkey
=
$rawresult
[
'selectkey'
];
$selectspec
=
$selectspecs
[
$selectkey
]
;
$query
=
''
;
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
{
$subquery
=
"(SELECT '"
.
qa_db_escape_string
(
$selectkey
)
.
"'"
.
(
empty
(
$query
)
?
' AS selectkey'
:
''
)
;
$keepresult
=
array
();
foreach
(
$selectspec
[
'outcolumns'
]
as
$columnas
=>
$columnfrom
)
$keepresult
[
$columnas
]
=
$rawresult
[
$columnas
];
foreach
(
$outcolumns
as
$columnas
)
{
$subquery
.=
', '
.
(
isset
(
$selectspec
[
'outcolumns'
][
$columnas
])
?
$selectspec
[
'outcolumns'
][
$columnas
]
:
'NULL'
);
if
(
isset
(
$selectspec
[
'arraykey'
]))
$outresults
[
$selectkey
][
$keepresult
[
$selectspec
[
'arraykey'
]]]
=
$keepresult
;
else
$outresults
[
$selectkey
][]
=
$keepresult
;
if
(
empty
(
$query
)
&&
!
isset
(
$selectspec
[
'autocolumn'
][
$columnas
]))
$subquery
.=
' AS '
.
$columnas
;
}
// Post-processing to apply various stuff include sorting request, since we can't rely on ORDER BY due to UNION
if
(
strlen
(
@
$selectspec
[
'source'
]))
$subquery
.=
' FROM '
.
$selectspec
[
'source'
];
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
qa_db_post_select
(
$outresults
[
$selectkey
],
$selectspec
);
$subquery
.=
')'
;
// Return results
if
(
strlen
(
$query
))
$query
.=
' UNION ALL '
;
return
$outresults
;
$query
.=
qa_db_apply_sub
(
$subquery
,
@
$selectspec
[
'arguments'
])
;
}
// Perform query and extract results
/**
* Post-process $outresult according to $selectspec, applying 'sortasc', 'sortdesc', 'arrayvalue' and 'single'.
*/
function
qa_db_post_select
(
&
$outresult
,
$selectspec
)
{
// PHP's sorting algorithm is not 'stable', so we use '_order_' element to keep stability.
// By contrast, MySQL's ORDER BY does seem to give the results in a reliable order.
if
(
isset
(
$selectspec
[
'sortasc'
]))
{
require_once
QA_INCLUDE_DIR
.
'util/sort.php'
;
$index
=
0
;
foreach
(
$outresult
as
$key
=>
$value
)
$outresult
[
$key
][
'_order_'
]
=
$index
++
;
qa_sort_by
(
$outresult
,
$selectspec
[
'sortasc'
],
'_order_'
);
}
elseif
(
isset
(
$selectspec
[
'sortdesc'
]))
{
require_once
QA_INCLUDE_DIR
.
'util/sort.php'
;
$rawresults
=
qa_db_read_all_assoc
(
qa_db_query_raw
(
$query
));
if
(
isset
(
$selectspec
[
'sortdesc_2'
]))
qa_sort_by
(
$outresult
,
$selectspec
[
'sortdesc'
],
$selectspec
[
'sortdesc_2'
]);
$outresults
=
array
();
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
$outresults
[
$selectkey
]
=
array
();
else
{
$index
=
count
(
$outresult
);
foreach
(
$outresult
as
$key
=>
$value
)
$outresult
[
$key
][
'_order_'
]
=
$index
--
;
foreach
(
$rawresults
as
$rawresult
)
{
$selectkey
=
$rawresult
[
'selectkey'
];
$selectspec
=
$selectspecs
[
$selectkey
];
qa_sort_by
(
$outresult
,
$selectspec
[
'sortdesc'
],
'_order_'
);
}
$outresult
=
array_reverse
(
$outresult
,
true
);
}
$keepresult
=
array
();
foreach
(
$selectspec
[
'outcolumns'
]
as
$columnas
=>
$columnfrom
)
$keepresult
[
$columnas
]
=
$rawresult
[
$columnas
];
if
(
isset
(
$selectspec
[
'arrayvalue'
]))
foreach
(
$outresult
as
$key
=>
$value
)
$outresult
[
$key
]
=
$value
[
$selectspec
[
'arrayvalue'
]];
if
(
@
$selectspec
[
'single'
])
$outresult
=
count
(
$outresult
)
?
reset
(
$outresult
)
:
null
;
if
(
isset
(
$selectspec
[
'arraykey'
]))
$outresults
[
$selectkey
][
$keepresult
[
$selectspec
[
'arraykey'
]]]
=
$keepresult
;
else
$outresults
[
$selectkey
][]
=
$keepresult
;
}
// Post-processing to apply various stuff include sorting request, since we can't rely on ORDER BY due to UNION
/**
* Return the full results from the $result resource as an array. The key of each element in the returned array
* is from column $key if specified, otherwise it's integer. The value of each element in the returned array
* is from column $value if specified, otherwise it's a named array of all columns, given an array of arrays.
*/
function
qa_db_read_all_assoc
(
$result
,
$key
=
null
,
$value
=
null
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading all assoc from invalid result'
);
foreach
(
$selectspecs
as
$selectkey
=>
$selectspec
)
qa_db_post_select
(
$outresults
[
$selectkey
],
$selectspec
);
$assocs
=
array
();
// Return results
while
(
$assoc
=
$result
->
fetch_assoc
())
{
if
(
isset
(
$key
))
$assocs
[
$assoc
[
$key
]]
=
isset
(
$value
)
?
$assoc
[
$value
]
:
$assoc
;
else
$assocs
[]
=
isset
(
$value
)
?
$assoc
[
$value
]
:
$assoc
;
}
return
$outresults
;
}
return
$assocs
;
}
/**
* Post-process $outresult according to $selectspec, applying 'sortasc', 'sortdesc', 'arrayvalue' and 'single'.
* @param array $outresult
* @param array $selectspec
*/
function
qa_db_post_select
(
&
$outresult
,
$selectspec
)
{
// PHP's sorting algorithm is not 'stable', so we use '_order_' element to keep stability.
// By contrast, MySQL's ORDER BY does seem to give the results in a reliable order.
/**
* Return the first row from the $result resource as an array of [column name] => [column value].
* If there's no first row, throw a fatal error unless $allowempty is true.
*/
function
qa_db_read_one_assoc
(
$result
,
$allowempty
=
false
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading one assoc from invalid result'
);
if
(
isset
(
$selectspec
[
'sortasc'
]))
{
require_once
QA_INCLUDE_DIR
.
'util/sort.php'
;
$assoc
=
$result
->
fetch_assoc
();
$index
=
0
;
foreach
(
$outresult
as
$key
=>
$value
)
$outresult
[
$key
][
'_order_'
]
=
$index
++
;
if
(
is_array
(
$assoc
))
return
$assoc
;
if
(
$allowempty
)
return
null
;
else
qa_fatal_error
(
'Reading one assoc from empty results'
);
}
qa_sort_by
(
$outresult
,
$selectspec
[
'sortasc'
],
'_order_'
);
}
elseif
(
isset
(
$selectspec
[
'sortdesc'
]))
{
require_once
QA_INCLUDE_DIR
.
'util/sort.php'
;
/**
* Return a numbered array containing the first (and presumably only) column from the $result resource.
*/
function
qa_db_read_all_values
(
$result
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading column from invalid result'
);
if
(
isset
(
$selectspec
[
'sortdesc_2'
]))
qa_sort_by
(
$outresult
,
$selectspec
[
'sortdesc'
],
$selectspec
[
'sortdesc_2'
]);
$output
=
array
();
else
{
$index
=
count
(
$outresult
);
foreach
(
$outresult
as
$key
=>
$value
)
$outresult
[
$key
][
'_order_'
]
=
$index
--
;
while
(
$row
=
$result
->
fetch_row
())
$output
[]
=
$row
[
0
];
qa_sort_by
(
$outresult
,
$selectspec
[
'sortdesc'
],
'_order_'
);
}
return
$output
;
$outresult
=
array_reverse
(
$outresult
,
true
)
;
}
/**
* Return the first column of the first row (and presumably only cell) from the $result resource.
* If there's no first row, throw a fatal error unless $allowempty is true.
*/
function
qa_db_read_one_value
(
$result
,
$allowempty
=
false
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading one value from invalid result'
);
$row
=
$result
->
fetch_row
();
if
(
is_array
(
$row
))
return
$row
[
0
];
if
(
$allowempty
)
return
null
;
if
(
isset
(
$selectspec
[
'arrayvalue'
]))
foreach
(
$outresult
as
$key
=>
$value
)
$outresult
[
$key
]
=
$value
[
$selectspec
[
'arrayvalue'
]];
if
(
@
$selectspec
[
'single'
])
$outresult
=
count
(
$outresult
)
?
reset
(
$outresult
)
:
null
;
}
/**
* Return the full results from the $result resource as an array. The key of each element in the returned array
* is from column $key if specified, otherwise it's integer. The value of each element in the returned array
* is from column $value if specified, otherwise it's a named array of all columns, given an array of arrays.
* @param $result
* @param string $key
* @param mixed $value
* @return array
*/
function
qa_db_read_all_assoc
(
$result
,
$key
=
null
,
$value
=
null
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading all assoc from invalid result'
);
$assocs
=
array
();
while
(
$assoc
=
$result
->
fetch_assoc
())
{
if
(
isset
(
$key
))
$assocs
[
$assoc
[
$key
]]
=
isset
(
$value
)
?
$assoc
[
$value
]
:
$assoc
;
else
qa_fatal_error
(
'Reading one value from empty results'
);
}
/**
* Suspend the updating of counts (of many different types) in the database, to save time when making a lot of changes
* if $suspend is true, otherwise reinstate it. A counter is kept to allow multiple calls.
*/
function
qa_suspend_update_counts
(
$suspend
=
true
)
{
global
$qa_update_counts_suspended
;
$qa_update_counts_suspended
+=
(
$suspend
?
1
:
-
1
);
$assocs
[]
=
isset
(
$value
)
?
$assoc
[
$value
]
:
$assoc
;
}
/**
* Returns whether counts should currently be updated (i.e. if count updating has not been suspended).
*/
function
qa_should_update_counts
()
{
global
$qa_update_counts_suspended
;
return
(
$qa_update_counts_suspended
<=
0
);
}
return
$assocs
;
}
/**
* Return the first row from the $result resource as an array of [column name] => [column value].
* If there's no first row, throw a fatal error unless $allowempty is true.
* @param $result
* @param bool $allowempty
* @return array|null
*/
function
qa_db_read_one_assoc
(
$result
,
$allowempty
=
false
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading one assoc from invalid result'
);
$assoc
=
$result
->
fetch_assoc
();
if
(
is_array
(
$assoc
))
return
$assoc
;
if
(
$allowempty
)
return
null
;
else
qa_fatal_error
(
'Reading one assoc from empty results'
);
}
/**
* Return a numbered array containing the first (and presumably only) column from the $result resource.
* @param $result
* @return array
*/
function
qa_db_read_all_values
(
$result
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading column from invalid result'
);
$output
=
array
();
while
(
$row
=
$result
->
fetch_row
())
$output
[]
=
$row
[
0
];
return
$output
;
}
/**
* Return the first column of the first row (and presumably only cell) from the $result resource.
* If there's no first row, throw a fatal error unless $allowempty is true.
* @param $result
* @param bool $allowempty
* @return mixed|null
*/
function
qa_db_read_one_value
(
$result
,
$allowempty
=
false
)
{
if
(
!
(
$result
instanceof
mysqli_result
))
qa_fatal_error
(
'Reading one value from invalid result'
);
$row
=
$result
->
fetch_row
();
if
(
is_array
(
$row
))
return
$row
[
0
];
if
(
$allowempty
)
return
null
;
else
qa_fatal_error
(
'Reading one value from empty results'
);
}
/**
* Suspend the updating of counts (of many different types) in the database, to save time when making a lot of changes
* if $suspend is true, otherwise reinstate it. A counter is kept to allow multiple calls.
* @param bool $suspend
*/
function
qa_suspend_update_counts
(
$suspend
=
true
)
{
global
$qa_update_counts_suspended
;
$qa_update_counts_suspended
+=
(
$suspend
?
1
:
-
1
);
}
/**
* Returns whether counts should currently be updated (i.e. if count updating has not been suspended).
*/
function
qa_should_update_counts
()
{
global
$qa_update_counts_suspended
;
return
(
$qa_update_counts_suspended
<=
0
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment