frosty
frosty

Reputation: 2851

Call to a member function prepare() on null OOP

Having trouble understanding classes and inheritance:

core.php:

$servername = "****";
$database = "****";
$username = "****";
$password = "****";

try {
    $pdo = new PDO("mysql:host=$servername;dbname=$database", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}

class Database {

  protected $pdo;

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

class User extends Database {

  private $ip;
  private $sessionId;

  public function __construct($ip, $sessionId) {
    $this->ip = $ip;
    $this->sessionId = $sessionId;
  }
  public function getSessionInfo () {
    $stmt = $this->pdo->prepare(".."); <-- error here
    ....
  }
}

When calling:

require_once 'api/core.php';
$database = new Database($pdo);
$user = new User($_SERVER['REMOTE_ADDR'], $_SESSION['info']['id']);

Upvotes: 2

Views: 162

Answers (2)

Muhammed
Muhammed

Reputation: 1612

In this contest $database, and $user variables are not related to each other:

require_once 'api/core.php';
$database = new Database($pdo);
$user = new User($_SERVER['REMOTE_ADDR'], $_SESSION['info']['id']);

Thus, calling prepare() on $user won't work.

You need a mechanism, at least like this , although not a good practice to assign Database to a User:

$user->setDatabase($database);

Instead create a static Database object, initiate it before User initiation, and call it statically within User object, or any other object, make it available for all objects.

A quick fix would look like this, where User doesn't extend Database, because it's wrong. User is not a Database.

$database = new Database();
$user = new User();
$user->setDatabase($database); //sets $db variable inside User

//User.php
namespace MyApp;

class User{
    private Database $db;

    public function setDatabase($db){
        $this->db = $db;
    }

    public function doSomething(){
         $this->db->getPdo()->prepare('..');
    }
}

//Database.php
namespace MyApp;

class Database{

    private $pdo; //returns PDO object

    function __construct(){

         //create pdo connection
         $this->pdo = ..
    }

    function getPdo(){
         return $this->pdo;
    }

}

Database should be injected to objects or used by objects, you shouldn't be extending Database just to have it. If you want to do it properly, in an object-oriented way.

Remember PHP doesn't allow multiple inheritances by extend. Tomorrow, you might want to have a Person class that every User will extend, but since you did it wrong in the beginning, and wasting precious extend on Database, it won't be possible. And by not having a control of how many database instances you have created, you will run into issues. You need to know for sure that you have only a single connection object for one database, if of course the opposite is a must - which in your case I doubt.

Of course this will change if you have multiple database requirements, and more sophisticated app structure.

Upvotes: 3

FastTurtle
FastTurtle

Reputation: 1787

You are receiving this error because User Instance has pdo empty. try this code

$servername = "****";
$database = "****";
$username = "****";
$password = "****";

try {
    $pdo = new PDO("mysql:host=$servername;dbname=$database", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
    echo "Connection failed: " . $e->getMessage();
}

class Database {

  protected $pdo;

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

class User extends Database {

  private $ip;
  private $sessionId;

  public function __construct($pdo, $ip, $sessionId) {\
    parent::__construct($pdo)
    $this->ip = $ip;
    $this->sessionId = $sessionId;
  }
  public function getSessionInfo () {
    $stmt = $this->pdo->prepare("..");
    ....
  }
}

then

require_once 'api/core.php';
$user = new User($pdo, $_SERVER['REMOTE_ADDR'], $_SESSION['info']['id']);

hope it helps.

Upvotes: 0

Related Questions