准备好的语句返回False

时间:2016-12-10 20:35:35

标签: php mysql mysqli prepared-statement

我知道有许多问题与我的问题类似。但我真的无法解决这里的问题。我的课程名为' UsersClass'负责与用户相关的每项任务。而且我使用mysqli prepare语句从数据库中插入或选择数据,问题是在很多情况下prepare语句返回false。我通过每次都建立新的连接解决了这个问题,但这导致了其他功能的另一个问题" 2006:Mysql服务器已经消失了#34;请参阅以下代码: 在这里和其他函数中,prepare返回false。

function isPostingAllowed() {
  $this->setPsSelectUsers($this->_connection->prepare("Select * from userpermissions where userpermissions.UserId = ? and userpermissions.PermissionId = 1"));
 $this->_psSelectUsers->bind_param('i',$this->UserId);
 $this->_psSelectUsers->execute();
        if ( false===$this->_psSelectUsers ) {
        die('prepare() failed: ' . htmlspecialchars($this->_connection->error));}
 if ($this->_psSelectUsers->fetch()){
     return true;
 }else{
     return false;
     }}


function setPsSelectUsers($stmt) {
    $this->unsetPsSelectUsers();
           // mysqli_close($this->_Connection);
           // $this-> __construct();

    $this->_psSelectUsers= $stmt;}

当我取消注释这两行时第一个函数将起作用并且准备staement不会返回false,但在这种情况下,以下函数将抛出错误2006:

 function checkUserAuthentication() {
 $this->setPsSelectUsers($this->_connection->prepare("SELECT UserLogInName FROM Users WHERE UserLogInName=? AND UserPassword=?"));
 $this->_psSelectUsers->bind_param('ss',$this->UserLogInName, $this->UserPassword);
 $this->_psSelectUsers->execute();
 if ($this->_psSelectUsers->fetch()){
     return true;
 }else{
     return false;
     }}

那么如何解决第一个问题而不会出现新问题?

1 个答案:

答案 0 :(得分:2)

问题:

  

...我使用mysqli prepare语句从数据库中插入或选择数据,问题是在很多情况下prepare语句返回false。

这是因为您以乱序方式运行查询,即您在关闭前一个语句对象之前执行->prepare()。根据您当前的代码,在准备好的语句中添加以下错误报告代码,

if(!$this->_connection->prepare(...)){
    printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
    die();
}

如果查看$this->_connection->error,您会看到以下错误,

  

命令不同步;你现在不能运行这个命令

许多论坛都记录了这个问题,例如:

  • 来自MySQL documentation

    如果你的命令不同步;您现在无法在客户端代码中运行此命令,而是以错误的顺序调用客户端函数。

  • 从此SO thread

    您不能同时拥有两个查询,因为默认情况下mysqli使用无缓冲的查询(对于预准备语句; ...

解决方案:

以正确的顺序执行命令,(See this example code

  1. 打开连接(一次:在开始时,而不是在执行每个查询期间)
  2. 创建预备声明
  3. 绑定参数
  4. 执行查询
  5. 绑定结果变量
  6. 将值提取到这些绑定变量
  7. 关闭声明对象
  8. 关闭连接(一次:最后,而不是在执行每个查询期间)
  9. (按照步骤2到7执行所有查询,但根据您的查询,一个或多个步骤可能是可选的)

    所以解决方法是,在再次调用->prepare()之前关闭先前的语句对象。从$this->unsetPsSelectUsers();方法中调用此setPsSelectUsers()方法并将其放在if ($this->_psSelectUsers->fetch()){...}else{...} isPostingAllowed()checkUserAuthentication()方法块之前。此外,将$this->_psSelectUsers->fetch()方法调用的状态保存在变量中,并在随后的if块中使用它。所以你的代码应该是这样的:

    public function isPostingAllowed() {
        $this->setPsSelectUsers($this->_connection->prepare("Select * from userpermissions where userpermissions.UserId = ? and userpermissions.PermissionId = 1"));
        if(!$this->_psSelectUsers){
            printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
            die();
        }
        $this->_psSelectUsers->bind_param('i',$this->UserId);
        $this->_psSelectUsers->execute();
        $status = $this->_psSelectUsers->fetch();
        $this->unsetPsSelectUsers();
        if ($status){
            return true;
        }else{
            return false;
        }
    }
    
    private function setPsSelectUsers($stmt){
        $this->_psSelectUsers= $stmt;
    }
    
    private function unsetPsSelectUsers() { 
        if (isset($this->_psSelectUsers)) { 
            $this->_psSelectUsers->close(); 
            unset($this->_psSelectUsers);
        }
    }
    
    public function checkUserAuthentication() {
        $this->setPsSelectUsers($this->_connection->prepare("SELECT UserLogInName FROM Users WHERE UserLogInName=? AND UserPassword=?"));
        if(!$this->_psSelectUsers){
            printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
            die();
        }
        $this->_psSelectUsers->bind_param('ss',$this->UserLogInName, $this->UserPassword);
        $this->_psSelectUsers->execute();
        $status = $this->_psSelectUsers->fetch();
        $this->unsetPsSelectUsers();
        if ($status){
            return true;
        }else{
            return false;
        }
    }
    

    此外,您不必再使用此setPsSelectUsers()方法,您可以直接在您的方法中使用属性$_psSelectUsers,如下所示:

    $this->_psSelectUsers = $this->_connection->prepare(...);