【问题标题】:PDO - Fatal error: Call to a member function fetch() on a non-object [duplicate]PDO - 致命错误:调用非对象上的成员函数 fetch() [重复]
【发布时间】:2013-10-01 19:09:46
【问题描述】:

如果我尝试运行以下 PHP 代码,我会得到一个

在非对象上调用成员函数 fetch()。

你知道为什么吗?我在另一个网站上使用相同的代码,它工作得很好。

<?php
$username = ($_GET ['user']);
try {
    $dbh = new PDO("mysql:host=localhost;dbname=***", '***', '***');    
} catch (PDOException $e) {
    echo $e->getMessage();
}
$sth = $dbh->query( "SELECT user, captcha 
    FROM xf_captcha WHERE user='$username'" );
print_r($sth->fetch());
?>

编辑:

$sth = $dbh->query( "SELECT username, user_state, last_activity, alerts_unread, conversations_unread, message_count 
    FROM xf_user WHERE username='$user'" );
$row = $sth->fetch();

编辑2:

这看起来安全吗,我应该做更多吗?

<?php
$username = ($_GET ['user']);
try {
    $dbh = new PDO("mysql:host=localhost;dbname=***", '***', '***');
} catch (PDOException $e) {
    echo $e->getMessage();
}
$sth = $dbh->prepare("SELECT username, captcha, timestamp 
    FROM xf_captcha 
    WHERE username = :username", array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':username' => $username));
print_r($sth->fetch());
?>

【问题讨论】:

  • 呃。 PDO,但内联变量。参数化一个准备好的语句,看在上帝的份上!
  • 你也应该将$sth = $dbh-&gt;query(...) 包围在一个try catch 块中,你不能保证它会成功。这可能是问题所在 - 或者它可能完成并返回 NULL。
  • 比,为什么编辑后的代码有效?
  • 我建议您阅读this answer I wrote,它会告诉您为什么@eggyal 嘴边会起泡沫,以及为什么您的代码非常不安全:)
  • @Fluffeh:你说的危险,get 是什么意思?哪个是真实脚本中的帖子?它可以让测试变得更容易。

标签: php mysql pdo fetch


【解决方案1】:

您的代码在问题的顶部有变量$username,但在底部有$user

您是否打算使用相同的变量?

$username = ($_GET ['user']);
$sth = $dbh->query( "SELECT username, user_state, last_activity, alerts_unread, conversations_unread, message_count 
  FROM xf_user WHERE username='$user'" );
  //                           ^^ Should this ALSO be $username ?   
$row = $sth->fetch();

编辑:好的,现在你的PDO::ATTR_EMULATE_PREPARES 很可爱。观察这一点:

数据库和表结构:

Database changed
mysql> show tables
    -> ;
+----------------+
| Tables_in_prep |
+----------------+
| users          |
+----------------+
1 row in set (0.00 sec)

mysql> select * from users;
+----+---------+--------+
| id | userid  | pass   |
+----+---------+--------+
|  1 | Fluffeh | mypass |
+----+---------+--------+
1 row in set (0.00 sec)

还有一些从你的复制而来的 PHP 代码,添加了 PDO 属性:

<?php
    //$username = ($_GET ['user']);
    $username="Fluffeh";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE userid='$username'" );
    echo "Trying to use $username.\n";
    print_r($sth->fetch());
    echo "----------------------------------------\n\n";
?>

<?php
    //$username = ($_GET ['user']);
    $username="user2693017";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE userid='$username'" );
    echo "Trying to use $username.\n";
    print_r($sth->fetch());
    echo "----------------------------------------\n\n";
?>

<?php
    //$username = ($_GET ['user']);
    $username="Oh my' or 1=1 or 'm=m";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE userid='$username'" );
    echo "Trying to use $username.\n";
    print_r($sth->fetch());
    echo "----------------------------------------\n\n";
?>

<?php
    //$username = ($_GET ['user']);
    $username="(select id from users limit 1)";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE id='$username'" );
    echo "Trying to use $username.\n";
    print_r($sth->fetch());
    echo "----------------------------------------\n\n";
?>

<?php
    //$username = ($_GET ['user']);
    // Changed this one to be a non-string, you might be checking an ID instead.
    $username="(select id from users limit 1)";

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    $sth = $dbh->query( "SELECT userid, pass FROM users WHERE id=$username" );
    echo "Trying to use $username.\n";
    print_r($sth->fetch());
    echo "----------------------------------------\n\n";
?>

<?php
    //$username = ($_GET ['user']);
    $username="bob'; drop table users; \  
    ";
    // This one is tricker to do in PHP code. I could easily enter this into a text field however.

    $dbh = new PDO('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);

    //$sth = $dbh->query( "SELECT userid, pass FROM users WHERE id='$username'" );
    echo "Trying to use $username.\n";
    print_r($sth->fetch());
    echo "----------------------------------------\n\n";
?>

还有输出:

    Trying to use Fluffeh.
stdClass Object
(
    [userid] => Fluffeh
    [pass] => mypass
)
----------------------------------------


    Trying to use user2693017.
----------------------------------------


    Trying to use Oh my' or 1=1 or 'm=m.
stdClass Object
(
    [userid] => Fluffeh
    [pass] => mypass
)
----------------------------------------


    Trying to use (select id from users limit 1).
----------------------------------------


    Trying to use (select id from users limit 1).
stdClass Object
(
    [userid] => Fluffeh
    [pass] => mypass
)
----------------------------------------


    Trying to use bob'; drop table users; \  
        .
----------------------------------------

哦,我把最后一个留到最后一个的原因是这个输出现在在我的数据库中:

mysql> show tables;
Empty set (0.00 sec)

是的,没错,我刚刚丢了一张桌子。让我再说一遍,我有一个 select 语句,我用一点技巧输入了一个值,任何有半脑和恶意意图的人都可以在文本字段中执行,然后删除了你的表。

现在,当然,如果您设置正确,您可能会为 select 语句设置一个不同的用户,并且只从您的数据库中授予他们select 权限,以阻止这种事情发生 - 但让老实说……你不是吗?

显然设置模拟是不够的。说真的,现在请转到read that answer,如果您想在代码中保持安全,请使用准备好的语句并使用参数。

【讨论】:

  • @user2693017 现在,请将此答案链接到您看到的代码与您的原始代码相似的朋友。告诉他们为什么他们需要编写好的代码,这样我就没有花这么多时间把这些放在一起只是为了帮助一个人。不要误会我的意思,即使只有你有所进步,这也是值得的,但让我们再传授一些吧:)
  • 请您从您的代码中取出所有的 try-catch 块吗?
  • @YourCommonSense 你是什么意思?删除它们以提高可读性?我把它们放在那里,因为我只是从 OP 复制代码并添加了仿真。
  • 为了让这段代码更加健全和安全。 特别是如果您要求某人在朋友之间分发链接。
  • 嗯,这对你我来说可能已经是千百次了,但是有些人以前根本没有听说过。我花时间把这些放在一起,希望能帮助他们。
【解决方案2】:

您的 Execute 语句在哪里被调用?这也在你的 -&gt;query 里面吗?如果不考虑使用以下内容来构建更好的查询。:

<?php
            $username = ($_GET['user']);
            try {
                $dbh = new PDO("mysql:host=localhost;dbname=***", '***', '***');    
            } catch (PDOException $e) {
                echo $e->getMessage();
            }
            $statement = "SELECT user, captcha FROM xf_captcha WHERE user=:username";
            //If you have query as a method(Which I don't think so but if you can change "prepare" to your "query"
            $sth = $dbh->prepare($statement);
            $sth->execute(array(":username" => $username));
            $row = $sth->fetch(PDO::FETCH_ASSOC);
    ?>

在执行括号中,您可以使用数组来填充变量:username 的参数$username

我认为查看 PDO 类示例也可能有助于更好地理解 PDO 和方法(您也可以参考 PHP PDO 手册)

【讨论】:

    【解决方案3】:

    当表不存在时也会发生这种情况。确保它确实存在,并且不只是由于硬盘驱动器错误而成为数据库中的持有者。

    如果发生这种情况,如果 phpMyAdmin 在尝试“手动”读取时出错,我建议您重新创建数据库/表。

    【讨论】:

    • 答案有什么问题?
    • 您描述了它可能失败的一个原因 - 而实际上并没有解释为什么一个不存在的表会导致它。与这个答案相比 - 它解释了理论......stackoverflow.com/questions/3349612/…“我建议你重新创建数据库/表。”,随机尝试不是一个好策略。
    • 您能否补充我的解释,或者更正您的观点?
    猜你喜欢
    • 2012-05-14
    • 1970-01-01
    • 2012-08-21
    • 2015-12-30
    • 1970-01-01
    • 2012-09-01
    • 2016-03-25
    • 2011-03-21
    • 2011-10-16
    相关资源
    最近更新 更多