2

I recently ran into a problem with a database class I have been using where my class is having some trouble getting mysqli. There are a few classes at play here so bear with me. There is one class called Manager that connects to the DB and sets it up, it then instantiates a new class called Business and passes in the DB, Then later in code execution one of Manager methods runs that asks Business to save some data to the DB and this is where the problem is. The database class is below, sensitive info redacted:

namespace Lib;

define('_DBHOST', 'localhost');
define('_DBUSER', '***');
define('_DBPASS', '***');
define('_DBNAME', '***');

class DB_Conn {
    protected $DB;

    function __construct() { 
       $this->connect();
    }

    protected function connect() {
        $this->DB = new \mysqli(_DBHOST, _DBUSER, _DBPASS, _DBNAME);         

        if($this->DB == false) {
           die(mysqli_connect_error());
        } 
    }

    function __destruct() {
        $this->DB->close();
    }

    function getDB() { 

        if (!isset($this->DB)) { echo "connecting...<br />";
            $this->connect();
        }          
        return $this->DB;

    }

}

Here is the Manager class:

class Manager {
    public $mysqli;
    private $businessMan;

    public function __construct() {
        //Grab database and connect to it
        $database = new \Lib\DB_Conn();
        $this->mysqli = $database->getDB();

        $this->businessMan = new \Lib\Business($this->mysqli);
    }       

    public function submitForm($data) { 

            //Save Data
            if($this->businessMan->addBusiness($data)) {
                echo "Yay";
            } else {
                echo "NO!";
            }

    }
}

And here is the Business class

namespace Lib;    
class Business {
    var $mysqli;
    var $database;
    function __construct($mysqli) {
        $this->database = $mysqli;          
    }

    function addBusiness($data)
    {           
        $stmt = $this->database->prepare("INSERT INTO reboot_business(business_name) VALUES (?)");
        //Here $stmt is NULL

        //Bind the values from the array to the prepared statement
        $stmt->bind_param('s', $data['values']['name']);            

        //Execute the prepared statement and display any error
        if (!$stmt->execute()) {
            var_dump($stmt->error);
            //exit();
            return false;
        } else {
            //Succcess add ID to variable    
            return true;
        }                       
    }
}

I double checked the SQL statement and that was fine and the actual error occurs in the above Business class where I use the mysqli->prepare function. The error is:

Warning: mysqli::prepare(): Couldn't fetch mysqli
Fatal error: Call to a member function bindParam() on a non-object

I also did a check on the $stmt variable and that is NULL so it would seem the issue is in getting mysqli

Have I missed something or am I just doing it wrong?

I was able to get it to work by creating a new instance of the DB_Conn class in the Business constructor but that didn't seem right or should I just do it that way and not have the Manager class take care of the DB at all? Actually thinking of it if the Manager class is doing this DB stuff when it doesn't need it itself is that wrong from an OO point of view?

Hope I made sense and appreciate any help.

After doing some extra debugging I found that in the Business constructor mysqli is all fine output no errors and looks like it is working.

Edit:
However in my addBusiness() method I get some interesting output, I am using var_dump($this->database) and I get about 16 errors: Warning: var_dump(): Property access is not allowed yet.
There is no loop at work in my code anywhere which is what makes it come up 16 times wierd then when it finally works the var_dump reports all null values:
object(mysqli)#12 (19) { ["affected_rows"]=> NULL ["client_info"]=> NULL ["client_version"]=> int(50010) ["connect_errno"]=> int(0) ["connect_error"]=> NULL ["errno"]=> NULL ["error"]=> NULL ["error_list"]=> NULL ["field_count"]=> NULL ["host_info"]=> NULL ["info"]=> NULL ["insert_id"]=> NULL ["server_info"]=> NULL ["server_version"]=> NULL ["stat"]=> NULL ["sqlstate"]=> NULL ["protocol_version"]=> NULL ["thread_id"]=> NULL ["warning_count"]=> NULL }
What is wierd is how the database property is fine in the constructor but then empty in the method. Any ideas why it is getting emptied or set to null rather?

7
  • Could this be an issue of passing $mysqli in by reference? For example using & Commented Feb 16, 2015 at 21:34
  • I am not passing mysqli in by reference at the moment, I did give it a try but that gave another error. Commented Feb 17, 2015 at 9:48
  • Is it mysqli or PDO that you are using? If mysqli then it should be the 'bind_param' statement - Binds variables to a prepared statement as parameters that is used rather than the PDO bindParam statement. Commented Feb 17, 2015 at 20:37
  • It is mysqli, I changed it back to bind_param but still no difference in error, I just did some digging and you cant use named parameters with mysqli so I took that out. Commented Feb 17, 2015 at 21:44
  • have you checked that '$database' is a valid 'mysqli object? i would start checking it with var_dump from where it is is created and follow it through. I would also check for any error messages at each stage. Returns a string description of the last error Commented Feb 17, 2015 at 22:01

2 Answers 2

2

The error is because you are trying to access a non-set variable for your DB connection.

In your Business constructor function you declare:

$this->database = $mysqli;        

But then you are trying to access your MYSQLi connection as such:

$stmt = $this->mysqli->prepare()

You would need to reference your established database variable so it would be:

$stmt = $this->database->prepare()
Sign up to request clarification or add additional context in comments.

7 Comments

Thanks for the quick response that was a silly error on my part, I have corrected that now but the issue is still there. I edited the code above to reflect this.
Warning: mysqli::prepare(): Couldn't fetch mysqli, this error then causes the bind_param function to have a fatal error: Fatal error: Call to a member function bind_param() on a non-object in
Yeah, I was thinking it was due to the bind param. Try this: $stmt->bindParam(:business_name, $data['values']['name'], PDO::PARAM_STR); and change your prepare statement from ? to use :business_name
Per php manual: For a prepared statement using question mark placeholders, this will be the 1-indexed position of the parameter. So your "s" would need to be a 1. Or switch it to use :parameter names
That didn't work either I am afraid, still go the same errors, the Warning about fetching mysqli and then the Fatal error with bind_param. Useful note about prepared statements and question marks though.
|
0

The issue is that the 'DB_Conn' in the $database variable, goes out of scope at the end of the __construct() statement in Manager. Which triggers the __destruct() in DB_Conn which closes the database.

The answer:

Save the '_DB_Conn_' object in the Manager->database property rather than the 'PDO connection' from the _DB_Conn_ object.

New Manager class:

namespace Lib;

class Manager {

    public $database = null;

    private $businessMan;

    public function __construct() {
        //Grab database and connect to it
        $this->database = new \Lib\DB_Conn();

        $this->businessMan = new \Lib\Business($this->database->getDB());
    }

    public function submitForm($data) {

            //Save Data
            if($this->businessMan->addBusiness($data)) {
                echo "Yay";
            } else {
                echo "NO!";
            }

    }
}

Explanation:

The error is in the constructor of 'Manager'.

public function __construct() {
    //Grab database and connect to it
    $database = new \Lib\DB_Conn();
    $this->mysqli = $database->getDB();

    $this->businessMan = new \Lib\Business($this->mysqli);
}


class DB_Conn {

    ...

    function __destruct() {
        var_dump(__CLASS__, __METHOD__, 'this should not happen until the end!');
        $this->DB->close();
    }

I recreated your code and ran it. I got this error:

Couldn't fetch mysqli in P:\developer\xampp\htdocs\testmysql\Q28550480\Business.php on line 15

Which lead me to this comment:

The cryptic "Couldn't fetch mysqli" error message can mean any number of things, including:

  • Trying to use a closed database...

1 Comment

Brilliant, thanks so very much that solved it and I learnt something new in the process. I thought that when I put the $database variable into the mysqli property in 'Manager' it would be fine. Thanks again

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.