如何从PDO prepare语句中获取结果

时间:2018-09-29 05:50:21

标签: php pdo prepared-statement

我正在使用PDO prepare语句选择结果。

我的index.php是:

include('operations.php');
$userprofileobj = new operations();
if(isset($_SESSION['user_email']))
{
$results = $userprofileobj->verify_user('account',$_SESSION['user_email'])->fetch(PDO::FETCH_ASSOC);

echo $results['username'];
}

我的operations.php是:

<?php
include('userclass.php');
class operations extends userclass{
    public function verify_user($table_name,$user_email)
{
    $stmt = $this->con->prepare("select * from ".$table_name." where username = :user_email");

    $stmt->execute([
        ':user_email' => $user_email,
    ]);

return $stmt->fetchAll(PDO::FETCH_ASSOC);

}
}

我正在尝试匹配电子邮件,结果应该在index.php中获取。但是我遇到了错误致命错误:在第84行,在Operations.php中的布尔值上调用了成员函数fetchColumn()

2 个答案:

答案 0 :(得分:3)

您不是result变量(除了被覆盖外)不是您想的那样。这是PDOStatment

试试看

$stmt = $this->con->prepare("select * from ".$table_name." where username = :user_email");

$stmt->execute([
    ':user_email' => $user_email,
]);

if (false !== ($row = $stmt->fetchColumn()))
{
    return $row;
}

但是,这只会返回第一行的第一列。相反,您可能想要:

 return $stmt->fetchAll(PDO::FETCH_ASSOC);

我将$result更改为$stmt,因为这不是结果,而是语句对象。

原始问题

在您的原始代码(请参见下文)中,您正在用execute的返回值覆盖它,它是一个布尔值。

//old code (don't use this)
$result = $result->execute([
    ':user_email' => $user_email,
]);

//$result = TRUE|FALSE

if ($result->fetchColumn() !== false)
{
    return $result;
}

然后您尝试调用布尔值的方法,该方法不会起作用。但是问题远不止于此。假设您没有覆盖它。

//old code (don't use this)
$result->execute([
    ':user_email' => $user_email,
]);

//$result = PDOStatment object.

if ($result->fetchColumn() !== false)
{
    return $result;
}

现在结果仍然是您的PDOStatement,这很好,但是正如我所说,您不保存获取的数据。这次您返回PDOStatement对象。这不是你想要的。

然后,正如我之前所说,如果您保存并返回它。它可能仍然不是您所追求的。因为fetchColumn()一次只能访问一行,而只能访问一列。

但是我无法知道您想要什么。也许那是您想要的?在这种情况下,您的查询不如理想查询。也许您只想查看用户是否存在给定的电子邮件?在这种情况下,我将使用此查询。

$result = $this->con->prepare("SELECT id FROM ".$table_name." WHERE username = :user_email");

$result->execute([
    ':user_email' => $user_email,
]);
//there is no need to check it (see below)
return $result->fetchColumn();
  

PDOStatement :: fetchColumn()从结果集的下一行返回单个列,如果没有更多行,则返回FALSE。

我还可以通过您的资料告诉您,您的数据库设置可能是错误的,也就是说,如果您确实需要动态表$table。我可以说这是因为您不应该复制任何用户数据(或实际上是任何数据,这被称为规范化),并且使表具有动态性意味着电子邮件可以单独存在于两个(或多个)表中。如果不是这种情况,请不要使其动态化。为什么这是一个问题,请仔细考虑一下,如果用户现在更改其“电子邮件”(因为它存在于2个表中)可能会发生什么,则您必须在两个地方都进行更新。但是那会更糟,因为它会使您处理电子邮件的任何事情变得复杂。

我没有看到表的架构,只能推测它以及如何解决它。但是通常,您将使用外键并将用户记录与此相关联。然后,使用JOIN即可访问电子邮件,而无需重复。

那就是说在某些情况下这是可以接受的,但是我无法知道在您的情况下这是否正确。一个简单的例子是为用户和管理员(基本上是2个用户系统)创建一个单独的表。

安全性

最后一件事情要非常小心:

"select * from ".$table_name." where username = :user_email"

这里的问题是它对SQL注入开放。每当将变量连接到SQL中时,就为注入攻击打开了大门。好吧,您可能会说我正在传递罐头字符串account。可以,但是在故障点没有验证。因此,也许在5个月内您将重用此代码,而忘记了从未验证表名。也许不是,但是事实仍然是,如果用户数据可以进入该参数,那么您就无法针对表名进行任何注入保护,

像这样简单的事情

  public function verify_user($table_name,$user_email){
     $allowed = ['account','users'];
     if(!in_array($table_name, $allowed )) throw new Exception('Invalid table name');
 }

现在看,几乎不可能在表名中注入一些内容。此外,由于采用相同的方法(在发生故障时),您将永远不会失去这种保护。匆匆忙忙,然后复制一段代码来更改一些事情,这很容易.....众所周知。

只要三美分。

更新

因此,即使用户输入进入$table的机会很小,您也不能保证100%的输入,因为在verify_user内部您无法知道数据的来源,但是您相信这不是用户输入。当涉及到SQLInjection时,您不能说很好,因为我只会以某种方式调用此方法。它必须是100%防注入的,或者要尽可能接近人类。

为什么这很重要?想象一下。

   $userprofileobj->verify_user('account --',$_SESSION['user_email']);

这两个小--与PHP中的//类似,但是对于SQL来说,它们要做的就是注释掉SQL中其余的行,以便您的查询成为此。

"select * from account -- where username = :user_email"

或(基本上)

"select * from account"

因此,我们刚刚修改了查询的功能。现在,庆幸的是,实际上不可能在PDO中一次运行2个查询,您可以在MySqli中做到(有一些工作)。但是由于安全原因,他们基本上已经取消了此功能。原因是这样的(或更糟糕的是像创建数据库用户一样)。

  $userprofileobj->verify_user('account; DROP TABLE account --',$_SESSION['user_email']);

如果您可以进行2次查询,则会这样做:

 SELECT * FROM account
 DROP TABLE account

无论如何,这是危险的东西,要不惜一切代价避免。懒惰(而且我是一个懒惰的程序员,所以请不要误会)将表名放入数据库中,并且您已经将用户数据暴露给第三方之后,您不想给出答案。这不是一个选择。

所有这些操作:

if(!in_array($table_name, ['table1', 'table2', ...])) throw new Exception('Invalid table name');

如果“ needle” $table_name不在“ haystack”中,则引发错误。表名称的罐头列表。因此,如果我这样做(使用上面的示例):

if(!in_array('account --', ['table1', 'table2', ...])) throw new Exception('Invalid table name');

它不会在我们的account --table1列表中找到table2,并且会爆炸,从而防止了注入攻击。

干杯。

答案 1 :(得分:1)

$result = $this->con->prepare("select * from ".$table_name." where username = :user_email");

$result = $result->execute(..)

您正在覆盖$ result。 $ this-> con-> prepare(..)将$ result设置为PDO语句(请参见http://php.net/manual/en/pdo.prepare.php)。 PDO Statement对象具有方法-> execute(...),该方法返回布尔值(true / false)以及-> fetchColumn()方法。当您执行execute()时,您将用execute()的结果覆盖您的PDO语句对象,后者只是一个布尔值,根本没有任何方法。因此,$ result没有-> fetchColumn()方法。