PHP+Mysql是最流行的搭配。
连接
连接数据库是第一步,PHP中可以使用mysql_connect()
来连接,注意7.x已弃用该函数,5.x仍可使用,7.x可用mysqli_connect()
。
mysql_connect(servername,username,password);
例子:
<?php
$con = mysql_connect("localhost","root","root");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
// some code
mysql_close($con);
?>
PHP脚本结束时,会自动关闭连接,如果需要提前可像例子中使用mysql_close()
提前关闭。
执行SQL语句
建立连接后,就可以使用mysql_query()
,向 MySQL 连接发送执行的SQL语句。
7.x使用mysqli_query()
。
- 建库
mysql:CREATE DATABASE database_name
创建名为 "my_db" 的数据库:mysql_query("CREATE DATABASE my_db",$con)
- 建表
mysql:
CREATE TABLE table_name
(
column_name1 data_type,
.......
)
创建名为 "Persons" 的表,此表有三列。列名是 "FirstName", "LastName" 以及 "Age":
mysql_select_db("my_db", $con);
$sql = "CREATE TABLE Persons
(
FirstName varchar(15),
LastName varchar(15),
Age int
)";
mysql_query($sql,$con);
在对某个数据库内的表操作时,必须先用mysql_select_db()选择对应数据库
7.x使用mysqli_select_db()
- 插
mysql:INSERT INTO table_name VALUES (value1, value2,....)
或INSERT INTO table_name (column1, column2,...) VALUES (value1, value2,....)
SQL语句对大小写不敏感
向 "Persons" 表添加了一个新记录:
mysql_select_db("my_db", $con);
mysql_query("INSERT INTO Persons (FirstName, LastName, Age) VALUES ('Peter', 'Griffin', '35')");
- 查
mysql:SELECT column_name(s) FROM table_name
PHP:
下面这个例子在$result
变量中存放由 mysql_query() 函数返回的数据。使用 mysql_fetch_array() 函数以数组的形式从记录集返回第一行。每个随后对 mysql_fetch_array() 函数的调用都会返回记录集中的下一行。
mysql_select_db("my_db", $con);
$result = mysql_query("SELECT * FROM Persons");
while($row = mysql_fetch_array($result))
{
echo $row['FirstName'] . " " . $row['LastName'];
echo "<br />";
}
- 改
修改表中某行某列的值:UPDATE table_name SET column_name = new_value WHERE column_name = some_value
修改Persons表中FirstName为Peter且LastName为Griffin的行中Age列的值为36:mysql_query("UPDATE Persons SET Age = '36' WHERE FirstName = 'Peter' AND LastName = 'Griffin'");
- 删
删除某表中的某行:DELETE FROM table_name WHERE column_name = some_value
删除 "Persons" 表中所有 LastName='Griffin' 的行:mysql_query("DELETE FROM Persons WHERE LastName='Griffin'");
- where指定条件
指定条件的查询:SELECT column FROM table WHERE 条件
PHP:$result = mysql_query("SELECT * FROM Persons WHERE FirstName='Peter'");
- order by 排序
ORDER BY 关键词用于将结果集中的数据按照某列的顺序排序。
order by 列名/n(n是第n列)
mysql:SELECT column_name(s) FROM table_name ORDER BY column_name
PDO
- 连接
$conn = new PDO("mysql:host=$servername;", $username, $password);
- 建库建表插数据
无结果返回使用exec()
$sql = "CREATE DATABASE myDBPDO";
//使用 exec() ,因为没有结果返回,注意,此处并未使用预处理,所以无防注入效果。
$conn->exec($sql);
预处理语句
预处理语句用于执行多个相同的 SQL 语句,并且执行效率更高。
预处理语句的工作原理如下:
- 预处理:创建 SQL 语句模板并发送到数据库。预留的值使用参数 "?" 标记 。例如:
INSERT INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?)
- 数据库解析,编译,对SQL语句模板执行查询优化,并存储结果不输出。
- 执行:最后,将应用绑定的值传递给参数("?" 标记),数据库执行语句。应用可以多次执行语句,如果参数的值不一样。
简单理解就是,先传递没有参数的语句给数据库解析编译,在传递参数的值给数据库作为参数执行。与传统的拼接语句一步执行相比,预处理是分两步的。此种情况下,即使参数的输入没有过滤,但因为参数在数据库中只作为参数参与SQL语句执行,没有与语句拼接,注入也就无法进行。
相比于直接执行SQL语句,预处理语句有两个主要优点:
- 预处理语句大大减少了分析时间,只做了一次查询(虽然语句多次执行)。
- 绑定参数减少了服务器带宽,你只需要发送查询的参数,而不是整个语句。
- 预处理语句针对SQL注入是非常有用的,因为参数值发送后使用不同的协议,保证了数据的合法性。但是注意,只要使用了字符串拼接SQL语句,即使使用了PDO,仍然可能存在注入。所以要杜绝拼接,使用?
实例:
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
// 检测连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 预处理及绑定
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
// 设置参数并执行
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();
echo "新记录插入成功";
$stmt->close();
$conn->close();
?>
解析实例的代码:
INSERT INTO MyGuests (firstname, lastname, email) VALUES(?, ?, ?)
在 SQL 语句中,我们使用了问号 (?),在此我们可以将问号替换为整型,字符串,双精度浮点型和布尔值。接下来,让我们来看下 bind_param() 函数:
$stmt->bind_param("sss", $firstname, $lastname, $email);
该函数绑定了 SQL 的参数,且告诉数据库参数的值。 "sss" 参数列处理其余参数的数据类型。s 字符告诉数据库该参数为字符串。
参数有以下四种类型:
i - integer(整型)
d - double(双精度浮点型)
s - string(字符串)
b - BLOB(binary large object:二进制大对象)
每个参数都需要指定类型。
通过告诉数据库参数的数据类型,可以降低 SQL 注入的风险。
总结,预处理语句使用的三个步骤:
- 预处理
$conn->prepare
- 绑定参数
$stmt->bind_param
- 设置参数并执行
$stmt->execute();
防止SQL注入
防止SQL注入,我们需要注意以下几个要点:
- 永远不要信任用户的输入。对用户的输入进行校验,可以通过正则表达式,或限制长度;对单引号和 双"-"进行转换等。
- 永远不要使用动态拼装sql,可以使用参数化的sql或者直接使用存储过程进行数据查询存取。
- 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
- 不要把机密信息直接存放,加密或者hash掉密码和敏感的信息。
- 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装。
如何防止
对用户输入的数据进行转义从而来防止SQL注入,注意如果magic_quotes_gpc开启了,就不要再转义了,多重转义会导致转义失效。
- mysqli_real_escape_string()
mysql指定的转义函数。
此函数用来对字符串中的特殊字符进行转义, 以使得这个字符串是一个合法的 SQL 语句。 传入的字符串会根据当前连接的字符集进行转义,得到一个编码后的合法的 SQL 语句。
mysqli_real_escape_string ( mysqli $link , string $escapestr ) : string
link
规定要使用的 MySQL 连接。
escapestr
需要进行转义的字符串。
会被进行转义的字符包括:NUL(ASCII 0),
,
,,'," 和 Control-Z
在调用 mysqli_real_escape_string() 函数之前, 必须先通过调用 mysqli_set_charset() 函数或者在 MySQL 服务器端设置合理的字符集,不合理的字符集会导致宽字节注入。
- addcslashes()
返回在指定字符前添加反斜杠的字符串。
对大小写敏感。
addcslashes ( string $str , string $charlist ) : string
string:规定要转义的字符串。
characters:规定要转义的字符或字符范围。
- addslashes()
返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符是单引号(')、双引号(")、反斜线()与 NUL(NULL 字符)。
addslashes( string $str) : string