我正在尋找一種方法來動態更改MYSQLi調用中使用的變量數量。我在5年前偶然發現了PHP.net上的一篇非常有用的帖子(http://php.net/manual/en/mysqli-stmt.bind-param.php#100879)。然而,我然後在事情上有點瘋狂,並希望得到幫助,知道我的工作改編是否仍然安全/有效/充滿錯誤,我不夠聰明。簡化和使MYSQLi查詢動態的好解決方案?
的想法是五倍:
- 爲了允許動態地期望容易地使用盡可能多的變量。
- 使書寫查詢與舊MYSQL一樣簡單(同時仍利用MYSQLi準備語句的現代性和安全性)。
- 爲了否定需要手動添加類,而是讓PHP處理它。
- 爲了自動區分期望返回的查詢(SELECT和EXPLAIN)和那些沒有(INSERT,DELETE和UPDATE)的查詢。
- 提供一種通過更改單個變量來調試單個行或整個頁面的簡單方法。
所有這些都實現,我希望,像這樣的東西:
doMYSQL( 'INSERT INTO表(ID,姓名)VALUES($ ID,$名)');
請注意,如果您願意,在下面的函數中,查詢(包含變量內聯,如舊的MYSQL)被單引號包圍 - 變量被解析爲實際變量名稱,而不是其值。這些值只在準備MYSQLi準備語句的階段纔會發生(因此,據我所知,應該有與禁止令攻擊相同的安全性)。
現在,官方說明。我很樂意提供任何有關如何使這一點變得更好的反饋,或者是否有明顯的錯誤。最後一個註釋(「雜項代碼」)下的所有代碼都來自PHP.net的帖子,其中大部分我都不明白,所以對此的任何評論也會有所幫助。如果這個功能通過吧,它肯定會讓我的生活更輕鬆,所以希望其他人也可以使用它:)。
只是爲了澄清,這對我嘗試過的所有測試都有效,所以我沒有理由認爲有任何問題。我只是有足夠的經驗知道我沒有足夠的經驗知道是否有任何紅旗。因此,我向你們致敬,並要求協助驗證功能的安全性。
謝謝!
<?php
/*
doMYSQL($sql, $debug_local [optional]);
$sql = Statement to execute;
$debug_local = 'print' to show query on page but not run, 'both' to show it and run, leave blank for normal execution.
(You can add a $debug variable at the top of the page to control all doMYSQL functions at once, though local ones take precedence.
*/
function doMYSQL($sql, $debug_local = 'none')
{
$mysqli = new mysqli("localhost", "username", "password", "database");
$print = $sql; // Save unaltered copy in case 'print' is enabled later
// Get debug settings (priority is user-set $debug_local, then global $debug, then default to 'none')
global $debug;
if (($debug == 'print' OR $debug == 'both') AND $debug_local == 'none'){$debug_local = $debug;}
// Create list of variables in the query
preg_match_all('/\$\w+/',$sql,$matches);
// For each variable found, find its value and add its kind and value to $params
$params = array();
foreach ($matches[0] AS $match)
{
$match = substr($match,1); // Get rid of the now-unneccessary '$'' on the variable name
global $$match; // Get the global value for that variable
$kind = gettype($$match); // Get the kind for that variable
// Convert PHP kind to mysqli kind for bind_result
if ($kind == "integer"){$kind = 'i';}
if ($kind == "double"){$kind = 'd';}
if ($kind == "string"){$kind = 's';}
$params[0] .= $kind; // Adds to ongoing list of types in $param[0]
$params[] = $$match; // Adds to ongoing list of values in $params[1+]
$sql = str_replace("$"."$match", '?', $sql); // Switch variable with '?' in the query
$print = str_replace("$"."$match", $$match."[$kind]", $print); // Switch variable with '?' in the query
}
// If debug is print or both, print
if ($debug_local == "print" OR $debug_local == "both")
{
echo "MYSQLi Debug: $print<br>";
}
// If debug is not 'print', run it
if ($debug_local != 'print')
{
// Get first word; if a select/explain, set $close to false; otherwise set to 'true.' If irregular query, error message.
$temp = explode(' ',trim($sql),2);
$firstword = strtolower($temp[0]);
if ($firstword == 'select' OR $firstword == 'explain'){$close=false;}
else if ($firstword == 'update' OR $firstword == 'delete' OR $firstword == 'insert'){$close=true;}
else {echo "Invalid first word on query $query!<br>";}
// Start misc code found on the PHP link
$stmt = $mysqli->prepare($sql) or die ("Failed to prepared the statement!");
call_user_func_array(array($stmt, 'bind_param'), refValues($params));
$stmt->execute();
if($close){
$result = $mysqli->affected_rows;
} else {
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), refValues($parameters));
while ($stmt->fetch()) {
$x = array();
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
$result = $results;
}
$stmt->close();
$mysqli->close();
return $result;
}
}
function refValues($arr)
{
if (strnatcmp(phpversion(),'5.3') >= 0) //Reference is required for PHP 5.3+
{
$refs = array();
foreach($arr as $key => $value)
$refs[$key] = &$arr[$key];
return $refs;
}
return $arr;
}
Examples (generic):
doMYSQL('SELECT * FROM table WHERE id = $id');
doMYSQL('SELECT * FROM table');
doMYSQL('INSERT INTO table(id, name) VALUES ($id,$name)');
Examples (with data):
$user = 1;
$location = 'California';
$result = doMYSQL('SELECT * FROM watchlists_locations WHERE user = $user AND location = $location');
print_r($result);
doMYSQL('INSERT INTO watchlists_locations(user, location) VALUES ($user,"1000")');
?>
謝謝你的職位 - 我聽說準備好的發言不僅僅是依靠mysqli_real_escape_string更安全()(以及該函數訪問數據庫,無論如何,所以沒有真正的速度/帶寬優勢)。否則,我可以簡化爲幾個獨立的部分,但我喜歡將所有這些功能都集中在一個屋檐下,只要它能夠工作:)。 我也看過PDO。在這一點上,我只是試圖避免學習一個新的系統,如果我可以讓mysqli爲我所需要的工作。如果我的大功能起作用,現在看起來我基本上可以編寫普通的MYSQL查詢。我很懶:P –