$dns='mysql:host=localhost;dbname=blog_test;port=3306;charset=utf8';$pdo=newPDO($dns,'root','');
普通情形下,我们直接实例化的时候通报布局参数就可以得到一个 PDO 工具。这样,我们就和数据库建立了连接。如果连接失落败,也便是参数写得有问题的时候,在实例化时直接就会报非常。
PDO 工具的参数包括 DNS 信息、用户名、密码,其余还有一个参数便是可以设置 PDO 连接的一些属性,我们将在后面看到它的利用。
PDO 布局参数的第一个参数是一个 DNS 字符串。在这个字符串中利用分号 ; 分隔不同的参数内容。它里面可以定义的内容包括:

我们可以通过一个函数来查看当前 PHP 环境中所支持的数据库扩展都有哪些:
print_r(PDO::getAvailableDrivers());exit;//Array//(//[0]=>dblib//[1]=>mysql//[2]=>odbc//[3]=>pgsql//[4]=>sqlite//)
PDO 工具属性
PDO 布局参数的末了一个参数可以设置连接的一些属性,如:
$pdo=newPDO($dns,'root','',[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);showPdoAttribute($pdo);//……//PDO::ATTR_ERRMODE:2//……
showPdoAttribute() 方法是我们自己封装的一个展示所有连接属性的函数。
//显示pdo连接属性functionshowPdoAttribute($pdo){$attributes=array("DRIVER_NAME","AUTOCOMMIT","ERRMODE","CASE","CLIENT_VERSION","CONNECTION_STATUS","ORACLE_NULLS","PERSISTENT","SERVER_INFO","SERVER_VERSION");foreach($attributesas$val){echo"PDO::ATTR_$val:";echo$pdo->getAttribute(constant("PDO::ATTR_$val"))."\n";}}
在这个函数中,我们利用 PDO 实例的 getAttribute() 方法来获取相应的属性值。在没有设置 PDO::ATTR_ERRMODE 时,它的默认值为 0 ,也便是 PDO::ERRMODE_SILENT 常量所对应的值。在上述代码中,我们将它设置为了 PDO::ERRMODE_EXCEPTION ,查看属性输出的结果就变成了 2 。
除了在布局函数的参数中设置属性外,我们也可以利用 PDO 实例的 setAttribute() 方法来设置 PDO 的属性值。
pdo2=newPDO($dns,'root','',[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION]);echo$pdo2->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE),PHP_EOL;//4//设置属性$pdo2->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);echo$pdo2->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE),PHP_EOL;//2
在这段代码中,我们设置 PDO::ATTR_DEFAULT_FETCH_MODE 为 PDO::FETCH_ASSOC 。这样,在利用这个 $pdo2 的连接进行查询时,输出的结果都会因此数组键值对形式返回的内容。我们立时就进入查询方面干系函数的学习。
查询语句大多数情形下,利用 PDO 我们都会用它的预处理能力来编写 SQL 语句,一来是性能更好,二来是更加安全。不过我们本日先不讲预处理方面的问题,还是以最原始的直接操作 SQL 语句的办法学习干系的一些函数。
普通查询及遍历//普通查询-遍历1$stmt=$pdo->query('selectfromzyblog_test_userlimit5');foreach($stmtas$row){var_dump($row);}//array(8){//["id"]=>//string(3)"204"//[0]=>//string(3)"204"//["username"]=>//string(5)"three"//[1]=>//string(5)"three"//["password"]=>//string(6)"123123"//[2]=>//string(6)"123123"//["salt"]=>//string(3)"ccc"//[3]=>//string(3)"ccc"//}//……//普通查询-遍历2$stmt=$pdo->query('selectfromzyblog_test_userlimit5');while($row=$stmt->fetch()){var_dump($row);}//array(8){//["id"]=>//string(3)"204"//[0]=>//string(3)"204"//["username"]=>//string(5)"three"//[1]=>//string(5)"three"//["password"]=>//string(6)"123123"//[2]=>//string(6)"123123"//["salt"]=>//string(3)"ccc"//[3]=>//string(3)"ccc"//}//……
PDO 实例的 query() 方法便是实行一条查询语句,并返回一个 PDOStatement 工具。通过遍历这个工具,就可以得到查询出来的数据结果集。
在代码中,我们利用了两种办法来遍历,实在它们的效果都是一样的。在这里,我们要关注的是返回的数据格式。可以看出,数据因此数组格式返回的,并且因此两种形式,一个是数据库定义的键名,一个因此下标形式。
查询结果集(数组、工具)实在大部分情形下,我们只须要数据库键名的那种键值对形式的数据就可以了。这个有两种办法,一是直策应用上文中我们定义好默认 PDO::ATTR_DEFAULT_FETCH_MODE 属性的 $pdo2 连接,另一个便是在查询的时候为 query() 方法指定属性。
$stmt=$pdo2->query('selectfromzyblog_test_userlimit5');foreach($stmtas$row){var_dump($row);}//array(4){//["id"]=>//string(1)"5"//["username"]=>//string(3)"two"//["password"]=>//string(6)"123123"//["salt"]=>//string(3)"bbb"//}//……$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_ASSOC);foreach($stmtas$row){var_dump($row);}//array(4){//["id"]=>//string(1)"5"//["username"]=>//string(3)"two"//["password"]=>//string(6)"123123"//["salt"]=>//string(3)"bbb"//}//……
当然,我们也可以直接让数据返回成工具的格式,同样的也是利用预定义的常量来指定 query() 或者 PDO 实例连接的属性就可以了。
$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_OBJ);foreach($stmtas$row){var_dump($row);}//object(stdClass)#4(4){//["id"]=>//string(1)"5"//["username"]=>//string(3)"two"//["password"]=>//string(6)"123123"//["salt"]=>//string(3)"bbb"//}//……
查询结果集(类)
上面返回工具形式的结果集中的工具是 stdClass 类型,也便是 PHP 的默认类类型。那么我们是否可以自己定义一个类,然后在查询完成后直接天生它的结果集呢?便是像是 ORM 框架一样,完成数据到工具的映射。既然这么说了,那当然是可以的啦,直接看代码。
classuser{public$id;public$username;public$password;public$salt;publicfunction__construct(){echo'func_num_args:'.func_num_args(),PHP_EOL;echo'func_get_args:';var_dump(func_get_args());}}classuser2{}//返回指定工具$u=newuser;$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_INTO,$u);foreach($stmtas$row){var_dump($row);}//object(user)#3(4){//["id"]=>//string(1)"5"//["username"]=>//string(3)"two"//["password"]=>//string(6)"123123"//["salt"]=>//string(3)"bbb"//}//……//空类测试$u=newuser2;$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_INTO,$u);foreach($stmtas$row){var_dump($row);}//object(user2)#2(4){//["id"]=>//string(1)"5"//["username"]=>//string(3)"two"//["password"]=>//string(6)"123123"//["salt"]=>//string(3)"bbb"//}//……
在这段代码中,我们定义了两个类,user 类有完全的和数据库字段对应的属性,还定义了一个布局方法(后面会用到)。而 user2 类则是一个空的类。通过测试结果来看,类的属性对付 PDO 来说并不主要。它会默认创建数据库查询到的字段属性,并将它赋值给工具。那么如果我们定义了一个 const 常量属性并给予相同的字段名称呢?大家可以自己考试测验一下。
对付 user 和 user2 来说,我们将它实例化了并通报给了 query() ,并且指定了却果集格式为 PDO::FETCH_INTO ,这样就实现了获取工具结果集的能力。但是 PDO 远比你想象的强大,我们还可以直接用类模板来获取查询结果集。
//根据类返回指定工具$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_CLASS,'user',['x1','x2']);foreach($stmtas$row){var_dump($row);}//func_num_args:2//func_get_args:array(2){//[0]=>//string(2)"x1"//[1]=>//string(2)"x2"//}//object(user)#4(4){//["id"]=>//string(1)"5"//["username"]=>//string(3)"two"//["password"]=>//string(6)"123123"//["salt"]=>//string(3)"bbb"//}//……
query() 方法直策应用查询结果集模式为 PDO::FETCH_CLASS ,并通报一个类模板的名称,PDO 就会在当前代码中查找有没有对应的类模板,得到的每个结果都会实例化一次。在这里,我们又多了一个参数,末了一个参数是一个数组,并且给了两个元素。估计有不幼年伙伴已经看出来了,这个参数是通报给类的布局方法的。记住,利用这个模式,每个元素都会实例化一次,结果集中的每个元素都是新创建的类(object(user2)#3,#号后面的数字是不同的工具句柄id),而 PDO::FETCH_INTO 则因此引用的形式为每个元素赋值(object(user2)#3,#号后面的数字是相同的工具句柄id)。也便是说,我们利用 PDO::FETCH_INTO 模式的时候,修正一个元素的值,其它的元素也会随着改变,如果利用一个数组去记录遍历的元素值,末了数组的结果也会是相同的末了一个元素的内容。
$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_INTO,$u);$resArr=[];foreach($stmtas$row){var_dump($row);$resArr[]=$row;}$resArr[0]->id=55555;print_r($resArr);//Array//(//[0]=>user2Object//(//[id]=>55555//[username]=>two//[password]=>123123//[salt]=>bbb//)//[1]=>user2Object//(//[id]=>55555//[username]=>two//[password]=>123123//[salt]=>bbb//)//[2]=>user2Object//(//[id]=>55555//[username]=>two//[password]=>123123//[salt]=>bbb//)//[3]=>user2Object//(//[id]=>55555//[username]=>two//[password]=>123123//[salt]=>bbb//)//[4]=>user2Object//(//[id]=>55555//[username]=>two//[password]=>123123//[salt]=>bbb//)//)
如何办理这个问题呢?最大略的办法便是在数组赋值的时候加个 clone 关键字呗!
末了轻松一点,我们看下 query() 方法还可以指定查询的某一个字段。
//只返回第几个字段$stmt=$pdo->query('selectfromzyblog_test_userlimit5',PDO::FETCH_COLUMN,2);foreach($stmtas$row){var_dump($row);}//string(32)"bbff8283d0f90625015256b742b0e694"//string(6)"123123"//string(6)"123123"//string(6)"123123"//string(6)"123123"
增、删、改操作
除了查询之外的操作,我们也可以利用 exec() 方法来实行其他一些相应的 SQL 语句。
增加操作$count=$pdo->exec("insertintozyblog_test_user(`username`,`password`,`salt`)value('akk','bkk','ckk')");$id=$pdo->lastInsertId();var_dump($count);//int(1)var_dump($id);//string(3)"205"
exec() 返回的是影响的行数,如果我们实行这一条 SQL ,返回的便是成功添加了一行数据。如果要得到新增加数据的 id ,就要利用 lastInserId() 方法来获取。
$count=$pdo->exec("insertintozyblog_test_user(`username`,`password`,`salt`)value('akk','bkk','ckk','dkk')");//Fatalerror:UncaughtPDOException:SQLSTATE[21S01]:Insertvaluelistdoesnotmatchcolumnlist:1136Columncountdoesn'tmatchvaluecountatrow1
实行缺点的 SQL 语句,就像根据 PDO::ATTR_ERRMODE 属性的设置来返回缺点信息。我们在最上面的实例化 PDO 代码中指定了缺点形式是非常处理模式,以是这里直接就会报 PDOException 非常。
修正操作//正常更新$count=$pdo->exec("updatezyblog_test_userset`username`='aakk'whereid='{$id}'");var_dump($count);//int(1)//数据不变更新$count=$pdo->exec("updatezyblog_test_userset`username`='aakk'whereid='{$id}'");var_dump($count);//int(0)//条件缺点更新$count=$pdo->exec("updatezyblog_test_userset`username`='aakk'whereid='123123123123'");var_dump($count);//int(0)echo'===============',PHP_EOL;
同样的,在实行更新操作的时候,exec() 返回的也是受影响的行数。很多小伙伴会以这个进行判断是否更新成功,但如果数据没有修正,那么它返回的将是 0 ,SQL 语句的实行是没有问题的,逻辑上实在也没有问题。比如我们在后台打开了某条数据查看,然后并不想更新任何内容就直接点了提交,这时候不应该涌现更新失落败的提示。也便是说,在前端判断更新操作的时候,须要判断字段是否都有改变,如果没有改变的话那么不应该提示更新失落败。这一点是业务逻辑上的考虑问题,如果你认为这样也是更新失落败的话,那么这么报错也没有问题,统统以业务形式为主。
删除操作$count=$pdo->exec("deletefromzyblog_test_userwhereid='{$id}'");var_dump($count);//int(1)//条件缺点删除$count=$pdo->exec("deletefromzyblog_test_userwhereid='5555555555'");var_dump($count);//int(0)
删除操作须要把稳的问题和更新操作是一样的,那便是同样的 exec() 只是返回影响行数的问题,不过相对付更新操作来说,没有受影响的行数那肯定是删除失落败的,没有数据被删除。同样的,这个失落败的提示也请根据业务情形来详细剖析。
总结不学不知道,一学吓一跳吧,简大略的一个 PDO 的创建和语句实行竟然有这么多的内容。对付我们的日常开拓来说,节制这些事理能够避免很多莫名其妙的问题,比如上面 exec() 只是返回影响行数在业务开拓中如何判断操作是否成功的问题就很范例。好了,这只是第一篇,后面的学习不要落下了哦!
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202008/source/PHP%E4%B8%AD%E7%9A%84PDO%E6%93%8D%E4%BD%9C%E5%AD%A6%E4%B9%A0%EF%BC%88%E4%B8%80%EF%BC%89%E5%88%9D%E5%A7%8B%E5%8C%96PDO%E5%8F%8A%E5%8E%9F%E5%A7%8BSQL%E8%AF%AD%E5%8F%A5%E6%93%8D%E4%BD%9C.php
参考文档:
https://www.php.net/manual/zh/pdo.construct.php
https://www.php.net/manual/zh/pdo.query.php
https://www.php.net/manual/zh/pdo.exec.php
https://www.php.net/manual/zh/pdo.lastinsertid.php
https://www.php.net/manual/zh/ref.pdo-mysql.connection.php