我正在使用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()
答案 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()方法。