PDO 简介——预处理语句和存储过程 $stmt->bindParam(':name', $name);

本文介绍了PDO中的预处理语句概念及其优势,包括减少资源消耗、提高执行速度及防止SQL注入等。同时展示了如何使用预处理语句进行数据插入和查询操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

PDO 简介——预处理语句和存储过程

许多成熟的数据库都支持预处理语句(Prepared Statements)的概念。它们是什么东西?你可以把它们想成是一种编译过的要执行的SQL语句模板,可以使用不同的变量参数定制它。预处理语句具有两个主要的优点:

  1. 查询只需要被解析(或准备)一次,但可以使用相同或不同的参数执行多次。当查询准备好(Prepared)之后,数据库就会分析,编译并优化它要执行查询的计划。对于复杂查询来说,如果你要重复执行许多次有不同参数的但结构相同的查询,这个过程会占用大量的时间,使得你的应用变慢。通过使用一个预处理语句你就可以避免重复分析、编译、优化的环节。简单来说,预处理语句使用更少的资源,执行速度也就更快。
  2. 传给预处理语句的参数不需要使用引号,底层驱动会为你处理这个。如果你的应用独占地使用预处理语句,你就可以确信没有SQL注入会发生。(然而,如果你仍然在用基于不受信任的输入来构建查询的其他部分,这仍然是具有风险的)


正因为预处理语句是如此有用,它成了PDO唯一为不支持此特性的数据库提供的模拟实现。这使你可以使用统一的数据访问规范而不必关心数据库本身是否具备此特性。

  1. /*
  2. 使用预处理语句重复插入数据(1)
  3. 此示例演示了一个通过向命名占位符代入一个name和一个value值来执行的INSERT查询
  4. */
  5. $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
  6. $stmt->bindParam(':name'$name);
  7. $stmt->bindParam(':value'$value); //插入一行
  8. $name = 'one';
  9. $value = 1;
  10. $stmt->execute();//使用不同的值插入另一行
  11. $name = 'two';
  12. $value = 2;
  13. $stmt->execute();
  14. /*
  15. 使用预处理语句重复插入数据(2)
  16. 此示例演示了一个通过向用?表示的占位符代入一个name和一个value值来执行的INSERT查询
  17. */
  18. $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
  19. $stmt->bindParam(1, $name);
  20. $stmt->bindParam(2, $value); // 插入一行
  21. $name = 'one';
  22. $value = 1;
  23. $stmt->execute(); // 使用不同的值插入另一行
  24. $name = 'two';
  25. $value = 2;
  26. $stmt->execute();
  27. /*
  28. 通过预处理语句获取数据
  29. 此示例演示使用从表单获取的数据为关键值来执行查询获取数据。用户的输入会被自动添加引号,所以这儿不存在SQL注入攻击的危险。
  30. */
  31. $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
  32.     if ($stmt->execute(array($_GET['name']))) {
  33.         while ($row = $stmt->fetch()) {
  34.         print_r($row);
  35.     }
  36. }

如果数据库驱动支持,你也可以像绑定输入参数那样绑定输出参数。输出参数常用于存储过程的返回值。输出参数用起来比输入参数稍微复杂一些,使用时你必须知道它返回的参数值有多大。如果它返回的参数值比你建议的大,就会发生错误。

  1. //调用一个带有输出参数的存储过程
  2. $stmt = $dbh->prepare("CALL sp_returns_string(?)");
  3. $stmt->bindParam(1, $return_value, PDO::PARAM_STR, 4000); //执行存储过程
  4. $stmt->execute();
  5. print "procedure returned $return_value/n";

 

你也可以指定既代表输入又代表输出的参数,语法类似于输出参数。在下个代码示例中,“hello”字符串被传递到存储过程中,当它返回时,hello就会被存储过程的返回值取代。

  1. //调用一个带有输入/输出参数的存储过程
  2. $stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)");
  3. $value = 'hello';
  4. $stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);
  5. // 执行存储过程
  6. $stmt->execute();
  7. print "procedure returned $value/n";
  8. //占位符的错误使用
  9. $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
  10. $stmt->execute(array($_GET['name'])); // 占位符必须用于整个值的位置(下面是正确的用法)
  11. $stmt = $dbh->prepare(”SELECT * FROM REGISTRY where name LIKE ?”);
  12. $stmt->execute(array(”%$_GET[name]%”));
/** * 搜索列表 */ public function lists() { $param = input('param.'); $users = session('?users') ? session('users') : []; $users_id = !empty($users['users_id']) ? intval($users['users_id']) : 0; $admin_id = !empty($users['admin_id']) ? intval($users['admin_id']) : 0; $nowTime = getTime(); /*记录搜索词*/ if (!isset($param['keywords'])) { die('标签调用错误:缺少属性 name="keywords",请查看标签教程修正 <a href="https://www.eyoucms.com/plus/view.php?aid=521" target="_blank">前往查看</a>'); } $word = $this->request->param('keywords'); if(empty($word)){ $this->error(foreign_lang('system15', $this->home_lang)); } $page = $this->request->param('page'); if(!empty($word) && 2 > $page) { $word_decode = htmlspecialchars_decode($word); $searchConf = tpCache('search'); if (!isset($searchConf['search_tabu_words'])) { $searchConf['search_tabu_words'] = ['<','>','"',';',',','@','&','#','\\','*']; } else { $searchConf['search_tabu_words'] = explode(PHP_EOL, $searchConf['search_tabu_words']); } /*前台禁止搜索开始*/ if (!empty($searchConf['search_tabu_words'])) { foreach ($searchConf['search_tabu_words'] as $key => $val) { if (strstr($word_decode, $val)) { $msg = sprintf(foreign_lang('system13', $this->home_lang), $val); $this->error($msg); } } } /*if (is_dir('./weapp/Wordfilter/')) { $wordfilterRow = Db::name('weapp')->where(['code'=>'Wordfilter', 'status'=>1])->find(); if(!empty($wordfilterRow['data'])){ $wordfilterRow['data'] = json_decode($wordfilterRow['data'], true); if ($wordfilterRow['data']['search'] == 3){ $wordfilter = Db::name('weapp_wordfilter')->where(['title'=>$word, 'status'=>1])->find(); if(!empty($wordfilter)){ $this->error('包含敏感关键词,禁止搜索!'); } } } }*/ /*前台禁止搜索结束*/ // 如果tag标签有,默认跳到tag列表页 /*$tagInfo = Db::name('tagindex') ->alias('a') ->field('a.id AS tagid') ->join('taglist b','a.id=b.tid','left') ->where(['a.tag'=>$word,'b.arcrank'=>['egt',0]]) ->find(); if (!empty($tagInfo)) { $city_switch_on = config('city_switch_on'); $domain = preg_replace('/^(http(s)?:)?(\/\/)?([^\/\:]*)(.*)$/i', '${1}${3}${4}', tpCache('web.web_basehost')); if (empty($city_switch_on)) { $tagurl = tagurl('home/Tags/lists', array('tagid'=>$tagInfo['tagid'])); } else { $tagurl = tagurl('home/Tags/lists', array('tagid'=>$tagInfo['tagid']), true, $domain); } $this->redirect($tagurl); exit; }*/ $word = addslashes($word); $method = input('param.method/d'); if (!empty($method)) { /*搜索频率限制 start*/ if (!isset($searchConf['search_second'])) { $searchConf['search_second'] = 60; } if (!isset($searchConf['search_maxnum'])) { $searchConf['search_maxnum'] = 5; } if (!isset($searchConf['search_locking'])) { $searchConf['search_locking'] = 120; } if (empty($admin_id) && 0 < $searchConf['search_second']) { $where = []; if (!empty($users_id)) { $where['users_id'] = $users_id; } else { $where['ip'] = clientIP(); } $where2 = [ 'update_time' => ['gt', $nowTime - $searchConf['search_second']], ]; $searchTotal = Db::name('search_word')->where($where)->where($where2)->count(); $lockingInfo = Db::name('search_locking')->where($where)->find(); if ($searchTotal >= intval($searchConf['search_maxnum'])) { if (empty($lockingInfo)) { $lockingInfo = [ 'users_id' => $users_id, 'ip' => clientIP(), 'locking_time' => $nowTime, 'add_time' => $nowTime, 'update_time' => $nowTime, ]; $insertId = Db::name('search_locking')->insertGetId($lockingInfo); $lockingInfo['id'] = $insertId; } else { if (($lockingInfo['locking_time'] + $searchConf['search_locking']) < $nowTime) { Db::name('search_locking')->where(['id'=>$lockingInfo['id']])->update([ 'locking_time' => $nowTime, 'update_time' => $nowTime, ]); $lockingInfo['locking_time'] = $nowTime; } } } if (!empty($lockingInfo)) { $locking_time = !empty($lockingInfo['locking_time']) ? $lockingInfo['locking_time'] : 0; $surplus_time = $locking_time + $searchConf['search_locking'] - $nowTime; if ($surplus_time > 0) { $minute = ceil($surplus_time/60); $msg = sprintf(foreign_lang('system14', $this->home_lang), $minute); $this->error($msg, null, [], $surplus_time); } } } /*搜索频率限制 end*/ /*记录搜索词*/ $row = $this->searchword_db->field('id')->where(['word'=>$word, 'lang'=>$this->home_lang])->find(); if(empty($row)) { $this->searchword_db->insert([ 'word' => $word, 'sort_order' => 100, 'users_id' => $users_id, 'ip' => clientIP(), 'lang' => $this->home_lang, 'add_time' => $nowTime, 'update_time' => $nowTime, ]); }else{ $this->searchword_db->where(['id'=>$row['id']])->update([ 'searchNum' => Db::raw('searchNum+1'), 'users_id' => $users_id, 'ip' => clientIP(), 'update_time' => $nowTime, ]); } } } /*--end*/ $result = $param; !isset($result['keywords']) && $result['keywords'] = ''; $eyou = array( 'field' => $result, ); $this->eyou = array_merge($this->eyou, $eyou); $this->assign('eyou', $this->eyou); /*模板文件*/ $viewfile = 'lists_search'; $channelid = input('param.channelid/d'); if (!empty($channelid)) { $viewfilepath = TEMPLATE_PATH.$this->theme_style_path.DS.$viewfile."_{$channelid}.".$this->view_suffix; if (file_exists($viewfilepath)) { $viewfile .= "_{$channelid}"; } } /*--end*/ if (config('city_switch_on') && !empty($this->home_site)) { // 多站点内置模板文件名 $viewfilepath = TEMPLATE_PATH.$this->theme_style_path.DS.$this->home_site; $viewfilepath2 = TEMPLATE_PATH.$this->theme_style_path.DS.'city'.DS.$this->home_site; if (!empty($this->eyou['global']['site_template'])) { if (file_exists($viewfilepath2)) { $viewfile = "city/{$this->home_site}/{$viewfile}"; } else if (file_exists($viewfilepath)) { $viewfile = "{$this->home_site}/{$viewfile}"; } } } else if (config('lang_switch_on') && !empty($this->home_lang)) { // 多语言内置模板文件名 $viewfilepath = TEMPLATE_PATH.$this->theme_style_path.DS.$viewfile."_{$this->home_lang}.".$this->view_suffix; if (file_exists($viewfilepath)) { $viewfile .= "_{$this->home_lang}"; } } return $this->fetch(":{$viewfile}"); } 检测sql注入利用链
最新发布
07-30
会员注册要求按照下面示例进行编写通过post提交用户需要添加的信息到registerResult.php,将用户信息添加到数据库里 image.png (1)在registerResult.php页面通过post获取表单提交的数据 $userName 获取用户名称 $password 获取用户密码 $checkPwd 获取用户确认密码 (2)使用 PDO 方式,链接操作 MySQL 数据库,执行 insert 语句,将表单提交数据的用户名密码,插入 admin 表 备注:数据表 admin 结构: id 自增序号 username 用户名 password 用户密码 对以下代码进行补充<?php header("content-type:text/html;charset=utf-8"); //$host,$dbname,$user,$pwd是自动生成的数据库相关信息,不能修改 //连接数据库时不需要写端口号 //数据库默认数据不能更改 $host = "mysql";//MySQL所在的服务器的IP $dbname = "database_46541_19_91471";//数据库名称 $user = "46541_19_91471";//数据库用户 $pwd = "54ff8238176b694a46b85e7981dfadb6";//数据库密码 示例:会员登录通过 post 提交用户信息到 loginUser.php,查询 admin 用户表用户数据,如果匹配的话登录成功 image.png 1)loginUser.php 页面通过 post 获取传递过来的参数 $userName 获取用户名称 $password 获取用户密码 2)使用 PDO 方式,连接 MySQL 数据库,通过表单提交数据的用户名密码查询 admin 表,将通过fetch()查询出的结果集赋值给变量 $rs 备注:数据表 admin 结构: id 自增序号 username 用户名 password 用户密码 代码<?php header("content-type:text/html;charset=utf-8"); //$host,$dbname,$user,$pwd是自动生成的数据库相关信息,不能修改 //连接数据库时不需要写端口号 $host = "mysql";//MySQL所在的服务器的IP $dbname = "database_46551_19_91472";//数据库名称 $user = "46551_19_91472";//数据库用户 $pwd = "b89b80210837d4790dea8a9dd994dfc8";//数据库密码 $userName = $_POST['userName'] ?? ''; $password = $_POST['password'] ?? ''; $dsn = "mysql:host={$host};dbname={$dbname};"; $pdo = new PDO($dsn, $user, $pwd); $stmt = $pdo->prepare("SELECT * FROM admin WHERE username=? AND password=?"); $stmt->execute([$userName, $password]); $rs = $stmt->fetch(); // 4. 验证结果 if ($rs) { echo "登录成功"; } else { echo "用户名或密码错误"; }?>
06-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值