1

I have created my own database class to simplify MySQLi prepared statements and I am trying to access this in another class.

I have extending the database class to my profile class and tried to run the select query within my construct and this error is returned:

Fatal error: Call to a member function prepare() on null in /home/matt500b/dev2.csgoberry.com/includes/database.class.php on line 80

profile class construct:

public function __construct($username) {
    $params = parent::SELECT("SELECT users_info.*, users.username, users.lastOnline FROM users_info INNER JOIN users ON users.id = users_info.user_id WHERE users.username = ?", array('s', $username));
    $this->fname = (isset($params[0]['fname']) ? $params[0]['fname'] : null);
    /*More settings done here*/
}

However if I run the query first then send the data to the construct like so:

$profileData = $db->SELECT('SELECT users_info.*, users.username, users.lastOnline FROM users_info INNER JOIN users ON users.id = users_info.user_id WHERE users.username = ?', array('s', $pid));
$pclass = new user_profile($profileData);

with the construct being:

public function __construct($params){
        $this->fname = (isset($params[0]['fname']) ? $params[0]['fname'] : null);
}

Everything works nice. Of course the database instance was called using $db = new database(HOST, USER, PASSWORD, DATABASE, DEBUG); where my database construct is:

public function __construct($db_host, $db_user, $db_pass, $db_name, $debug){
    $this->link = mysqli_connect($db_host, $db_user, $db_pass);
    mysqli_select_db($this->link, $db_name);
    $this->debug = $debug;
}

The error relates to this line: if ($stmt = $this->link->prepare($sql)) { and suggests that $sql is null however the two queries are exactly the same except for their origins (parent::SELECT vs $db->SELECT).

Any suggestions to make this work?

Many thanks

2
  • $this->link is null not $sql. I don;t see this line of code in your code examples in the question. You question is pretty hard to follow in that it is not clear what the whole call stack looks like. Commented May 24, 2016 at 20:37
  • $this->link is defined in the database construct. Commented May 24, 2016 at 20:39

1 Answer 1

2

When you extend a class, and both the parent and child class have a constructor, you must call the parent constructor from the child. If the parent constructor has arguments, you must then either a) get those arguments in the child constructor (best practice), or b) use hard-coded values (generally bad practice).

I assume that either a) your parent class establishes the database connection in its constructor, or b) you pass a database connection to the parent class. If the latter (b), then you have to take that db connection as an argument to the child class and manually evoke the parent constructor, OR, re-implement the tasks from the parent constructor in the child class. If the former, (a), the above is true BUT you are engaged in a bad practice. A class constructor should not do work, it should not know about other classes except its ancestors and typehinted constructor arguments. The new keyword in a constructor body is usually a sign of a "code smell' -- you create a hidden dependency.

Instead of this for a class that uses the database:

public function __construct ($username, $password) {
    // establish database connection using username and password
}

....

$myObject = new SomeClass('ausername', 'apassword');

... do this:

$dbConnection = establishDbConnection($username, $password); // establish connection outside of the class
$myObject = new SomeClass($dbConnection); // pass in the connection into the object

^ that is called "Dependency Injection", and is a worthwhile topic to learn!

Here is an example of calling a parent constructor:

class A {
    private $a1 = null;
    public function __construct($arg1) {
        // do something with the args
        $this->a1 = $arg1;
    }
}

class B extends A {
    private $a2 = null;
    public function __construct($arg1, $arg2) {
        parent::__construct($arg1);
        $this->a2 = $arg2;
    }
}

Documentation

Sign up to request clarification or add additional context in comments.

7 Comments

So when I create a new instance of b will I need to do something like $myVar = new b ($classAParams, $classBParams); and use classAParams to use in the constructor for class A ?
@Matt Exactly, yes, though if Class A is establishing a DB connection inside its constructor, check out my note on that (edited the post)... not good practice.
In my db class I create a new instance using $db = new database(HOST, USER, PASSWORD, DATABASE, DEBUG); (you can see the db construct in my question). Are you saying it's best to pass the value of $db to classB instead of passing the parameters for class A to class B? If so, how would I go around sending $db to B so that I can use the database class such that I don't need to use classA construct?
A class should do one thing. So, you have a database class that's just a database class, it gets rows, etc. Don't extend that database class, make an instance of it and pass it into objects that will use it. Let's say that Class A is an "Animal", and class B is a "Dog", which extends "Animal". Neither of these classes should have any idea how to make a database object, they just get it in their constructor and use it.
@Matt Here's a quick example that should help clarify: pastebin.com/rwrKwYj2 For more reading, look up SOLID. Don't be dismayed if you don't immediately understand it all :)
|

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.