1

Before I ask my question I want to make sure a few things are clear:

1) I read the official PHP manual on OOP (intro, basics, inheritance, properties and so on) It seems im honestly not getting something. Im on it for hours now and fixed a few things there and then but new errors are popping up.

2) I read the error messages Missing argument 1 for Main::__construct() and Undefined variable: array. This means my $start variable is NULL.

3) I did do a few searches on stackoverflow either it is not fully related or very hard for me understand whats going on since the example code is so complex (Not the best thing for a starter).

My question: Why is the below code not working? I would really appreciate why it is failing and what I have to consider.

    class Main {
    protected $config;
    protected $start;

        function __construct($arr) {
            if(!isset($this -> start)) {
                if ($arr['secure']){
                    $this -> config = parse_ini_file('C:\xampp\htdocs\project\config.ini');
                    $this -> start = mysqli_connect($this -> config['host'], $this -> config['username'], 
                                                    $this -> config['password'], $this -> config['database']);
                }
                else {
                    $this -> start = mysqli_connect($arr['host'], $arr['username'], $arr['password'], $arr['database']);
                }
            }
            if($this -> start == false) {
                return mysqli_connect_error();
            }
        }
    }

$Main = new Main($array = ["host" => '', "username" => '', "password" => '', "database" => '', 
                           "errors" => false, "secure" => true]);

    class Test extends Main {
        public function sample() {
            $query = "SELECT id, user,
                      FROM users";
            $result = mysqli_query($Main -> start , $query);
            $row = mysqli_fetch_array($result);
            echo $row['user'];
        }
    }       

    $x = new Test();

    $x -> sample();

4 Answers 4

3

So here's what happens on run time:

$Main = new Main(...);

OK, you may get a connection there if those details are filled in, but there is an issue in determining whether you made a successful connection or not. See below for more info, but $Main is important to note for now.

$x = new Test();

Class Test extends your class Main. Your class Test therefore inherits the class Main's constructor, which requires an argument. The argument isn't provided, so it generates a warning.

To account for this, make $arr optional if you're not always going to be passing it:

function __construct($arr = array())

Then check if it exists by using isset on the index:

if(isset($arr['secure'])) // ...

Fast forward, because you haven't provided $arr, you will not be able to successfully connect to your DB. According to mysqli::__construct(), which mysqli_connect is an alias of, it will try to return an object ("which represents the connection to a MySQL Server.").

Now, take a look at these lines:

$this -> start = mysqli_connect( ... )
// ...
if ($this -> start == false) {

You must check against the return's connect_error attribute (or mysqli_connect_error()) to verify if the connection worked or not.

Thanks to the comments below, you should ALSO check for a false assignment, as mysqli_connect has been known to generate a warning and return false too, even though it is not shown on the PHP docs.

Let's continue.

$x -> sample();

Test::sample uses mysqli_query which expects the database connection as it's first argument. You attempt this by passing $Main->start.

Unfortunately, $Main is not in your variable scope, and cannot be accessed from inside of that method. What you need to reference is $this->start.

In fact, if this is the only reason you instantiated $Main, then you don't need to at that point. Instead, pass the connection details array through to new Test() instead.

Here's two solutions:

  1. Pass your DB connection details through to $x = new Test(); The instance will then connect to the DB as intended and will be able to run the query.
  2. Separate class Test from class Main, and pass an instance of class Main through your class Test constructor. Probably the better option. If your Test is meant to be for your query don't have it extend Main, create your Database connection object (new Main(..)) and then pass that through into new Test($Main). In your Test class, create a constructor which accepts the Main class as an argument:

     public function __construct(Main $main)
     {
         $this->db = $main;
     }
    

Allow access to your connection object by making $main's $start attribute public, or by creating a getter function (e.g. getConnection()), then use than in your query:

$result = mysqli_query($this->db->getConnection(), $query);

There's many, many ways you can approach your scenario, it's down to you. The answers have addressed the initial problems, but I thought I might also offer a few implementation suggestions also.

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

5 Comments

That is really detailed. Im going to read this through now. It is a bit hard coming from JS OOP so I really appreciate this.
Objects can be NULL though so I could say == NULL?
Actually, although the documentation says mysqli_connect() will return an object, in reality it can throw a warning and return false. Try this: var_dump(mysqli_connect('', '', '', ''));
Good catch! I'll change my answer.
@Sean I think more secure would be start === false right? A triple equals
1

You are working with two different instances.

// This is one instance
$Main = new Main([...]);

// This is a different instance.
$test = new Test();

Extending a class does not mean that it gets the values from the existing instances of the class. It only means that it gets the same methods (and default properties).

Therefore, your class Test gets the same constructor as Main, meaning you need to send in the array to Test to be able to use instantiate it. In your example, there is no reason to instantiate Main directly at all.

Comments

0
$result = mysqli_query($Main -> start , $query);

to

$result = mysqli_query($this -> start , $query);

That removes the syntax error at least. $this is an introspective variable, it always refers to the current scope of instances.

6 Comments

But then it doesnt use the connection from the new object
new Test() is instance of Main by inheritence, so the instance of Test has access to the protected members of Main. You don't need to inject the instance of Main into the instance of Test if they are meant to be the same.
Is that what you mean? check my update. Maybe im mentally too stuck into JS prototypes
@Asperger Ye, basically. You shouldn't change your original question though, otherwise our answers won't make sense.
@Asperger If you're mind is stuck on the prototype paradigm, read about "lexical scoping".
|
0

Check out the comments below

//I suggest to make this class abstract to make sure
//php doesn't let you to instantiate it directly as you did before 

    abstract class Main {

        protected $config;
        protected $start;

            function __construct($arr) {
                if(!isset($this -> start)) {
                    if ($arr['secure']){
                       $this -> config = parse_ini_file('C:\xampp\htdocs\project\config.ini');
                        $this -> start = mysqli_connect($this -> config['host'], $this -> config['username'], 
                                                        $this -> config['password'], $this -> config['database']);
                    }
                    else {
                        $this -> start = mysqli_connect($arr['host'], $arr['username'], $arr['password'], $arr['database']);
                    }
                }
                if($this -> start == false) {
                    return mysqli_connect_error();
                }
            }
        }

        class Test extends Main {
            public function sample() {
                $query = "SELECT id, user,
                          FROM users";

//Here you should use $this instead of $Main as you can access protected 
//properties of the parent from the child class. 

                $result = mysqli_query($this -> start , $query);
                $row = mysqli_fetch_array($result);
                echo $row['user'];
            }
        }       

//Instantiate Test class instead of Main as it inherits Main anyway
//Your code also had a typo in the constructor argument.

       $x = new Test(array("host" => '', "username" => '', "password" => '', "database" => '', "errors" => false, "secure" => true));

       $x -> sample();

Please not that I didn't check the mysql part of your code - just an OOP structure

Comments

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.