이 기사에서는 PHP의 초기화 PDO 및 원본 SQL 문 작업을 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
PDO는 PHP에서 데이터베이스를 운영하기 위한 사실상의 표준이 되었습니다. 현재 프레임워크와 다양한 클래스 라이브러리를 포함하여 모두 PDO를 데이터베이스 연결 방법으로 사용합니다. 기본적으로 간단한 테스트 코드나 작은 함수를 작성할 때 데이터베이스를 운영하기 위해 mysqli만을 사용합니다. 일반적인 mysql 확장은 더 이상 사용되지 않습니다!
먼저 PDO 인스턴스가 어떻게 초기화되는지 살펴보겠습니다.
$dns = 'mysql:host=localhost;dbname=blog_test;port=3306;charset=utf8'; $pdo = new PDO($dns, 'root', '');
일반적인 상황에서는 PDO 객체를 직접 인스턴스화할 때 구성 매개변수를 전달하여 PDO 객체를 얻을 수 있습니다. 이러한 방식으로 데이터베이스와의 연결을 설정했습니다. 연결이 실패하는 경우, 즉 작성된 매개변수에 문제가 있는 경우 인스턴스화 중에 예외가 직접 보고됩니다.
PDO 개체의 매개변수에는 DNS 정보, 사용자 이름, 비밀번호 및 PDO 연결의 일부 속성을 설정할 수 있는 다른 매개변수가 포함됩니다.
PDO 구성 매개변수의 첫 번째 매개변수는 DNS 문자열입니다. 이 문자열에 세미콜론을 사용하여 다양한 매개변수 내용을 구분하세요. 여기에 정의할 수 있는 내용은 다음과 같습니다:
DSN 접두사, 이는 우리가 연결하려는 데이터베이스 유형입니다. MySQL 데이터베이스는 일반적으로 mysql을 사용하여 직접 정의됩니다.
host, 연결 주소, 여기서는 로컬 데이터베이스에 연결합니다. localhost
port, 포트 번호, MySQL의 기본값은 3306이므로
dbname, 이름을 쓸 필요가 없습니다. 연결할 데이터베이스
unix_socket, MySQL Unix 소켓 파일
charset, 연결 문자 집합
을 지정할 수 있습니다. 함수를 사용하여 현재 PHP 환경에서 어떤 데이터베이스 확장이 지원되는지 확인할 수 있습니다. :
print_r(PDO::getAvailableDrivers());exit; // Array // ( // [0] => dblib // [1] => mysql // [2] => odbc // [3] => pgsql // [4] => sqlite // )
PDO 구성 매개변수 속성의 마지막 매개변수는 다음과 같은 연결의 일부 속성을 설정할 수 있습니다.
$pdo = new PDO($dns, 'root', '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); showPdoAttribute($pdo); // …… // PDO::ATTR_ERRMODE: 2 // ……
showPdoAttribute() 메소드는 모든 연결 속성을 표시하기 위해 자체적으로 캡슐화된 함수입니다.
// 显示pdo连接属性 function showPdoAttribute($pdo){ $attributes = array( "DRIVER_NAME", "AUTOCOMMIT", "ERRMODE", "CASE", "CLIENT_VERSION", "CONNECTION_STATUS", "ORACLE_NULLS", "PERSISTENT", "SERVER_INFO", "SERVER_VERSION" ); foreach ($attributes as $val) { echo "PDO::ATTR_$val: "; echo $pdo->getAttribute(constant("PDO::ATTR_$val")) . "\n"; } }
이 함수에서는 PDO 인스턴스의 getAttribute() 메서드를 사용하여 해당 속성 값을 가져옵니다. PDO::ATTR_ERRMODE가 설정되지 않은 경우 기본값은 PDO::ERRMODE_SILENT 상수에 해당하는 값인 0입니다. 위 코드에서는 PDO::ERRMODE_EXCEPTION으로 설정했고, 속성 출력을 본 결과는 2가 됩니다.
생성자의 매개변수에 속성을 설정하는 것 외에도 PDO 인스턴스의 setAttribute() 메서드를 사용하여 PDO의 속성 값을 설정할 수도 있습니다.
pdo2 = new PDO($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('select * from zyblog_test_user limit 5'); foreach ($stmt as $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('select * from zyblog_test_user limit 5'); 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('select * from zyblog_test_user limit 5'); foreach ($stmt as $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('select * from zyblog_test_user limit 5', PDO::FETCH_ASSOC); foreach ($stmt as $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('select * from zyblog_test_user limit 5', PDO::FETCH_OBJ); foreach ($stmt as $row) { var_dump($row); } // object(stdClass)#4 (4) { // ["id"]=> // string(1) "5" // ["username"]=> // string(3) "two" // ["password"]=> // string(6) "123123" // ["salt"]=> // string(3) "bbb" // } // ……
위 개체 형식으로 반환된 결과 집합의 개체는 PHP의 기본 클래스 유형인 stdClass 유형입니다. 그렇다면 클래스를 직접 정의하고 쿼리가 완료된 후 결과 집합을 직접 생성할 수 있습니까? ORM 프레임워크와 마찬가지로 데이터를 개체에 매핑하는 작업을 완료합니다. 물론 가능하다고 말했으므로 코드를 살펴보십시오.
class user { public $id; public $username; public $password; public $salt; public function __construct() { echo 'func_num_args: ' . func_num_args(), PHP_EOL; echo 'func_get_args: '; var_dump(func_get_args()); } } class user2 { } // 返回指定对象 $u = new user; $stmt = $pdo->query('select * from zyblog_test_user limit 5', PDO::FETCH_INTO, $u); foreach ($stmt as $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 = new user2; $stmt = $pdo->query('select * from zyblog_test_user limit 5', PDO::FETCH_INTO, $u); foreach ($stmt as $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('select * from zyblog_test_user limit 5', PDO::FETCH_CLASS, 'user', ['x1', 'x2']); foreach ($stmt as $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('select * from zyblog_test_user limit 5', PDO::FETCH_INTO, $u); $resArr = []; foreach ($stmt as $row) { var_dump($row); $resArr[] = $row; } $resArr[0]->id = 55555; print_r($resArr); // Array // ( // [0] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [1] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [2] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [3] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // [4] => user2 Object // ( // [id] => 55555 // [username] => two // [password] => 123123 // [salt] => bbb // ) // )
如何解决这个问题呢?最简单的方式就是在数组赋值的时候加个 clone 关键字呗!
最后轻松一点,我们看下 query() 方法还可以指定查询的某一个字段。
// 只返回第几个字段 $stmt = $pdo->query('select * from zyblog_test_user limit 5', PDO::FETCH_COLUMN, 2); foreach ($stmt as $row) { var_dump($row); } // string(32) "bbff8283d0f90625015256b742b0e694" // string(6) "123123" // string(6) "123123" // string(6) "123123" // string(6) "123123"
除了查询之外的操作,我们也可以使用 exec() 方法来执行其他一些相应的 SQL 语句。
$count = $pdo->exec("insert into zyblog_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("insert into zyblog_test_user(`username`, `password`, `salt`) value('akk', 'bkk', 'ckk', 'dkk')"); // Fatal error: Uncaught PDOException: SQLSTATE[21S01]: Insert value list does not match column list: 1136 Column count doesn't match value count at row 1
执行错误的 SQL 语句,就像根据 PDO::ATTR_ERRMODE 属性的设置来返回错误信息。我们在最上面的实例化 PDO 代码中指定了错误形式是异常处理模式,所以这里直接就会报 PDOException 异常。
// 正常更新 $count = $pdo->exec("update zyblog_test_user set `username`='aakk' where id='{$id}'"); var_dump($count); // int(1) // 数据不变更新 $count = $pdo->exec("update zyblog_test_user set `username`='aakk' where id='{$id}'"); var_dump($count); // int(0) // 条件错误更新 $count = $pdo->exec("update zyblog_test_user set `username`='aakk' where id='123123123123'"); var_dump($count); // int(0) echo '===============', PHP_EOL;
同样的,在执行更新操作的时候,exec() 返回的也是受影响的行数。很多小伙伴会以这个进行判断是否更新成功,但如果数据没有修改,那么它返回的将是 0 ,SQL 语句的执行是没有问题的,逻辑上其实也没有问题。比如我们在后台打开了某条数据查看,然后并不想更新任何内容就直接点了提交,这时候不应该出现更新失败的提示。也就是说,在前端判断更新操作的时候,需要判断字段是否都有改变,如果没有改变的话那么不应该提示更新失败。这一点是业务逻辑上的考虑问题,如果你认为这样也是更新失败的话,那么这么报错也没有问题,一切以业务形式为主。
$count = $pdo->exec("delete from zyblog_test_user where id = '{$id}'"); var_dump($count); // int(1) // 条件错误删除 $count = $pdo->exec("delete from zyblog_test_user where id = '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
推荐学习:php视频教程
위 내용은 3분 안에 초기화 PDO와 PHP의 원본 SQL 문 작업에 대해 알아봅니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!