首先思考, 为何选择PDOphp
PDO
是一个数据访问抽象层(Database Access Abstraction Layer). 抽象是双重的: 一个是众所周知但不过重要的. 另外一个是模糊的可是是最重要的.
众所周知 PDO
为不一样的数据库提供了统一的接口. 虽然这个功能自己很庞大, 可是对于固定程序来讲不是过于重要的事情, 基本全部的程序都是使用统一的后端数据库. 尽管有一些谣言, 可是经过改变单行 PDO
配置来切换后端数据库是不可能的-因为不一样的 SQL
风格(为此, 须要使用像 DQL
这样的平均查询语言). 所以对于普通的 LAMP
开发者来讲, 这一点是微不足道的, 而且对他而言, PDO只是熟悉的 mysql(i)_query()
函数的另外一个更复杂版本. 但实际上它不是, 它有丰富的其余功能.PDO
不只抽象了数据库API, 还抽象了基本操做, 不然必须在每一个应用程序中重复数百次, 使您的代码很是WET. 不一样于 mysql
和 mysqli
, 两个都不能直接使用低级裸 APIs
(但仅做为某些更高级别抽象层的构建材料), PDO
就是这样的抽象. 虽然还是不完整的, 可是至少可用.
真正的PDO好处是:mysql
请注意, 尽管 PDO
是原生数据库驱动程序中最好的, 但对于现代WEB应用程序来讲, 请考虑将使用有查询构建器的 ORM
或者与其余更高抽象级别的库一块儿使用, 只是偶尔使用原生的PDO. 好的ORM好比 Doctrine
, Eloquent
, RedBean
和 Yii::AR
. Aura.SQL
是具备不少附加功能的使用PDO包装器的一个很好的例子.
不管哪一种方式, 首先要了解基本工具是件好事. 那么, 让咱们开始吧:sql
PDO
有一个叫 DSN
的预想接方式. 它并不复杂-PDO须要你在三个不一样的位置输入不一样的配置, 而不是一个简单的选项列表.数据库
database driver
, host
, db(schema) name
和 charset
, 以及不常使用的 port
和 unix_socket
设置 DSN
user_name
和 password
设置构造方法其中 DSN
是以分号分隔的字符串, 由 param=value
键值对组成, 从驱动程序名称和冒号开始:后端
mysql:host=localhost;dbname=test;port=3306;charset=utf8mb4 driver^ ^colon ^param=value pair ^semicolon
注意, 遵循正确的格式是很是重要的- DSN中不能使用 空格, 引号, 和其余的符号, 只能使用参数, 值和定界符. 就像手册上展现的.数组
这里有一个例子:安全
$host = '127.0.0.1'; $db = 'test'; $pass = 'root'; $charset = 'utf8mb4'; $dsn = "mysql:host={$host};dbnamej={$db};charset={$charset}"; $options = [ PDO::ATR_ERRMODE => PDO::ERRMODE_EXECPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]; try { $pdo = new \PDO($dsn, $user, $pass, $options); } catch (\PDOException $e) { }
设置了全部上述变量属性, 咱们将在 $pdo
变量中获得一个正确的 PDO
实例.
使用旧mysql扩展用户重要通知oracle
mysql_*
函数, 能够在代码的任意位置使用, pdo
实例被存储在一个变量中, 那就意味着只能在函数内部进行访问. 所以, 必须经过函数参数传递或使用更高级的技术, 好比IOC容器.PDO
实例, 让整个脚本使用.(适用于FPM模式)Query
运行SET NAMES
或者经过 PDO::MYSQL_ATTR_INIT_COMMAND
. 只有当PHP版本太低时(低于5.3.6), 才可使用 SET NAMES
查询, 而且关闭仿真模式.更多关于链接的内容能够在 链接MySQL查看socket
使用 PDO
有两种方式运行查询. 若是查询中没有使用变量, 可使用 PDO::query
方法. 它会运行查询并返回一个 PDOStatement
类的对象, 该类与 mysql_query
返回的资源大体相同, 特别时从中获取实际记录的操做:ide
$stmt = $pdo->query('SELECT name FROM users'); while ($row = $stmt->fetch()) { echo $row['name'] . "\n"; }
而且 query()
方法容许咱们使用一个整洁的方法链接 SELECT
查询, 以下所示.
放弃熟悉的 mysql_query()
函数 并进入严格数据对象领域的主要缘由是 PDO
已经准备好了开箱即用的预处理语句. 若是要在语句中使用变量, 预处理语句是惟一正确运行的方式. 它如此重要的缘由在 The Hitchhiker's Guide to SQL Injection prevention.有详细的解释.
对于运行的查询, 若是至少使用一个变量, 你必须使用占位符替换它. 准备执行语句, 而后分别传入变量执行.
长话短说, 它不像感受的那么困难. 在大多数例子中, 你只须要使用函数 prepare
和 execute
.
首先, 须要修改查询, 在使用变量的位置添加占位符, 就像这样
$sql = "SELECT * FROM users WHERE email = '{$email}' AND status = '{$status}'";
改成
$sql = "SELECT * FROM users where email = ? and status = ?";
或者
$sql = "SELECT * FROM users where email = :email AND status = :status";
注意 PDO
支持位置(?)和命名(:email)占位符, 后者始终以冒号开始,而且只能使用字母, 数字和下划线. 还须要注意 占位符周围不能使用引号 .
一个查询使用了占位符, 就必须使用PDO::prepare()
方法预处理. 这个方法返回一个和咱们上边讨论的相同的 PDOStatement
对象, 可是没有绑定任何数据.
最后, 必须使用 PDOStatement
对象的 execute()
方法执行查询, 而且经过数组形式传递参数. 以后, 就能够从语句中获得结果数据(若是适用).
$stmt = $pdo->prepare("SELECT * FROM users WHERES email = ? AND status = ?"); $stmt->execute([$email, $status]) $user = $stmt->fetch(); // or $stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email AND status = :status"); $stmt->execute(['email' => $email, 'status' => $status]); $user = $stmt->fetch();
能够看到, 位置占位符, 你须要提供一个索引数组. 命名占位符, 须要提供一个关联数组, 而且键要匹配查询中的占位符. 同一个查询中不能混合位置占位符和命名占位符.
位置占位符可让你写更简短的代码, 可是对参数顺序是敏感的(必须于查询中参数的顺序一致). 虽然命名占位符使代码更冗长, 可是容许随机参数绑定.
另外须要注意, 虽然存在广泛的误解, 可是数组键中 :
不是必须的.
执行后就可使用支持的方法获取结果.
更多的例子能够查看(respective article)[https://phpdelusions.net/pdo_...].
将数据传入 execute()
(如上所示)方法中应被视为默认的最方便的方式. 若是使用这个方法, 全部参数都将会绑定为字符串(若是使用NULL值, 将会使用SQL NULL发送给查询), 大多数时候都没有问题.
可是, 有时候最好明确设置类型. 可能状况以下:
bigint
boolean
必须绑定精确的操做数(为了将 BIGINT
绑定为 PDO::PARAM_INT
须要基于 mysqlnd
)这种状况下, 必须使用显式绑定, 能够从 bindvalue()
和 bindParam()
两个函数中选择一个. 前者是推荐使用的, 它不像 bindParam()
具备必定的反作用.
了解哪些查询部分可使用参数绑定哪些部分不能使用是很是重要的. 事实上, 这个列表是很是短的: 只有字符串和数字字面量能够被绑定. 只要你的数据在查询中能被表示为数字或者带引号的字符串, 就能够被绑定. 其余全部状况你不能使用 PDO
预处理语句: 既不是标识符也不是逗号分隔列表, 或者是引用的文字字符串的一部分, 或者其余任意查询部分都不能使用预准备语句绑定
最多见的用例解决方案能够在[本章的响应部分查看]()
有时候你可使用预处理屡次执行准备好的查询, 比一次又一次执行相同的查询快一点, 由于它只解析查询一次. 若是能够执行另外一个PHP实例中的预处理语句, 这个功能就是很是有用的, 可是事实并不是如此. 只会在同一个实例中重复相同的查询, 这在常规的PHP脚本中不多使用到, 并限制了此功能用于重复插入和更新.
$data = [ 1 => 1000, 2 => 200, 3 => 200, ]; $stmt = $pdo->prepare('UPDATE users SET bonus = bonus + ? where id = ?'); foreach ($data as $id => $bonus) { $stmt->execute([$bonus, $id]); }
注意这个功能有点被高估了. 不只须要讨论, 并且性能提高也不是很大 - 查询解析有时候是
很快的. 并且只有在关闭仿真模式的时候才能带来性能提高.
这些查询没有什么特别之处, 对PDO来讲他们都是同样的. 运行哪一个查询并不重要.
如上所示, 须要准备带有占位符的预处理查询, 传入变量并执行. DELETE
和 SELECT
的处理过程是基本相同的. 仅有的不一样点是( DML
查询不会返回任何数据), 你可使用链式方法, 调用 execute()
和 prepare()
.
$sql = "UPDATE users SET name = ? where id = ?"; $pdo->prepare($sql)->execute([$name, $id]);
然而, 你像得到影响行数, 代码将和无聊的三行代码相同:
$stmt = $pdo->prepare("DELETE FROM goods where category = ?"); $stmt->execute([$cat]); $deleted = $stmt->rowCount();
更多的例子能够在respective article.找到.
咱们已经见过这个函数了, 如今让咱们仔细看看. 它从数据库获取单行数据, 在结果集中移动内部指针, 所以, 对函数的后续调用将逐个返回全部行. 这个方法和 mysql_fetch_array()
大体相同但在工做模式稍微有点不一样: 代替不少不一样函数( mysql_fetch_assoc()
mysql_fetch_row
), 这个只有一个方法, 可是它的行为能够经过一个参数改变. 在 PDO
中有不少的获取模式, 稍后咱们详细讨论, 这里有一些简单的实例:
PDO::FETCH_NUM
返回索引数组PDO::FETCH_ASSOC
返回关联数组PDO::FETCH_BOTH
以上二者都包含PDO::FETCH_OBJ
返回对象PDO::FETCH_LAZY
容许三个(索引数组, 关联数组, 对象)方法没有内存开销.从上面能够看出, 这个必须在两种状况下使用:
当只须要一行时, 只获取一行
$row = $stmt->fetch(PDO::FETCH_ASSOC);
将以关联数组的方式从语句中获取一行
另外一种有用的模式是 PDO::FETCH_CLASS
能够建立一个特定类的对象
$news = $pdo->query("select * from news")->fetchAll(PDO::FETCH_CLASS, 'News');
将生成一个News类对象的数组, 而且经过返回值设置类属性. 注意这个模式下:
__set
魔术方法__set
方法, 将会建立新属性