《PHP精粹:编写高效PHP代码》——2.4节PHP数据库对象

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介:

本节书摘来自华章社区《PHP精粹:编写高效PHP代码》一书中的第2章,第2.4节PHP数据库对象,作者:(美)  Davey Shafik,更多章节内容可以访问云栖社区“华章社区”公众号查看

2.4 PHP数据库对象
如果之前你使用过PHP和MySQL,你可能用过mysql或mysqli类库连接到数据库,如使用mysql_connect()函数。多年来,这是连接到MySQL数据库的标准方式,并且对于其他数据库平台也使用同样的方式。
这些类库可以直接使用,并形成了无数PHP应用程序类库和框架的基础。这种方式的缺点是每个扩展都与其他稍有不同,因此使得代码在数据库平台之间轻松转移变得复杂。虽然这些数据库特定类库依然活跃并且运转良好,但是本章中仍将专注讲解更先进的PDO扩展。创建的PDO扩展提供了一组统一功能与各种数据库平台的对话。PHP 5采用的就是面向对象的扩展,当时人们将它的很多特性引入了PHP语言。
理解OOP
如果你不熟悉面向对象编码,并且已仔细阅读第1章,现在你正好可以了解使用OOP的更多内容。

然而,PDO并没有解决问题,这个问题是出现在不同数据库平台之间的SQL语法差异;因此,乍看起来似乎这个扩展并不完全是最有效的手段。PDO可以和各种各样的数据库平台连接和对话,但是为了创建真正独立于平台的应用程序,我们不得不改写发送的SQL语句。
PDO是一个抽象层,这表明它建立在PHP以及PHP连接数据库的方式之间。PDO提供了非常简洁的功能来执行查询和遍历数据集,让我们深入研究如何使用PDO的技术细节吧。

2.4.1 使用PDO连接到MySQL
要使用PDO连接到数据库,需要实例化一个新的PDO对象并且传递一个DSN,如果需要,还要加上用户名和密码。DSN(Data Source Name, 数据源名称)由描述实际连接的数据结构组成。若要连接到创建(数据库名称recipes,使用localhost作为主机名)的数据库,将使用下面的PHP代码生成连接:


cd1e84ba87c13097ef5f46e537e2739a9d6eddf3


06afb1a219b17dd39bdf2b32ac9ba661ec446896

2.4.2 从表中选择数据
创建了PDO对象之后,就可以检索数据了。首先,在数据库中会有怎样的食谱清单呢?当用PDO选择数据时,要创建一个PDOStatement对象。它代表查询,并使我们获取结果。对于一个基本的查询,可以使用PDO::query()方法:


0cb7af03a94aa710b1490144e862def472b8b4b3

使用ORDER对结果排序
当我们像这样从MySQL中选择数据时,将得到以未定义的顺序返回的记录;通常这些记录是按照插入的顺序排序的。为成为更完美的应用,可以在查询的末尾添加这样的命令:ORDER BY created DESC。将按时间的降序返回结果,即总是先看到最新的食谱。

以上示例利用了PDOStatement::fetch()方法,此方法能处理大量提取数据的模式。
2.4.3 数据提取模式
在前面的示例中,我们看到了如何用PDOStatement对象来表示查询及其数据集。每次调用fetch()方法,都将从结果集中接收到另外一行。还可以使用fetchAll()方法一次检索所有的行。这两种方法都接受fetch_style参数,这个参数定义如何格式化结果集。
PDO提供了便于使用的常量:
PDO::FETCH_ASSOC完成了以前你在while循环中看到的,它使用键组返回数组到列名。
PDO::FETCH_NUM也返回数组,但这次使用数字键。
PDO::FETCH_BOTH(默认值)结合了PDO::FETCH_ASSOC和PDO::FETCH_NUM以提供一个每个值出现两次的数组,一次使用其列名,一次使用数字索引。
PDO::FETCH_CLASS返回一个已命名的类的对象而不是数组,这些值以列的名字命名设置到属性中。
为了看到由PDO::FETCH_ASSOC返回的结果,可以输入下列代码:


c6597b482d27a214775c2035eec9750cd0380292

这里有一些正在运行,让我们依次看看它们。
首先,通过传递SQL语句进入prepare()方法创建了PDOStatement。仔细观察这条SQL语句,你可能会看到一些奇怪的东西。在:recipe_id前面的冒号表示这是一个占位符(placeholder)。在实际运行这个查询之前,会用真正的值来替换这个占位符。
然后,execute()这个查询。必须为字符串中的每个占位符传入值,而且还要将这些字符串传入prepare()方法中。因为使用指定的占位符,所以要创建一个由与这些占位符数量相同的元素组成的数组。每个占位符都有一个与之匹配的数组元素,数组元素的名字作为键值,然后要用它的实际值来替换键值。
既然已知道只能返回一行,可以通过调用一次fetch()方法来代替循环。

生成SQL语句
在之前的示例中,定义了一个单独的$sql变量来保存这个字符串并传入PDO::prepare。这种方法可以让代码更易于阅读,并且在需要建立一个更复杂的查询时提供帮助。这种方式也可以帮助我们进行调试,你可以不费力地检查有哪些东西传入了prepare()。

占位符不需要名字,你也可以使用??符号为变量保留一个位置作为没有命名的占位符。此外,在SQL语句中有很多这样的占位符,用它们来创建PDOStatement,而且作为数组把这些值传入execute()中,但在这个例子中,我们必须将这些值按顺序排列在查询语句中。用下面的示例很容易说明这些:
如果查询变得很庞大或很复杂,命名占位符可使你更容易保存代码。将数组中命名的键值传入execute(),比起应付一个巨大的用数字作为索引的数组,这种方式让你更容易看出哪个值属于哪个参数。
预处理语句使我们清楚地标识出查询中哪些部分是数据库语言,哪些包含可变数据。你会听说“安全咒语”:“过滤输入,避免输出”(如果你还未听说,在第5章将很快看到)。当使用数据库时,必须溢出已经发送到数据库的值(也就是说删除不需要的字符)。你可能见过像mysql_escape_string()这样的MySQL功能。当使用预处理语句时,为占位符传入的值已经溢出,因为MySQL知道这些都是可能改变的值。这种额外的安全保障是将PDO及预处理语句作为规范令人信服的一个原因。

2.4.5 绑定值和预处理语句的变量
既然MySQL已经准备了一个查询,那么使用不同的值再次运行这个查询时只会有很小的系统开销。我们已经知道了如何传递变量到PDOStatement的execute()方法中。在本节中,我们将看到如何绑定值甚至变量到语句中,以便在每次执行查询时都使用这些值或变量。
解释概念的简单例子
这些例子可能看起来确实没有什么意义,但举例说明数据集里更多先进的技术的确是种乐趣!如果你问自己:“为什么我要尝试这些呢?”要记住这些都是适用于你自己项目的技术(可能用在更复杂的设置中)。

虽然这是事实,但总的来说,我们最好用尽可能少的步骤从数据库中检索数据,有时你使用的查询类型意味着这些步骤不能组合使用。当使用不同的值重复调用相同的查询时,可以设置一些元素用于每次查询。
例如,如果我们总想使用同样的chef值,可以使用PDOStatement::bindValue():


85fa0a699aa42fd388ff4f2a007be74ad1526ffa

最后这两个例子表明了在调用execute()方法之前如何设定值或变量到PDOStatement对象。不管你是使用bindValue()方法、bindParam()方法或是传入值到execute()方法本身,预处理语句都是极为有用的!如果我们多次运行这个语句,这种方式不仅能够提高代码的性能,而且也可以毫无疑问地溢出占位符。

2.4.6 插入一行并获取ID
前面已经深入研究了SELECT语句的选项,但是INSERT和UPDATE语句又怎么样呢?实际上这几个语句看起来确实很相似,即我们预处理然后再执行一条语句。接下来以插入一些新的食谱作为示例:


7c8794c3aeaa38d16f9c67befd3b09897e394459

rowCount()是PDOStatement对象的一个方法,它会指出有多少行由于查询而发生改变。
2.4.8 删除数据
和插入或更新数据一样,我们以相同的方式删除数据,即对查询进行预处理然后再执行查询。如果想删除“Starter”这一类别(因为它未使用),可以这样做:


d59dfc66703bfd83d89be0cdfe5e1917051fa353

我们会再次使用rowCount()方法检查删除行,查看其数量是否和预期的一样多(很多缺失的或错误的WHERE子句比预期会造成更大损害)。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
2月前
|
存储 关系型数据库 数据库
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
本文通过一个 Agentic RAG 应用的完整构建流程,展示了如何借助 RDS Supabase 快速搭建具备知识处理与智能决策能力的 AI 应用,展示从数据准备到应用部署的全流程,相较于传统开发模式效率大幅提升。
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
|
3月前
|
人工智能 安全 机器人
无代码革命:10分钟打造企业专属数据库查询AI机器人
随着数字化转型加速,企业对高效智能交互解决方案的需求日益增长。阿里云AppFlow推出的AI助手产品,借助创新网页集成技术,助力企业打造专业数据库查询助手。本文详细介绍通过三步流程将AI助手转化为数据库交互工具的核心优势与操作指南,包括全场景适配、智能渲染引擎及零代码配置等三大技术突破。同时提供Web集成与企业微信集成方案,帮助企业实现便捷部署与安全管理,提升内外部用户体验。
406 12
无代码革命:10分钟打造企业专属数据库查询AI机器人
|
3月前
|
安全 druid Nacos
0 代码改造实现应用运行时数据库密码无损轮转
本文探讨了敏感数据的安全风险及降低账密泄漏风险的策略。国家颁布的《网络安全二级等保2.0标准》强调了企业数据安全的重要性。文章介绍了Nacos作为配置中心在提升数据库访问安全性方面的应用,并结合阿里云KMS、Druid连接池和Spring Cloud Alibaba社区推出的数据源动态轮转方案。该方案实现了加密配置统一托管、帐密全托管、双层权限管控等功能,将帐密切换时间从数小时优化到一秒,显著提升了安全性和效率。未来,MSE Nacos和KMS将扩展至更多组件如NoSQL、MQ等,提供一站式安全服务,助力AI时代的应用安全。
245 14
|
3月前
|
SQL 数据建模 关系型数据库
别光知道存数据库了,数据建模才是王道!(入门指南+实战代码)
别光知道存数据库了,数据建模才是王道!(入门指南+实战代码)
278 4
|
4月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
5月前
|
消息中间件 缓存 弹性计算
纯PHP+MySQL手搓高性能论坛系统!代码精简,拒绝臃肿
本内容分享了一套经实战验证的社交系统架构设计,支撑从1到100万用户的发展,并历经6次流量洪峰考验。架构涵盖客户端层(App、小程序、公众号)、接入层(API网关、负载均衡、CDN)、业务服务层(用户、内容、关系、消息等服务)、数据层(MySQL、Redis、MongoDB等)及运维监控层(日志、监控、告警)。核心设计包括数据库分库分表、多级缓存体系、消息队列削峰填谷、CQRS模式与热点数据动态缓存。同时提供应对流量洪峰的弹性伸缩方案及降级熔断机制,并通过Prometheus实现全链路监控。开源建议结构清晰,适合大型社交平台构建与优化。
211 11
|
6月前
|
人工智能 前端开发 JavaScript
代码采纳率从 22% 到 33%,通义灵码辅助数据库智能编码实践
通义灵码本质上是一个AI agent,它已经进行了大量的优化。然而,为了更完美或有效地调用模型的潜在能力,我们在使用时仍需掌握一些技巧。通常,大多数人在使用通义灵码时会直接上手,这是 AI agent 的一个优势,即 zero shot 使用,无需任何上下文即可直接使用通义灵码的能力。
|
6月前
|
数据库连接 应用服务中间件 PHP
|
2月前
|
安全 Java Nacos
0代码改动实现Spring应用数据库帐密自动轮转
Nacos作为国内被广泛使用的配置中心,已经成为应用侧的基础设施产品,近年来安全问题被更多关注,这是中国国内软件行业逐渐迈向成熟的标志,也是必经之路,Nacos提供配置加密存储-运行时轮转的核心安全能力,将在应用安全领域承担更多职责。