Cet article vous présentera les classes de prétraitement et les données liées en PHP. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il sera utile à tout le monde.
Pour être considéré comme le plus puissant du PDO En plus de fournir une interface unifiée pour différentes bases de données, la fonction la plus importante est sa capacité de prétraitement, qui est la fonction fournie par PDOStatement. De par son existence, nous pouvons l'utiliser en toute tranquillité sans nous soucier des risques de sécurité causés par un mauvais épissage des instructions SQL. Bien sûr, le prétraitement améliore également pour nous l'efficacité de l'exécution des instructions, ce qui peut être considéré comme un autre grand tueur de PDO.
La classe PDOStatement représente en fait une instruction préparée et représente un ensemble de résultats associé après l'exécution de l'instruction. Il fournit quelques méthodes qui nous permettent d'opérer sur cette instruction préparée.
$dns = 'mysql:host=localhost;dbname=blog_test;port=3306;charset=utf8'; $pdo = new PDO($dns, 'root', '', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); $stmt = $pdo->prepare("select * from zyblog_test_user"); // PDOStatement 对象的内容 var_dump($stmt); // object(PDOStatement)#2 (1) { // ["queryString"]=> // string(57) "select * from zyblog_test_user where username = :username" // }
L'objet PDOStatement est un objet renvoyé par la méthode prepare() de l'objet PDO. Il n’a pas de constructeur, ce qui signifie que nous ne pouvons pas instancier directement un objet PDOStatement. Il contient une propriété en lecture seule, qui est l'instruction SQL que nous souhaitons exécuter, stockée dans queryString.
Ensuite, jetons un coup d'œil aux deux méthodes de message d'erreur de PDOStatement.
// 没有指定异常处理状态下的错误信息函数 $pdo_no_exception = new PDO($dns, 'root', ''); $errStmt = $pdo_no_exception->prepare("select * from errtable"); $errStmt->execute(); var_dump($errStmt->errorCode()); // string(5) "42S02" var_dump($errStmt->errorInfo()); // array(3) { // [0]=> // string(5) "42S02" // [1]=> // int(1146) // [2]=> // string(40) "Table 'blog_test.errtable' doesn't exist" // }
Dans l'article précédent, nous avons appris que si le format de gestion des erreurs n'est pas spécifié pour l'objet PDO. Il gère les erreurs en renvoyant des codes d'erreur et des messages d'erreur. Dans ce cas, s'il y a un problème avec l'instruction préparée, nous pouvons obtenir le code d'erreur et les détails de l'erreur via les méthodes errorCode() et errorInfo(). Cependant, il est plus recommandé de spécifier la méthode de gestion des erreurs du PDO pour lever une exception, tout comme l'objet PDO que nous avons défini ci-dessus. De cette façon, nous pouvons gérer les exceptions d’erreur via try...catch.
// 为语句设置默认的获取模式。 $stmt->setFetchMode(PDO::FETCH_ASSOC); $stmt->execute(); while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ var_dump($row); } // array(4) { // ["id"]=> // string(1) "1" // ["username"]=> // string(3) "aaa" // ["password"]=> // string(3) "aaa" // ["salt"]=> // string(3) "aaa" // } // ……
La spécification de FETCH_MODE pour la structure de requête est réalisée via la méthode setFetchMode(). Nous avons également mentionné précédemment que le mode de jeu de résultats de requête par défaut peut être spécifié via les propriétés de l'objet PDO. Cependant, dans PDOStatement, cette méthode peut également être utilisée pour spécifier FETCH_MODE pour la requête actuelle de l'instruction préparée.
// 返回结果集列数、返回结果集中一列的元数据 $stmt = $pdo->prepare("select * from zyblog_test_user"); $stmt->execute(); var_dump($stmt->columnCount()); // int(4) var_dump($stmt->getColumnMeta(0)); // array(7) { // ["native_type"]=> // string(4) "LONG" // ["pdo_type"]=> // int(2) // ["flags"]=> // array(2) { // [0]=> // string(8) "not_null" // [1]=> // string(11) "primary_key" // } // ["table"]=> // string(16) "zyblog_test_user" // ["name"]=> // string(2) "id" // ["len"]=> // int(11) // ["precision"]=> // int(0) // }
columnCount() peut renvoyer le nombre de colonnes dans notre ensemble de résultats de requête actuel. Nous présenterons la méthode d'obtention du nombre de lignes dans le prochain article.
La méthode getColumnMeta() consiste à obtenir les métadonnées d'une colonne dans le jeu de résultats. Son paramètre est le numéro de série de la colonne, à partir de 1. Ici, nous obtenons la première colonne, qui est l'information de. la colonne identifiant. À partir des résultats imprimés, vous pouvez voir le nom de la colonne, la précision, la longueur, le type, le nom de la table, les attributs (clé primaire, non nulle) et d'autres informations. Cela ne vous semble-t-il pas très utile ? Cependant, cette méthode est expérimentale et pourra être modifiée dans les futures versions de PHP. Il ne s'agit pas d'une méthode formelle fixe. Et tous les pilotes de connexion à la base de données ne prennent pas en charge cette méthode.
$stmt = $pdo->prepare("select * from zyblog_test_user where username=? and salt = ?"); $username = 'aaa'; $stmt->bindParam(1, $username, PDO::PARAM_STR); $stmt->bindValue(2, 'aaa', PDO::PARAM_STR); $stmt->execute(); var_dump($stmt->debugDumpParams()); // SQL: [60] select * from zyblog_test_user where username=? and salt = ? // Sent SQL: [68] select * from zyblog_test_user where username='aaa' and salt = 'aaa' // Params: 2 // Key: Position #0: // paramno=0 // name=[0] "" // is_param=1 // param_type=2 // Key: Position #1: // paramno=1 // name=[0] "" // is_param=1 // param_type=2
debugDumpParams() est également une méthode très intéressante. Elle imprime directement les informations de l'instruction SQL actuellement exécutée. similaire à var_dump() et php_info() sont les mêmes fonctions, elles s'impriment directement au lieu de renvoyer le résultat à une variable. Rappelez-vous comment nous avons enregistré le contenu d'une telle fonction dans une variable ? [Toujours confus au sujet du contrôle de la mise en mémoire tampon de sortie en PHP ? ]().
À en juger par les résultats imprimés, il peut renvoyer l'instruction SQL réellement exécutée et certaines informations sur les paramètres associés. C'est définitivement un artefact pour le développement et le débogage quotidiens. De nombreux amis seront piégés dans l'instruction de prétraitement PDO s'ils obtiennent la véritable instruction d'exécution. Cette méthode nous oblige simplement à l'encapsuler, et nous pouvons ensuite en extraire la véritable instruction d'exécution !
// MySQL 驱动不支持 setAttribute $stmt->setAttribute(PDO::ATTR_CURSOR, PDO::CURSOR_FWDONLY); // Fatal error: Uncaught PDOException: SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support setting attributes // MySQL 驱动不支持 getAttribute var_dump($stmt->getAttribute(PDO::ATTR_AUTOCOMMIT)); // Fatal error: Uncaught PDOException: SQLSTATE[IM001]: Driver does not support this function: This driver doesn't support getting attributes
Ces deux méthodes ne sont pas prises en charge par le pilote d'extension MySQL, mais d'autres bases de données le prennent en charge. L'auteur n'en a pas testé d'autres. testez-le vous-même.
L'étape suivante est le contenu clé dans l'instruction de prétraitement, nous pouvons utiliser des espaces réservés pour lier les variables afin d'obtenir un traitement sûr des requêtes. la déclaration. Grâce aux espaces réservés, nous n'avons pas besoin d'assembler et de traiter nous-mêmes le contenu du champ avec des guillemets simples, évitant ainsi l'apparition d'une injection SQL. Notez que tous les problèmes d'injection SQL ne peuvent pas être traités ici, tels que l'injection d'octets larges de problèmes de jeu de caractères.
L'espace réservé contient deux formes. La première consiste à utiliser l'espace réservé pour le nom sous la forme :xxx. Le contenu après : peut être un nom défini par vous-même. Une autre forme consiste à utiliser l'espace réservé au point d'interrogation. Lorsque nous utilisons l'espace réservé au point d'interrogation, nous lions l'indice du champ et l'indice commence à 1. C'est quelque chose auquel il faut prêter attention. Jetons un coup d’œil directement à travers un exemple.
$stmt = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(:username, :pass, :salt)"); $username = 'ccc'; $passwrod = '333'; $salt = 'c3'; $stmt->bindParam(':username', $username); $stmt->bindParam(':pass', $password); $stmt->bindParam(':salt', $salt); $stmt->execute(); // bindParam 问号占位符 $stmt = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(?, ?, ?)"); $username = 'ccc'; $passwrod = '333'; $salt = 'c3'; $stmt->bindParam(1, $username); $stmt->bindParam(2, $password); $stmt->bindParam(3, $salt); $stmt->execute();
在这段代码中,我们分别使用了两种形式的占位符来实现了数据的插入。当然,预处理语句和占位符是任何操作语句都可以使用的。它的作用就是用绑定的值来替换语句中的占位符所在位置的内容。不过它只是使用在 values 、 set 、 where 、 order by 、 group by 、 having 这些条件及对字段的操作中,有兴趣的同学可以试试用占位符来表示一个表名会是什么结果。
bindParam() 方法是绑定一个参数到指定的变量名。在这个方法中,绑定的变量是作为引用被绑定,并且只能是一个变量,不能直接给一个常量。这点我们在后面讲和 bindValue() 的区别时再详细讲解。一些驱动支持调用存储过程的输入/输出操作,也可以使用这个方法来绑定,我们将在后面的文章中讲解。
$stmt = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(:username, :pass, :salt)"); $username = 'ddd'; $passwrod = '444'; $salt = 'd4'; $stmt->bindValue(':username', $username); $stmt->bindValue(':pass', $password); $stmt->bindValue(':salt', $salt); $stmt->execute();
咦?它的用法和 bindParam() 一样呀?没错,它们的作用也是一样的,绑定一个参数到值。注意,这里是绑定到值,而 bindParam() 是绑定到变量。在正常情况下,你可以将它们看作是一样的操作,但是,其实它们有很大的不同,我们直接就来看它们的区别。
首先,bindValue() 是可以绑定常量的。
$stmt = $pdo->prepare("select * from zyblog_test_user where username = :username"); //$stmt->bindParam(':username', 'ccc'); // Fatal error: Uncaught Error: Cannot pass parameter 2 by reference $stmt->bindValue(':username', 'ccc'); $stmt->execute(); while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ var_dump($row); } // array(4) { // ["id"]=> // string(2) "19" // ["username"]=> // string(3) "ccc" // ["password"]=> // string(3) "bbb" // ["salt"]=> // string(2) "c3" // } // ……
如果我们使用 bindParam() 来指定第二个参数值为常量的话,它会直接报错。bindParam() 的第二个参数是作为引用类型的变量,不能指定为一个常量。
其次,因为bindParam() 是以引用方式绑定,它的变量内容是可变的,所以在任何位置定义绑定的变量都不影响它的预处理,而 bindValue() 是定义后就立即将参数进行绑定的,所以下面的代码使用 bindValue() 是无法获得结果的($username 在 bindValue() 之后才赋值)。
$stmt = $pdo->prepare("select * from zyblog_test_user where username = :username"); $stmt->bindValue(':username', $username); $username = 'ccc'; $stmt->execute(); while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ var_dump($row); } //
必须要保证变量在 bindValue() 之前被赋值。
$username = 'ccc'; $stmt->bindValue(':username', $username);
当然,bindParam() 就不存在这样的问题了,我们可以在 bindParam() 之后再给它指定的变量赋值。
$stmt = $pdo->prepare("select * from zyblog_test_user where username = :username"); $stmt->bindParam(':username', $username); $username = 'ddd'; $stmt->execute(); while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ var_dump($row); } // array(4) { // ["id"]=> // string(1) "8" // ["username"]=> // string(3) "ddd" // ["password"]=> // string(3) "bbb" // ["salt"]=> // string(2) "d4" // } // ……
这下对 bindParam() 和 bindValue() 的区别就非常清楚了吧?总结一下:
bindParam() 必须绑定变量,变量是引用形式的参数,只要在 execute() 之前完成绑定都可以
bindValue() 可以绑定常量,如果是绑定的变量,那么变量赋值要在 bindValue() 语句执行之前完成,否则绑定的就是一个空的数据
这个方法是用于绑定查询结果集的内容的。我们可以将查询结果集中指定的列绑定到一个特定的变量中,这样就可以在 fetch() 或 fetchAll() 遍历结果集时通过变量来得到列的值。
这个方法在实际应用中用到的比较少,所以很多小伙伴可能是只闻其名不见其身。我们还是通过代码来看看。
$stmt = $pdo->prepare("select * from zyblog_test_user"); $stmt->execute(); $stmt->bindColumn(1, $id); $stmt->bindColumn(2, $username, PDO::PARAM_STR); $stmt->bindColumn("password", $password); $stmt->bindColumn("salt", $salt, PDO::PARAM_INT); // 指定类型强转成了 INT 类型 // 不存在的字段 // $stmt->bindColumn(5, $t); //Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: Invalid column index while($row = $stmt->fetch(PDO::FETCH_BOUND)){ $data = [ 'id'=>$id, 'username'=>$username, 'password'=>$password, 'salt'=>$salt, ]; var_dump($data); } // array(4) { // ["id"]=> // string(1) "1" // ["username"]=> // string(3) "aaa" // ["password"]=> // string(3) "aaa" // ["salt"]=> // int(0) // } // array(4) { // ["id"]=> // string(1) "2" // ["username"]=> // string(3) "bbb" // ["password"]=> // string(3) "bbb" // ["salt"]=> // int(123) // } // …… // 外部获取变量就是最后一条数据的信息 $data = [ 'id'=>$id, 'username'=>$username, 'password'=>$password, 'salt'=>$salt, ]; print_r($data); // Array // ( // [id] => 2 // [username] => bbb // [password] => bbb // [salt] => bbb // )
在代码中,我们使用的是 * 来获得的查询结果集。然后就可以通过问号占位符或者列名来将列绑定到变量中。接着在 fetch() 的遍历过程中,就可以通过变量直接获取每一条数据的相关列的值。需要注意的是,为变量赋值的作用域仅限于在执行 fetch() 方法之后。从代码的结构中我们就可以看出,bindColumn() 方法对于变量也是作为引用的方式绑定到 PDOStatement 对象内部的,所以 fetch() 在处理的时候就直接为这些变量赋上了值。
bindCloumn() 方法后面的参数是可选的字段类型,这个参数在 bindParam() 和 bindValue() 中都是存在的,也都是可选的。如果获取的类型和我们绑定时定义的类型不同,那么 PDOStatement 就会强转为绑定时指定的类型。例如上面例子中我们将本身为 varchar 类型的 salt 字段强转为 int 类型之后就输出的都是 int 类型了。除了这个参数之外,还有一些其它可选的参数,大家可以自行查阅相关的文档。
fetch() 循环结束后,变量中依然保留着最后一行结果集的内容。所以在使用的时候要注意如果外部有其它地方使用这些变量的话,是否需要重新赋值或者清理掉它们。
最后,如果我们不想这么麻烦地去绑定字段或者变量,也可以直接在 execute() 方法中直接传递参数,它是类似于 bindValue() 的形式进行字段绑定的。
$stmt = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(:username, :pass, :salt)"); $stmt->execute([ ':username'=>'jjj', ':pass'=>'888', ':salt'=>'j8' ]); // 使用问号占位符的话是按从0开始的下标 $stmt = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(?, ?, ?)"); $stmt->execute(['jjjj','8888','j8']);
execute() 的这个绑定参数是一个数组,在使用问号占位符的时候需要注意,在这里,按数组的下标来说,它们是从 0 开始算位置的。
另外需要注意的是,PDOStatement 对象的操作都是使用 execute() 方法来进行语句执行的。这个方法只会返回一个布尔值,也就是成功或者失败。不像 PDO 对象的 exec() 方法返回的是受影响的条数。如果是查询类的语句,我们需要在 execute() 之后调用 fetch() 之类的方法遍历结果集。而增、删、改之类的操作,则需要通过 rowCount() 来获得返回的执行结果条数。相关的内容我们也将在之后的文章一起详细讲解。
划重点的时刻又到咯!今天我们学习的主要是 PDOStatement 对象的一些不太常用但很好玩的方法,另外就是占位符绑定的问题。其中最主要的就是 bindParam() 和 bindValue() 的区别。下篇文章我们主要就是要学习 PDOStatement 中的查询相关的操作,这个可不能丢呀,大家一定不要迟到!
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202009/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%89%EF%BC%89%E9%A2%84%E5%A4%84%E7%90%86%E7%B1%BB%E5%8F%8A%E7%BB%91%E5%AE%9A%E6%95%B0%E6%8D%AE.php
推荐学习:php视频教程
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!