Call to a member function prepare() on a non-object

In a secured area I have a very simple class, holding some calculation functions, i.e.


    class cAantallen{		
		function dagAantal(){
			global $pdo;	
			$d   = "SELECT SUM(aantal)
                      FROM operator_bericht_aantallen
		             WHERE operator_id = :operator";
		    $dag = $pdo->prepare($d);
            $dag->bindParam(":operator", $operator);
		    $dag->execute();
			while($row = $dag->fetch()) {
				echo $row['SUM(aantal)'];
			}
			return true;			
		}
		
		function weekAantal(){
			global $pdo;
			$w    = "SELECT SUM(aantal)
                       FROM operator_bericht_aantallen
					  WHERE operator_id = :operator
					    AND WEEKDAY(datum) >= 0
						AND WEEKDAY(datum) <= 6
					   ";
            $week = $pdo->prepare($w);
			$week->bindParam(":operator", $operator);
			$week->execute();
			while($row = $week->fetch()) {
				echo $row['SUM(aantal)'];
			}
		    return true;			
		}
	}

which I call the following way:


include_once "classes/cAantallen.php";
$cAantallen = new cAantallen;

$cAantallen->dagAantal();

When running this on the page I get the error : Call to a member function prepare() on a non-object

I think it has to do with the global $pdo; which I have declared within my login function, i.e.


function dbconnect(){
   global $pdo;
    require_once "config.php";
    require_once "connect.php";	
}

But I am not sure so I don’t know what to do or change

Hi Donboe,

I suspect the problem lies somewhere in connect.php, as I can’t see anything wrong with the code you posted.

It’s considered bad practice to use global variables though, as they have a number of drawbacks - your code becomes dependent on global state (and these dependencies are not obvious unless you look at the code for the function/class) and can be easily broken (e.g. if one of your functions accidentally sets $pdo to a string, all the code that relies on that global variable will break, and the problem will be hard to debug).

A better solution is to pass in your $pdo object as a dependency to your class:


class cAantallen
{
    protected $pdo;

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

    public function dagAantal()
    {
        // ...

        $dag = $this->pdo->prepare($d);

        // ...
    }
}

// Initialising the class
$foo = new cAantallen($pdo);

First, don’t use global. If I ever teach a PHP class I will instruct the students that using global is an auto fail of their project, even if it runs correctly. Global variables in general, and the global statement in particular, leads to complex, unmaintainable spaghetti code. Stop now.

Pass your pdo object to this class and bind it in the constructor.


class cAantallen {
  protected $pdo;
  public function __construct( PDO $pdo ) {
    $this->pdo = $pdo
  }
}

Then your functions can refer to $this->pdo rather than rely on a global variable.

As for why its not working - check the connection string. PDO is a bit odd in that it’s constructor only returns a PDO object if the connection succeeds. If it fails for any reason an execption will be thrown and null is returned.

Hi fretburner and Michael. Thank you both for the reply and instructions.

In regards to connect.php. here is how it is in in combination with config.php:

config.php


    $dsn  = 'mysql:host=localhost;dbname=dbname';
    $user = 'root';
    $pass = '';	
    // other variables used in pages

connect.php


    try {
        $pdo = new PDO($dsn, $user, $pass);
    } catch (PDOException $e) {
        echo 'Er is een database fout opgetreden.';
        exit();
    }

In regards to the global $pdo:

I will try to pass in the $pdo object as a dependency to the class as you both suggest, and will let you know what the outcome is

Hi again. I just changed the class to this:


    class cAantallen{
        
        protected $pdo;
        public function __construct( PDO $pdo ){
            $this->pdo = $pdo;
        }
        
        function dagAantal(){
        
            $d = "SELECT SUM(aantal) 
                    FROM operator_bericht_aantallen
                   WHERE operator_id = :operator
                     AND WEEKDAY(datum) = NOW()";
            
            $dag = $this->pdo->prepare($d);
            $dag->bindParam(":operator", $operator);
            $dag->execute();
            while($row = $dag->fetch()) {
                echo $row['SUM(aantal)'];
            }
            return true;            
        }
        
        function weekAantal(){
    
            $w = "SELECT SUM(aantal)
                    FROM operator_bericht_aantallen
                   WHERE operator_id = :operator
                     AND WEEKDAY(datum) >= 0
                     AND WEEKDAY(datum) <= 6";
                       
            $week = $this->pdo->prepare($w);           
            $week->bindParam(":operator", $operator);
            $week->execute();
            while($row = $week->fetch()) {
                echo $row['SUM(aantal)'];
            }
            return true;            
        }
    }

however I get the following 2 errors:

Undefined variable: pdo in C:\wamp\www\Mysite\admin\index.php on line 9 = $foo = new cAantallen($pdo);

and

Catchable fatal error: Argument 1 passed to cAantallen::__construct() must be an instance of PDO, null given, called in C:\wamp\www\Mysite\admin\index.php on line 9 and defined in C:\wamp\www\Mysite\admin\classes\cAantallen.php on line 6 = $this->pdo = $pdo;

What is the reason for these errors

You must create a PDO object before trying to create an instance of your class with this setup.


$db = new \\PDO($connectionstring, $user, $password);
$foo = new cAantallen($db);

The catchable fatal error is because the constructor fretburner and I gave you has type hinting. This means the function will throw an error (which isn’t catchable despite the name, PHP is funny that way) if you try to pass an object that isn’t of the given class.

Hi Michael. Again thank you for the reply. I am realy lost now. The pdo object is created and is included at the top of the page. Truly sorry for my ignorance.

It’s only going to get created if that dbconnect() function gets called. Is it being called?

Hi Michael. This is really abracadabra for me. At the top of my index page I include the conection and validation file called: signin_function.php


function dbconnect(){
    global $pdo;
    require_once "config.php";
    require_once "connect.php";
}

function username_exists($username){
	global $pdo;
	
	$stmt = $pdo->prepare('
		SELECT operartor_id
		FROM operators
		WHERE username = :username
		LIMIT 1');

	$stmt->execute( array('username' => $username) );
	return $stmt->fetchColumn();
}

function attempt($username, $password){
	global $pdo;
	
    $stmt = $pdo->prepare('
        SELECT *
        FROM   operators
        WHERE  username = :username
        LIMIT 1');
		
	$stmt->execute(array(':username' => $username ));	

    if ($data = $stmt->fetch( PDO::FETCH_OBJ )) {
        if (password_verify($password, $data->password)) {
            $_SESSION['user_id']  = $data->operator_id;
            $_SESSION['user']     = $data->naam;
            $_SESSION['username'] = $data->username;
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

function is_user(){
	if (isset($_SESSION['username']))
		return true;
}

function redirect($url)
{
	header('Location: ' .$url);
	exit;
}

function valid_username($str){
	return preg_match('/^[a-z0-9_-]{3,16}$/', $str);
}

function valid_password($str){
	return preg_match('/^[a-z0-9_-]{6,18}$/', $str);
}

config.php and connect.php, included in signin_function.php are like this:


    $dsn  = 'mysql:host=localhost;dbname=database';
    $user = 'root';
    $pass = '';


    try {
        $pdo = new PDO($dsn, $user, $pass);
    } catch (PDOException $e) {
        echo 'Er is een database fout opgetreden.';
        exit();
    }

After signin_function.php I have this:


session_start();
include_once "classes/cAantallen.php";
$foo = new cAantallen($pdo);

and in the menu I have:


$foo->dagAantal();

Complete html is:


<?php
require_once('includes/signin_function.php');
session_start();
$operator = 1;
include_once "classes/cAantallen.php";
$foo = new cAantallen($pdo);
if (!is_user()) {
	redirect('login.php');
}
?>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<link rel="stylesheet" type="text/css" href="css/cssReset.css">
<link rel="stylesheet" type="text/css" href="css/cssAdmin.css">
</head>
<body>
<div id="wrapper">
<div id="header">
  <div id="logo">
      <img src="../images/logo.png"> 
  </div>
</div>
<div id="menu">
  <?php
      $foo->dagAantal();
  ?>  
</div>
</div>
</body>
</html>

So what am i missing or what should I adjust

Hi fretburner & Michael. I have it nearly working the way I hoped for. I followed up on Michaels suggestion and included a connection just before creating an instance of my class, ie


require_once('connect.php');
$cAantallen = new cAantallen($pdo);

$cAantallen->dagAantal();

Like I said I have it nearly working. The problem I have now is to access a SESSION variable user_id, which was set in my login file. i.e.


function attempt($username, $password){
	global $pdo;	
    $stmt = $pdo->prepare('
        SELECT *
        FROM   operators
        WHERE  username = :username
        LIMIT 1');
		
	$stmt->execute(array(':username' => $username ));	

    if ($data = $stmt->fetch( PDO::FETCH_OBJ )) {
        if (password_verify($password, $data->password)) {
            $_SESSION['user_id']  = $data->operator_id;
            $_SESSION['user']     = $data->naam;
            $_SESSION['username'] = $data->username;
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

How can I get $_SESSION[‘user_id’] ; to work in my class, since I need it in all functions within that class, i.e.

WHERE operator_id = :operator

Where : operator should represent $_SESSION[‘user_id’]

Hi try this idea…

config.php


<?php
$dsn= 'mysql:/host=localhost; dbname=mydb;';
$user = 'root';
$pass = '';


?>

function.php


<?php

function connectionDB()
{
    global $dsn, $user, $pass;
    return new PDO($dsn, $user, $pass);
}

function login($username,$password)
{
   try{
    $db = connectionDB();
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $sql = "SELECT * FROM user WHERE username=? AND password=?";
    $cmd = $db->prepare($sql);
    $cmd->execute(array($username,$password));

    if ($cmd->rowCount() == 1) {
        $row = $cmd->fetch(PDO::FETCH_OBJ);
        $_SESSION['user'] = $row->username;
        header("location:welcome.php");
    }else{
        return "invalid username and password";
    }
   }
   catch(PDOException $e){
       return $e->getMEssage();
   }
    $db = null;
}


?>

index.php


<?php
session_start();
include_once 'config.php';
include_once 'function.php';

  $login =login('admin','admin123');

   echo $login;


?>

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>



</body>
</html>


welcome.php

<?php
session_start();

include_once 'config.php';
include_once 'function.php';
$loginuser='';

  if(isset($_SESSION['user'])){
       $loginuser = $_SESSION['user'];

  }

?>

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
   <p>welcom.<?php echo $loginuser;?></p>
</body>
</html>