【问题标题】:SQLSTATE[42000]: Syntax error or access violation: 1064SQLSTATE [42000]:语法错误或访问冲突:1064
【发布时间】:2013-09-24 18:10:20
【问题描述】:

我现在正在编写一些软件。读者摘要版本:用户选择一个包,输入他们的姓名、电子邮件和所需的子域。然后检查子域以查看是否有人已经注册它,以及它是否只是字母数字。我使用 OO mysqli 完成了所有这些工作,但我决定转向 PDO。

错误的确切措辞:

SQLSTATE[42000]:语法错误或访问冲突:1064 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“->子域”附近使用正确的语法

当我开始实例化我的 Admin 对象时,一切都很好。然而,当我调用createAccount() 函数时,一切都变得混乱起来。堆栈跟踪到处都是,我几乎不知道什么时候开始解决这个问题。我在这里检查了其他答案,它们似乎都过于本地化,所以这里是产生它的代码,然后是所有包含错误的方法。我们来了……

首先,产生错误的代码:

include 'classes/Admin.class.php';
$admin = new Admin('test@test.com');

try {
    $admin->createAccount('John', 'Smith', 'test', 'pro');
}
catch(Exception $e) {
    echo '<pre />';
    echo $e->getMessage();
    die(print_r($e->getTrace()));
}

管理类构造函数

public function __construct($email) {
        $this->email = $email;

        include 'Database.class.php';
        include 'PasswordHash.php';

        define("DB_HOST", "localhost");
        define("DB_USER", "root");
        define("DB_PASS", "");
        define("DB_NAME", "ems");

        $this->data = new Database;
        $this->hasher = new PasswordHash(8, false);
    }

在 Admin 类中,检查子域

private function checkSubdomain() {
    $this->data->query('SELECT * FROM clients WHERE subdomain = :subdomain');
    $this->data->bind(':subdomain', $this->subdomain);
    $this->data->execute();

    return ($this->data->rowCount() == 0) && (ctype_alnum($this->subdomain));
}

PDO 类的执行、绑定和查询

public function execute() {
    return $this->stmt->execute();
}

public function query($query) {
    $this->stmt = $this->dbh->prepare($query);
}

public function bind($param, $value, $type = null) {
    if(is_null($type)) {
        switch(true) {
            case is_int($value):
                $type = PDO::PARAM_INT;
                break;
            case is_bool($value):
                $type = PDO::PARAM_BOOL;
                break;
            case is_null($value):
                $type = PDO::PARAM_NULL;
            default:
                $type = PDO::PARAM_STR;
        }
    }
    $this->stmt->bindValue($param, $value, $type);
}

这个错误在我的代码中很猖獗,所以我认为它只在 PDO 类中,但我的 Session 类工作得很好。此外,我的查询仅使用 mysqli 即可完美运行,因此我对自己的语法很有信心。非常感谢任何帮助。

根据要求,创建账户功能:

public function createAccount($firstname, $lastname, $subdomain, $package)
{
    $this->firstname = $firstname;
    $this->lastname  = $lastname;
    $this->subdomain = $subdomain;
    $this->package   = $package;
    //does the subdomain exist, or is it invalid?
    if(!$this->checkSubdomain())
        throw new Exception('This domain is not available. It can only contain letters and numbers and must not already be taken.');

    //now comes the table creation, provided everything is in order
    $this->setup();
}

以下是 createAccount 调用的设置函数(不包含所有表结构):

private function setup() {
    $isError = false;
    $queries = array(
        //all of these are CREATE statements, removed for security reasons
        );

    //need a database that matches with the client subdomain
    $this->data->query('CREATE TABLE $this->subdomain');
    $this->data->bind(':subdomain', $this->subdomain);

    //PDO secured...execute
    $this->data->execute();

    $this->data->beginTransaction();
    foreach($queries as $query) {
        $this->data->query($query);
        if(!$this->data->execute()) { //we hit some sort of error, time to GTFO of here
            $isError = true;
            $this->data->cancelTransaction();
            //die('Error with: ' . $query);
            break;
        }
    }

    if(!$isError) {
        $this->data->endTransaction();
        mkdir('a/' . $this->subdomain, 0755);
        $this->generatePass();
        //this is where I insert the user into the admin table using PDO, removed for security
        $this->data->execute();
    }

    $this->data->close();

}

【问题讨论】:

  • 您需要找到导致此错误的确切行。错误消息直接来自 MySQL RDBMS,并且您的部分 PHP 代码 -&gt;subdomain 正在连接到某处的查询中。您在上面发布的代码看起来不会产生这样的错误,因为您正确地调用了bindValue()
  • 首先查看(并发布)createAccount() 函数。
  • 发布了 createAccount() 和 setup() 函数。为了安全起见,我删除了表结构。如果您需要特定的 SQL 语句,请告诉我,我们可以从那里开始。
  • $this-&gt;data-&gt;query('CREATE TABLE $this-&gt;subdomain'); - 您需要将 SQL 中的变量转义。
  • 哎呀!谢谢安德鲁西。你看下一行很好,但我有一个小小的心理失误,它应该是 :subdomain 而不是 $this->subdomain.

标签: php mysql pdo


【解决方案1】:

这是导致错误的原因:

$this->data->query('CREATE TABLE $this->subdomain');
$this->data->bind(':subdomain', $this->subdomain);

正如 Michael Berkowski 和 andrewsi 在 cmets 中指出的那样,您不能将值绑定到 :subdomain 占位符,因为它没有在查询中注明,即使是 PDO 占位符也只能用于值而不是数据库、表或列名

如果您想动态创建此类 SQL 查询,您需要将数据库、表或列名括在反引号中(以防您的列和名称包含可能破坏查询)和转义值,但如果已经使用PDO,则不能使用MySQLi

由于 PDO 没有提供可以做到这一点的 real_escape_string() 方法,并且在实践中不需要转义这样的值(除非你真的有名为 Ye'name 的列,这完全是愚蠢的恕我直言),所以使用preg_match()preg_replace() 的简单过滤器就足够了:

if (preg_match('/^[\w_]+$/i', $this->subdomain)) {
    // note the ` (backtick), and using " (double quotes):
    $this->data->query("CREATE TABLE `{$this->subdomain}`"); 
} else {
    // throw exception or error, do not continue with creating table
}

在 PHP 中使用 '(单引号 - 撇号)与 "(双引号)字符串的几个示例:

$a = 1;
$b = 2;
echo '$a + $b'; // outputs: $a + $b
echo "$a + $b"; // outputs: 1 + 2
$c = array(5, 10);
echo '\$c[0] = {$c[0]}'; // outputs: \$c[0] = {$c[0]}
echo "\$c[0] = {$c[0]}"; // outputs: $c[0] = 5

双引号内的{} 字符串用于数组和对象属性访问,可用于常规变量。
在双引号中转义$\$ 完成,否则它将假定一个变量调用。

【讨论】:

  • /^[\w_]+$/i有一些废话。 \w 表示[A-Za-z0-9_],因此[\w_] 应替换为\w,并且在\w 已经不区分大小写时声明不区分大小写的模式修饰符是没有意义的。应简化为:/^\w+$/
【解决方案2】:

我遇到了这个错误,但原因略有不同。你必须在 SELECT 语句中留下空格 'SELECT'.$userfield.' FROM '.$usertable. ' WHERE 1' 效果很好,但 'SELECT'.$userfield.'FROM'.$usertable.'WHERE 1' 失败得很惨。

    $stmt = $dbh->query(
    'SELECT ' . $userfield . ' FROM ' . $usertable . ' WHERE 1 '
    );
    $results = $stmt->fetchAll(PDO::FETCH_ASSOC);

如果有人通过 42000 故障代码查找点击此条目,则提供信息。

【讨论】:

  • 这是 SQL 错误,而不是 PDO 错误。该问题专门针对 PDO 占位符来引用表/列名称,而不是您引用的实际 SQL 语法。
猜你喜欢
  • 2015-10-12
  • 2022-01-25
  • 2017-10-29
  • 2014-01-14
  • 2013-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多