这是一个创建社区学习资源。我们的目标是拥有好的代码示例,这些代码不会重复复制/粘贴PHP代码中经常出现的可怕错误。我已经要求它成为社区维基。
这不是一个编码竞赛,它不是关于寻找最快或最紧凑的方式来做查询-它是提供一个很好的,可读的参考,特别是对于新手。
每天,都有大量的问题涌入,其中包含使用堆栈溢出上的mysql_*
系列函数的非常糟糕的代码片段。虽然通常最好的做法是将这些人引向PDO,但有时这既不可能(例如,继承的遗留软件),也不是一个现实的期望(用户已经在他们的项目中使用它)。
使用mysql_*
库的代码的常见问题包括:
值中的
中的脚本(XSS)注入中,也始终发生错误
让我们编写一个PHP代码示例,它使用mySQL_* family of functions执行以下操作
失败失败接受两个POST值id
(数字)和name
(字符串)
tablename
执行更新查询,更改ID为name
列,正常退出,但仅在生产模式下显示详细的错误。更新就足够了;或者使用choosing$name
trigger_error()
“。没有显示出上面列出的任何弱点吗?
它应该是尽可能简单的。理想情况下,它不包含任何函数或类。我们的目标不是创建一个copy/pasteable库,而是向展示使数据库查询安全所需的最低限度。
好的评论会被加分。
目标是使这个问题成为用户在遇到具有糟糕代码(即使它根本不是问题的焦点)的提问者时可以链接到的资源,或者遇到失败的查询而不知道如何修复它。
先发制人的讨论:
是的,将这些问题提交给PDO往往更可取。当这是一种选择时,我们应该这样做。然而,这并不总是可能的-有时,提问者正在处理遗留代码,或者已经在这个库中走了很长的路,现在不太可能改变它。此外,如果使用得当,mysql_*
系列函数是完全安全的。所以请不要在这里回答“使用PDO”。
发布于 2011-06-01 17:10:10
我对它的尝试。尽量保持简单,同时仍然保持一些现实世界的便利性。
处理unicode并使用松散比较以提高可读性。友善;-)
<?php
header('Content-type: text/html; charset=utf-8');
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', 1);
// display_errors can be changed to 0 in production mode to
// suppress PHP's error messages
/*
Can be used for testing
$_POST['id'] = 1;
$_POST['name'] = 'Markus';
*/
$config = array(
'host' => '127.0.0.1',
'user' => 'my_user',
'pass' => 'my_pass',
'db' => 'my_database'
);
# Connect and disable mysql error output
$connection = @mysql_connect($config['host'],
$config['user'], $config['pass']);
if (!$connection) {
trigger_error('Unable to connect to database: '
. mysql_error(), E_USER_ERROR);
}
if (!mysql_select_db($config['db'])) {
trigger_error('Unable to select db: ' . mysql_error(),
E_USER_ERROR);
}
if (!mysql_set_charset('utf8')) {
trigger_error('Unable to set charset for db connection: '
. mysql_error(), E_USER_ERROR);
}
$result = mysql_query(
'UPDATE tablename SET name = "'
. mysql_real_escape_string($_POST['name'])
. '" WHERE id = "'
. mysql_real_escape_string($_POST['id']) . '"'
);
if ($result) {
echo htmlentities($_POST['name'], ENT_COMPAT, 'utf-8')
. ' updated.';
} else {
trigger_error('Unable to update db: '
. mysql_error(), E_USER_ERROR);
}
发布于 2011-06-01 16:39:10
我决定抢先一步做点什么。这是一个开始的东西。在出错时引发异常。
function executeQuery($query, $args) {
$cleaned = array_map('mysql_real_escape_string', $args);
if($result = mysql_query(vsprintf($query, $cleaned))) {
return $result;
} else {
throw new Exception('MySQL Query Error: ' . mysql_error());
}
}
function updateTablenameName($id, $name) {
$query = "UPDATE tablename SET name = '%s' WHERE id = %d";
return executeQuery($query, array($name, $id));
}
try {
updateTablenameName($_POST['id'], $_POST['name']);
} catch(Exception $e) {
echo $e->getMessage();
exit();
}
发布于 2011-06-01 16:55:21
/**
* Rule #0: never trust users input!
*/
//sanitize integer value
$id = intval($_GET['id']);
//sanitize string value;
$name = mysql_real_escape_string($_POST['name']);
//1. using `dbname`. is better than using mysql_select_db()
//2. names of tables and columns should be quoted by "`" symbol
//3. each variable should be sanitized (even in LIMIT clause)
$q = mysql_query("UPDATE `dbname`.`tablename` SET `name`='".$name."' WHERE `id`='".$id."' LIMIT 0,1 ");
if ($q===false)
{
trigger_error('Error in query: '.mysql_error(), E_USER_WARNING);
}
else
{
//be careful! $name contains user's data, remember Rule #0
//always use htmlspecialchars() to sanitize user's data in output
print htmlspecialchars($name).' updated';
}
########################################################################
//Example, how easily is to use set_error_handler() and trigger_error()
//to control error reporting in production and dev-code
//Do NOT use error_reporting(0) or error_reporting(~E_ALL) - each error
//should be fixed, not muted
function err_handler($errno, $errstr, $errfile, $errline)
{
$hanle_errors_print = E_ALL & ~E_NOTICE;
//if we want to print this type of errors (other types we can just write in log-file)
if ($errno & $hanle_errors_print)
{
//$errstr can contain user's data, so... Rule #0
print PHP_EOL.'Error ['.$errno.'] in file '.$errfile.' in line '.$errline
.': '.htmlspecialchars($errstr).PHP_EOL;
}
//here you can write error into log-file
}
set_error_handler('err_handler', E_ALL & ~E_NOTICE & E_USER_NOTICE & ~E_STRICT & ~E_DEPRECATED);
和一些注释的解释:
//1. using `dbname`. is better than using mysql_select_db()
使用mysql_select_db可以创建错误,并且查找和修复它们并不是那么容易。
例如,在某些脚本中,您会将db1设置为数据库,但在某些函数中,您需要将db2设置为数据库。
调用此函数后,将切换数据库,脚本中的所有后续查询都将中断或将错误数据库中的某些数据中断(如果表名和列名重合)。
//2. names of tables and columns should be quoted by "`" symbol
一些列的名称也可以是SQL-关键字,使用“‘”符号将有助于实现这一点。
另外,插入到查询中的所有字符串值都应该用的符号引起来。
//always use htmlspecialchars() to sanitize user's data in output
它将帮助您预防XSS-attacks。
https://stackoverflow.com/questions/6198104
复制相似问题