PHP语法高级篇(七):MySQL数据库
Web编程不可避免的需要使用到数据库,而MySQL数据库使用的非常多,并且是LAMP(Linux+Apache+MySQL+PHP)的技术栈的核心组合。本篇文章将记录PHP操作MySQL的学习过程。
一、连接 MySQL
PHP 5 及更高版本可以使用以下方式与 MySQL 数据库协同工作:
- MySQLi 扩展("i" 代表 improved,即改进)
- PDO(PHP Data Objects,即 PHP 数据对象)
本篇文章将记录如何使用 PDO 进行 MySQL 数据库操作。为什么选择 PDO?是因为在之后会记录 ThinkPHP 框架的学习,而在该框架中,数据库的操作是基于 PDO 的,所以这里只选择 PDO 进行学习。
连接是通过创建 PDO 基类的实例而建立的。使用哪种驱动程序并不重要,始终都会用 PDO 类名。构造函数接受用于指定数据库源以及可选的用户名和密码(如果有)的参数。
示例 打开连接
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
} catch (PDOException $e) {echo "连接失败";
}
连接数据库成功后,返回 PDO 类的实例给脚本。此连接在 PDO 对象的生存周期中保持有效状态。要关闭连接,需要确保删除它的所有剩余引用来销毁对象——可以通过对对象变量赋值 null 来实现。如果没有明确这么做,PHP 在脚本结束时会自动关闭连接。
示例 关闭连接
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass);// 一些代码...$conn = null; // 关闭连接
} catch (PDOException $e) {echo "连接失败";
}
还可以创建数据库持久连接。持久连接不会在脚本结束时关闭,而是会缓存,且当另一个脚本使用相同凭证请求连接时重用。持久连接缓存可以避免每次脚本需要与数据库通信时建立新连接的开销,从而让 web 应用程序更快。
示例 持久化连接
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));
} catch (PDOException $e) {echo "连接失败";
}
二、预处理语句
很多更成熟的数据库都支持预处理语句的概念。什么是预处理语句?可以把它看作是想要运行的 SQL 的一种编译过的模板,它可以使用变量参数进行定制。预处理语句可以带来两大好处:
- 查询仅需解析(或预处理)一次,但可以用相同或不同的参数执行多次。当查询准备好后,数据库将分析、编译和优化执行该查询的计划。对于复杂的查询,此过程要花费较长的时间,如果需要以不同参数多次重复相同的查询,那么该过程将大大降低应用程序的速度。通过使用预处理语句,可以避免重复分析/编译/优化周期。简言之,预处理语句占用更少的资源,因而运行得更快。
- 提供给预处理语句的参数不需要用引号括起来,驱动程序会自动处理。如果应用程序只使用预处理语句,可以确保不会发生SQL 注入。(然而,如果查询的其他部分是由未转义的输入来构建的,则仍存在 SQL 注入的风险)。
下面我们来创建一张用于程序测试的表格:
CREATE TABLE test( id int NOT NULL PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255),value VARCHAR(255)
);
稍后我们将通过示例来说明如何使用预处理语句来完成数据库表的增、删、改、查操作。
1、事务与自动提交
数据库操作是离不开事务的,在开始进行查询前,必须先理解 PDO 是如何管理事务的。
当第一次打开连接时,PDO 将在所谓的"自动提交"模式下运行。自动提交模式意味着,如果数据库支持,运行的每个查询都有它自己的隐式事务,如果数据库不支持事务,则没有。如果需要一个事务,则必须用 PDO::beginTransaction() 方法来启动。如果底层驱动不支持事务,则抛出一个 PDOException 异常。一旦开始了事务,可用 PDO::commit() 或 PDO::rollBack() 来完成,这取决于事务中的代码是否运行成功。
2、插入数据
首先,我们先来看一个使用预处理语句进行插入数据的示例:
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));// 1、通过用 name 和 value 替代相应的命名占位符来执行一个插入查询 $stmt = $conn->prepare("INSERT INTO test (name, value) VALUES (:name, :value)");$stmt->bindValue(':name', 'zhangsan');$stmt->bindValue(':value', 'one');$stmt->execute(); // 执行// 2、通过用 name 和 value 取代 ? 占位符的位置来执行一条插入查询$stmt = $conn->prepare("INSERT INTO test (name, value) VALUES (?, ?)");$stmt->bindValue(1, 'lisi');$stmt->bindValue(2, 'two');$stmt->execute(); // 执行// 关闭连接$stmt = null;$conn = null;
} catch (PDOException $e) {echo "连接失败";
}
现在对示例进行一下说明:
1、PDO::prepare:预处理要执行的语句,并返回语句对象。
public PDO::prepare(string $query, array $options = []): PDOStatement|false
为 PDOStatement::execute() 方法传入预处理待执行的 SQL 语句。语句模板可以包含零个或多个参数占位标记,格式是命名(:name)或问号(?)的形式,当它执行时将用真实数据取代。在同一个语句模板里,命名形式和问号形式不能同时使用,只能选择其中一种参数形式。
参数
query | 必需。必须是对目标数据库服务器有效的 SQL 语句模板。 |
options | 可选。数组包含一个或多个 key=>value 键值对,为返回的 PDOStatement 对象设置属性。常见用法是:设置 PDO::ATTR_CURSOR 为 PDO::CURSOR_SCROLL,将得到可滚动的光标。某些驱动有驱动级的选项,在 prepare 时就设置。 |
返回值
如果数据库服务器已经成功预处理语句,PDO::prepare() 返回 PDOStatement 对象。如果数据库服务器无法预处理语句,PDO::prepare() 返回 false 或抛出 PDOException。
2、PDOStatement:PDO::prepare() 预处理语句成功后会返回该对象。代表一条预处理语句,并在该语句被执行后代表一个相关的结果集。
3、PDOStatement::bindValue:绑定一个值到用作预处理的 SQL 语句中的对应命名占位符或问号占位符。
public PDOStatement::bindValue(string|int $param, mixed $value, int $type = PDO::PARAM_STR): bool
参数
param | 必需。参数标识符。对于使用命名占位符的预处理语句,应是类似 :name 形式的参数名。对于使用问号占位符的预处理语句,是以1开始索引的参数位置。 |
value | 必需。绑定到参数的值。 |
type | 可选。使用 PDO::PARAM_* 常量明确地指定参数的类型。 |
返回值
成功时返回 true, 或者在失败时返回 false。
注意:bindValue() 方法只能绑定值,如果要绑定变量,需要使用 bindParam() 方法。
注意:bindValue() 方法只能绑定值,如果要绑定变量,需要使用 bindParam() 方法。
3、查询数据
现在,我们将刚才插入的数据使用查询语句将其显示在页面上:
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));$stmt = $conn->prepare('SELECT id, name, value FROM test');$stmt->execute();foreach ($stmt as $row) {echo 'id:' . $row['id'] . ',name:' . $row['name']. ',value:' . $row['value'] . '<br>';}// 关闭连接$stmt = null;$conn = null;
} catch (PDOException $e) {echo "连接失败";
}
现在对示例进行一下说明:
1、PDOStatement:代表一条预处理语句,并在该语句被执行后代表一个相关的结果集。可以使用 foreach 语句对其进行遍历,其中查询的每条记录以关联数组的形式进行存储,可以使用 $row[列名] 的方式得到指定列的数据。
4、修改数据
我们将 zhangsan 对应的 value 更改为大写的”ONE“。
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));$stmt = $conn->prepare('UPDATE test SET value=:value WHERE name=:name');$value = 'ONE';$name = 'zhangsan';$stmt->bindParam("value", $value);$stmt->bindParam("name", $name);$stmt->execute();echo $stmt->rowCount() . '条记录被更新';// 关闭连接$stmt = null;$conn = null;
} catch (PDOException $e) {echo "连接失败";
}
现在对示例进行一下说明:
1、PDOStatement::bindParam 方法与 PDOStatement::bindValue 做的事情相同,不同的是:bindParam() 方法绑定的是变量,bindValue() 方法绑定额是值。
2、PDOStatement::rowCount:返回受上一个 SQL 语句影响的行数。
5、删除数据
将 name 为 "zhangsan" 的数据进行删除。
<?php
try {$user = 'root'; // 数据库用户名$pass = '123456'; // 数据库密码$conn = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(PDO::ATTR_PERSISTENT => true));$stmt = $conn->prepare('DELETE FROM test WHERE name=:name');$stmt->bindValue('name', 'zhangsan');$stmt->execute();echo $stmt->rowCount() . '条记录被删除';// 关闭连接$stmt = null;$conn = null;
} catch (PDOException $e) {echo "连接失败";
}
该示例没有什么需要进行说明的,操作的方法与上面的修改数据是一样的。
到这里,本篇文章记录了如何进行数据库连接、增删改查操作,更多的关于PHP数据库的内容,可以查看 数据库扩展 进行学习。
这篇文章是PHP语法学习的最后一篇文章,从下一篇文章开始,将记录 ThinkPHP 8 的学习过程。