PHP之PDO的理解和实践

[ "\n\t\t\t\t\t", "\t概念", "\n", "\n什么是PDO, 即PHP DATA OBJECT,是一种抽象的数据库接口, 其本身并不会有任何的数据库功能, 只是定义了一些数据操作的接口规范。", "\n如果需要针对某个数据库的操作, 需要对应的数据库类型的PDO驱动。这样才能真正的操作数据库.", "\n同时, PDO 是需要PHP的面向对象的支持", "\n", "\t优点", "\n", "\n那么PDO 有什么优点呢", "\n", "\t灵活", "\n", "\nPDO 提供的是一种抽象的接口定义, 每种实际的PDO驱动都要实现这套定义。", "\n既然接口一样, 那我们再代码中就能通过很少的代价对数据库进行替换了。 实际的操作只是更改了数据库驱动", "\n", "\t安全", "\n", "\n通过使用PDO的预处理语句,我们可以直接将查询参数传递进来, 而无需担心sql注入的问题", "\n", "\t快速", "\n", "\n当需要执行多个相似的sql查询语句, 而仅仅是查询参数不同的操作时, 通过预处理语句, 可以避免数据库对查询语句的重复 解析/编译/优化 的过程, 从而提高运行速度", "\n", "\tPDO 类说明", "\n", "\nPDO 的实现有主要有两个类, 在此说明下", "\n", "\tPDO 类", "\n", "\n这个类主要用于保存用户的数据库连接, 并设置连接参数,操作事务", "\n通过这个类的实例, 我们还可以获取一些当前使用的数据库驱动的参数", "\n比如: 当前连接是否处于事务状态,当前使用的是哪种数据库驱动", "\n通过这个类实例的prepare方法, 我们可以预处理要执行的sql语句,这个函数返回的结果实际上就是下面要说的另外一个类PDOStatement类的实例", "\n此外, 这个类的实例也可以直接对数据库发起请求, 比如exec和query 函数", "\n", "\tPDOStatement 类", "\n", "\n代表一条预处理语句,并在该语句被执行后代表一个相关的结果集。", "\n这是官方的一个说明。太过简单", "\n上面 PDO 类实例的prepare query函数的返回结果, 实际上也是PDOStatement的实例。", "\n这个类的实例拥有对数据库请求结果的多种操作和设置, 特别是,为PDO连接预处理sql语句时,其拥有的快速安全的特性,很受推崇", "\n", "\t实践", "\n", "\n", "\tPDO 类实践", "\n", "\n", "\t连接数据库", "\n", "\n", "$dbh = new PDO(\"mysql:host=localhost;dbname=test_db;charset=utf8\",\"root\", \"123456\");\n", "\n这里我们连接上数据库后创建了一个PDO的类实例$dbh", "\n", "\t获取数据库驱动类型", "\n", "\n", "var_dump($dbh->getAvailableDrivers());", "\n可以看到输出是", "\n", "array(1) {\n [0]=>\n string(5) \"mysql\"\n}", "\n", "\t执行query", "\n", "\nvar_dump($stm = $dbh->query(\"select id,title from article\",PDO::FETCH_NAMED));", "\n这里我们直接使用PDO的query 函数,第二个参数控制返回结果集, 比如返回的是一个关联数组, 用字段名作key,用查询结果做value时用PDO::FETCH_NAMED 参数, 或者要返回一个索引数组, 直接用偏移量做key时, 用PDO::FETCH_NUM参数", "\n在这里, 我们可以通过var_dump得到$stm的结果实际上就是PDOStatement类的实例", "\n输出如下", "\n", "object(PDOStatement)#2 (1) {\n [\"queryString\"]=>\n string(28) \"select id,title from article\"\n}", "\n结果处理部分参考 PDOStatement 类实践", "\n", "\t预处理", "\n", "\n当我们有多个相似的语句, 这些语句唯一的区别就是参数不同, 或者需要执行多次的语句, 这个时候我们就比较适合使用预处理语句了", "\n预处理语句可以预先解析,编译,优化sql语句, 返回一个PDOStatement类实例, 再调用该实例的execute函数, 传递不同的参数就能得到不同的结果, 同时, 因为避免了多次解析编译优化的过程, 可以使得后面的query请求更快", "\n", "$stm = $dbh->prepare(\"select id,title from article where cid = :cid\");", "\n至于其中的:cid,其实只是一个占位符, 后面通过调用PDOStatement的bindParam 或 bindValue方法就可以通过变量将参数传递进去, 这里的:cid是关键词占位符, 每个关键词不可重复", "\n或者这里也可以是", "\n", "$stm = $dbh->prepare(\"select id,title from article where cid = ?\");", "\n? 也是一个占位符, 通过调用PDOStatement的bindParam或bindValue 方法也能传递参数进去, 也可以通过在调用execute时传递一个索引数组,数组里面的元素与?占位符一一对应, 也能传递参数到sql的请求里", "\n", "\tPDOStatement 类实践", "\n", "\n在上面我们通过两种方式得到了这个类实例", "\nPDO 的query 函数 和 PDO的预处理函数prepare", "\n我们先说对调用prepare 后的这个类实例", "\n", "\tbindParam", "\n", "\n在prepare当我们使用占位符时,我们可以通过bindParam 将关键字与某个变量关联起来, 然后调用execute, 就能得到传递了参数的查询结果", "\n如", "\n", "$cid = 6;\nvar_dump($stm = $dbh->prepare(\"select id,title from article where cid = :cid\"));\n$stm->bindParam(\":cid\",$cid);\n$stm->execute();", "\n注意这里的绑定是引用绑定, 如果后面我们继续如下", "\n", "$cid = 5;\n$stm->execute();", "\n实际的查询将是直接传递新的cid参数而无须再次绑定", "\n对使用?来说, 绑定的关系只是从关键词变成了偏移量,", "\n", "$cid = 6;\nvar_dump($stm = $dbh->prepare(\"select id,title from article where cid = ?\"));\n$stm->bindParam(0,$cid);\n$stm->execute();", "\n", "\tbindValue", "\n", "\nbindValue 与 bindParam 其实很类似, 也是为占位符填充具体的参数,绑定的方式基本上也与bindParam一致,只要把bindParam 换成 bindValue 即可", "\n但其余bindParam有什么区别呢", "\nbindParam 是引用方式绑定的变量, 只要变量做了修改, 那么query 传递的参数就发生了变化。bindParam的例子里有说明", "\n而bindValue 只是绑定具体的值, 而非引用方式, 所以query 传递的参数一旦需要修改,就要重新bindValue,所以要注意这点", "\n", "\texecute", "\n", "\n在prepare并绑定好参数后, 我们就可以直接调用execute 去执行query请求了", "\n", "$stm->bindParam(\":cid\",$cid);\n$stm->execute();", "\n假如prepare的query 中用的是?占位符, 还可以直接使用 execute 传递一个数组, 数组元素与query中的?一一对应, 也是可以传递请求参数的", "\n", "\tsetFetchMode", "\n", "\n设置返回结果的格式, 比如是返回关联数组(PDO::FETCH_NAMED)结果还是索引数组(PDO::FETCH_NUM)结果", "\n", "$stm->setFetchMode(PDO::FETCH_NAMED);", "\n", "\tfetch 系列函数", "\n", "\n然后我们就可以通过fetch系列函数返回结果了", "\n如:", "\n", "$stm->fetchall();//返回所有查询结果\n$stm->fetch();//从结果集的下一行返回结果\n$stm->fetchColumn();//从结果集中的下一行返回单独的一列。\n$stm->fetchObject();// 从结果集的下一行返回结果并以对象的方式返回", "\n对PDO的query函数的执行结果就可以通过这些函数返回", "\n", "\tcloseCursor", "\n", "\n关闭游标,使语句能再次被执行。", "\n在有些数据库驱动中, 如果查询结果不被fetch 完, 后续的查询就不能继续执行, 所以再查询完成后, 我们要调用这个函数,防止这种问题发生", "\n", "\t总结", "\n", "\n以上一个完整的例子如下:", "\n", "try{\n $dbh = new PDO(\"mysql:host=localhost;dbname=test_db;charset=utf8\",\"root\",\"1\");\n}catch(PDOException $e){\n die(\"Error: $e\");\n}\n\nvar_dump($dbh->getAvailableDrivers());\n$cid = 6;\n$stm = $dbh->prepare(\"select id,title from article where cid = :cid\");\n$stm->bindParam(\":cid\",$cid);\n$stm->execute();\n$stm->setFetchMode(PDO::FETCH_NAMED);\nwhile($row = $stm->fetch()){\n var_dump(\"hello\");\n var_dump($row);\n}\n$stm->closeCursor();", "\n\t\t\t\t\t\n\t\t\t\t\t", "\n\t\t\t\t\t\n\t\t\t\t" ]