【问题标题】:Issues with mysqli::prepare() and bind_param() [closed]mysqli::prepare() 和 bind_param() 的问题 [关闭]
【发布时间】:2013-02-05 00:45:42
【问题描述】:

我的应用程序中有以下类:

    <?php

    class Connection extends Mysqli{

public function __construct($mysqli_host,$mysqli_user,$mysqli_pass, $mysqli_db) {
    parent::__construct($mysqli_host,$mysqli_user,$mysqli_pass,$mysqli_db);
    $this->throwConnectionExceptionOnConnectionError();     
}

private function throwConnectionExceptionOnConnectionError(){

    if(!$this->connect_error){
        echo "Database connection established<br/>";
    }else{
    //$message = sprintf('(%s) %s', $this->connect_errno, $this->connect_error);
        echo "Error connecting to the database."; 
    throw new DatabaseException($message);
    }
}
    }

    class DatabaseException extends Exception
    {
    }

class Page extends Mysqli{

private $con; 

public function __construct(Connection $con) {
    $this->con = $con;
    if(isset($_GET['id'])){
    $id = $_GET['id'];
    }else{      
    $id = 1;
    }       
    $this->get_headers($id);
    $this->get_content($id);
    $this->get_footer($id);
}

private function get_headers($pageId){ 
    $retrieveHead = $this->con->prepare("SELECT headers FROM pages WHERE page_id=?");
    $retrieveHead->bind_param('i',$pageId);
    $retrieveHead->execute();
    $retrieveHead->bind_result($header);
    $retrieveHead->fetch();
    $retrieveHead->close();
    echo $header;   
}

private function get_footer($pageId){ 
    $retrieveFooter = $this->con->prepare("SELECT footer FROM pages WHERE page_id=?");
    $retrieveFooter->bind_param('i',$pageId);
    $retrieveFooter->execute();
    $retrieveFooter->bind_result($footer);
    $retrieveFooter->fetch();
    $retrieveFooter->close();
    echo $footer;   
}

private function get_content($pageId){
    $retreiveContent = $this->con->prepare("SELECT template_id, section_title, i1, i2 FROM content WHERE page_id=? ORDER BY sequence DESC");
    $retreiveContent->bind_param('i',$pageId);
    $retreiveContent->execute();
    $retreiveContent->bind_result($template_id, $section_title, $i1, $i2);
         while ($retreiveContent->fetch()) {
            //Variables will be populated for this row.
            //Update the tags in the template.
            $template = $this->get_template($template_id);
            $template = str_replace('[i1]',$i1,$template);
            $template = str_replace('[i2]',$i2,$template);
            //$theTemplate is populated with content. Probably want to echo here
            echo $template;
        }
}

private function get_template($template_id){
    $retreiveTemplate = $this->con->prepare("SELECT code FROM templates WHERE template_id=?");
    $retreiveTemplate->bind_param('i',$template_id);
    $retreiveTemplate->execute();
    $retreiveTemplate->bind_result($template);
    $retreiveTemplate->fetch();
    $retreiveTemplate->close();
    return $template;
}

}
    ?>       

小型应用程序基本上应该从 url 获取页面变量,并使用它来提取页面的页眉、内容项和页脚,内容项使用模板呈现,该模板也从数据库中检索。运行包含以下代码的索引文件时:

    require ("/req/db_connection.php");
    require ("/req/connection.php");
    $dbConnection = new Connection($mysqli_host,$mysqli_user,$mysqli_pass, $mysqli_db);
    $page = new Page();

我得到以下输出:

    Database connection established

    Warning: mysqli::prepare(): Couldn't fetch Page in C:\xampp\htdocs\forum\req\connection.php on line 39

    Fatal error: Call to a member function bind_param() on a non-object in C:\xampp\htdocs\forum\req\connection.php on line 40

谁能指出我为什么会遇到这些错误的正确方向?

第 39 行是以下语句:

     $retrieveHead = $this->prepare("SELECT headers FROM pages WHERE page_id=?");

第 40 行当然是:

     $retrieveHead->bind_param('i',$pageId);

任何帮助将不胜感激。

注意上面的代码现在已经根据下面的答案更正了,但是我在第 80 行仍然遇到以下错误:

    Call to a member function bind_param() on a non-object in C:\xampp\htdocs\forum\req\connection.php on line 80

这是哪一行:

    $retreiveTemplate->bind_param('i',$template_id);

有什么建议吗?

【问题讨论】:

  • SELECT 'headers' FROM pages WHERE ... 你的列名有单引号。
  • 我不知道为什么在里面。在我的实际代码中不存在所以这不是问题。
  • 在切题上,你的函数名让我想起了商业环境中的命名空间地狱,例如Company.Subsidiary.Team.Project.Namespace.Class.someone_forgot_to_use_using。哦,你还有一个额外的}
  • Hi Hiroto,这只是我重新回到 PHP 的尝试。这不是一个商业项目(还),我只是从 OOP 方法开始,所以我还不太关心命名空间或类似的东西。

标签: php sql mysqli


【解决方案1】:
class Page extends Mysqli{
public function __construct() {
    if(isset($_GET['id'])){
    $id = $_GET['id'];
    }else{      
    $id = 1;
    }       
    $this->get_headers($id);
    $this->get_content($id);
    $this->get_footer($id);
}

上面的这段代码看起来像问题(以及其他)。您扩展 mysqli,覆盖构造函数并且不调用父构造函数。结果,没有调用任何基本的 mysqli 构造函数例程,并且您没有任何连接。考虑重新设计您的对象并将您的 Connection 对象传递给它们,而不是在任何地方扩展 mysqli。我也会尽量避免在构造函数中做所有这些工作。例如,您的页面类可能类似于:

class Page{

private $con;

public function __construct(Connection $con) {
    $this->con = $con;
}

public function get_headers($pageId){ 
    $retrieveHead = $this->conn->prepare("SELECT headers FROM pages WHERE page_id=?");
    //...
}

}

当我在上面打趣说“除此之外”(无意气馁)时,我在暗示——虽然你的错误的实际原因是构造函数——你的类和 OOP 概念的设计看起来有些混乱一般来说。

class Connection extends Mysqli{

 public function __construct($mysqli_host,$mysqli_user,$mysqli_pass, $mysqli_db) {
    parent::__construct($mysqli_host,$mysqli_user,$mysqli_pass,$mysqli_db);
    $this->throwConnectionExceptionOnConnectionError();     
 }
}

在这里,您使用您的Connection 类扩展了mysqli,并正确调用了父构造函数并传递了所有必需的参数以建立连接。如果您实例化此类型的对象$myConnection = new Connection(...),它可以充当您通往数据库的网关。

Page 这样的其他类可能需要对数据库执行操作,它们可以通过Connection 来完成,而不是通过扩展mysqli 本身。您可以在Page 构造函数中传递Connection 的实例来实现此目的。所以你的代码行在这里:

$dbConnection = new Connection($mysqli_host,$mysqli_user,$mysqli_pass, $mysqli_db);
$page = new Page();

会变成(在对 Page 类进行一些修改后,如上所示):

$dbConnection = new Connection($mysqli_host,$mysqli_user,$mysqli_pass, $mysqli_db);
$page = new Page($dbConnection);

【讨论】:

  • 还有其他的吗?请详细说明。请记住,我刚开始使用面向对象的方法(我在 Uni 的第一年用 Java 做过,但后来专注于过程 PHP)。我实际上认为连接是问题,但在这里的其他地方被告知扩展 mysqli 或者实际上是 Connection 对象可以解决我的问题。我将如何将我的对象传递给 Connection 对象?
  • 道歉。在许多方法中,OOP 通常看起来很愚蠢,尤其是在关注点分离方面。如果数据库连接对象是活动的,它应该允许访问所有其他类,以防止需要在每个类中调用数据库对象(代码是为了代码)。我一直在为 OOP 苦苦挣扎,因为与使用过程方法学以更少的代码行来实现相同的方法相比,很多 OOP 似乎毫无意义。
  • 好的,所以我修改了上面的代码,并且正在输出标头,但是我在第 80 行仍然遇到错误:调用非对象上的成员函数 bind_param()
  • @jme,你正在取得进展。如果您在bind_param() 失败,则您的连接正常,但请检查prepare() 中的SQL 是否正常。我已经删除了复制并粘贴单引号,如果围绕“标题”,如果这让你失望。
  • SQL 查询没有问题。从模板中选择代码 WHERE template_id=?
【解决方案2】:

显示此错误消息是因为您在尝试准备语句时尚未建立与数据库服务器的连接。更短的重现代码:

class Page extends Mysqli{
    public function __construct(){
        $this->prepare('SELECT CURRENT_TIMESTAMP');
    }
}
new Page;

在 mysql 对象中,连接是在 constructor 中建立的,但是您覆盖了这样的构造函数并且从不调用父构造函数。

恕我直言,页面是数据库对象的实例毫无意义。您应该在需要时简单地提供一个 DB 对象作为参数。

更新:

您可以在每次需要时传递 DB 对象:

class Page{
    public function foo(Mysqli $db){
        $db->prepare('SELECT CURRENT_TIMESTAMP');
    }
}

...或将其存储以备后用:

class Page{
    protected $db;
    public function __construct(Mysqli $db){
        $this->db = $db;
    }
    public function foo(){
        $this->db->prepare('SELECT CURRENT_TIMESTAMP');
    }
}

如果你扩展Mysqli,使用正确的类名作为类型提示:

public function foo(Connection $db){
}

还有其他解决方案,例如接口,但它变得太复杂了:)

【讨论】:

  • 我将如何提供一个数据库对象作为参数(请记住,我是过程 PHP 的老手,我刚刚开始使用 OOPHP。
  • @jme1988 - 我添加了几个例子。
  • 谢谢。我今晚回家时会试试这个。如果它有效,那么我会将您的回复标记为正确,因为您提供了所有必需的信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-03
  • 1970-01-01
  • 2022-06-10
  • 2017-07-11
  • 1970-01-01
  • 2023-03-25
相关资源
最近更新 更多