Call to a member function prepare() on null OOP - php

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']);

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.

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.

Related

How to call a local property using OOP?

I'm a beginner looking to learn more about PHP OOP, so there are things I don't know, in the ShowUsers() method, I would like to display all users in the database, but I'm not getting it, because I don't know how to call the connection property. If I've been using encapsulation, it would be easy, but I'm using the connection property as local, and I really don't know how to call this property, how can I call it without using encapsulation?
db.php
<?php
class DbConnect {
private $host = 'localhost';
private $dbname = 'database';
private $username = 'root';
private $password = '';
public function __construct() {
try {
$conn = new PDO("mysql:host=$this->host;dbname=$this->dbname", $this->username, $this->password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $exception) {
throw new Exception($exception->getMessage());
}
}
}
main.php
<?php
require_once 'db.php';
class Main extends DbConnect {
public function __construct() {
parent::__construct();
}
public function ShowUsers() {
$sql = "SELECT * FROM users";
$result = parent::__construct()->prepare($sql); //Problem here
$result->execute();
}
}
$object = new Main();
$object->ShowUsers();
Note: I don't want to use encapsulation to make it work, I want to learn how to call the variable without using encapsulation, if possible.
Based on the code above and the comments, I recommend that you declare $conn as protected in your DbConnect class:
<?php
// db.php
class DbConnect {
private $host = 'localhost';
private $dbname = 'database';
private $username = 'root';
private $password = '';
protected $conn;
public function __construct() {
try {
$this->conn = new PDO("mysql:host=$this->host;dbname=$this->dbname", $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $exception) {
throw new Exception($exception->getMessage());
}
}
}
Then in main.php, you can do:
<?php
// main.php
require_once 'db.php';
class Main extends DbConnect {
public function __construct() {
parent::__construct();
}
public function ShowUsers() {
$sql = "SELECT * FROM users";
$result = $this->conn->prepare($sql);
$result->execute();
}
}
$object = new Main();
$object->ShowUsers();
?>

How to create 2 PDO database connection instances PHP

I currently have a class which creates one database connection, however I would like to create another connection too. I have tried copying the class structure but just renaming the variables and functions however that doesn't work and it seems like it doesn't detect where my new PDO connection is as I get error Uncaught Error: Call to a member function prepare() on null in. What is the best approach to take in my case when creating 2 database connections?
config.php:
<?php
class Database
{
private $host = "localhost";
private $db_name = "database1";
private $username = "root";
private $password = "";
public $conn;
public function dbConnection()
{
$this->conn = null;
try
{
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $exception)
{
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
?>
class.user.php:
class USER
{
private $conn;
public function __construct()
{
$database = new Database();
$db = $database->dbConnection();
$this->conn = $db;
}
public function runQuery($sql)
{
$stmt = $this->conn->prepare($sql);
return $stmt;
}
}
You could create your User class and pass the connection to the constructor. This will give you flexibility to swap out the connection.
Regarding your database class, it seems to be a wrapper, to create a PDO connection. You could do away with it, or extend the class with different params.
Perhaps look at dependency injection, and containers. They might help you here.
<?php
class User
{
private $conn;
public function __construct(PDO $conn)
{
$this->conn = $conn;
}
public function runQuery($sql)
{
$stmt = $this->conn->prepare($sql);
return $stmt;
}
}

How to use database connection in other classes

PHP programming is pretty new to me - so I hope that I don't ask something stupid. However, I could not find any answer to my problem :-(
I have saved my database connection in a separate .php file. Let's say "connection.inc.php"
connection.inc.php looks as follows:
$host = "name.server.com";
$dbname = "foo";
$user = "bar";
$pass = "PaSW0rd";
try
{
$DBH = new PDO("mysql:host=$host;dbname=$dbname",$user,$pass,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
}
catch(PDOException $e)
{
...
}
In my index.php I would include the connection.inc.php with 'include_once "connection.inc.php"
Additionally, I have a class called house.class.php in which I have a method with an SQL query to my database. I call the method via 'getColor($id, $DBH)' from my index.php
Is there any way to use the method without always putting $DBH into the call?
Thanks for your help!
Let's say you have a class named House with a getColor() method, and in your index.php. You can use the keyword global inside your function/method, but I am strongly against this. Example:
public function getColor(int $id)
{
global $dbh;
//and use it as $dbh
$dbh->prepare(....
}
Or you can inject it in the constructor. Example of how your class will look like
class House
{
private $dbh;
function __construct($dbh)
{
$this->dbh = $dbh;
}
public function getColor($id)
{
//$this refers to current object context
$this->dbh->prepare.....
}
}
And when you instantiate the House object you will pass the $DBH into the constructor. Example for your index.php:
include_once "connection.inc.php";
$house = new House($DBH);
$house->getColor($id);
Although, I suggest that you checkout Domain Driven Design, Factory and Repository pattern, and ORM like Doctrine or Eloquent to get more understanding on how to separate your application/domain logic from the database.
You should get the $DBH from the class itself rather than in the index file. Then you can have that instance of the database connection available to use class wide with other methofs in that class as well without having to re-connect for every function or having to provide every function the connection as an argument.
An example of this would look something like this:
house.class.php
<?php
class House{
private $DBH;
function __construct(){
include_once "connection.inc.php";
$this->DBH = $DBH;
}
function getColor($id){
$this->DBH // <---- use it like this instead of your $DBH old variable.
}
}
?>
class Database
{
private $host = "localhost";
private $dbname = "codeignitor";
private $username = "root";
private $password = "";
public $conn;
public function getConnection()
{
$this->conn = null;
try {
$this->conn = new PDO ("mysql:host=" . $this->host . ";dbname=" . $this->dbname, $this->username, $this->password);
$this->conn->exec("set names utf8");
// echo "MySqlConnected";
} catch(PDOException $exception){
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}

PHP OOP and PDO

My first real foray into using PHP OOP and PDO extensively. I have finally gotten the script to work, but as you notice in order to do it I had to move the PDO connect into the login function - originally it was just in the __construct(). I do not want to have to open a new PDO connect and this is sloppy. How can I maintain the same connection throughout the whole class?
<?php
class user{
public $id;
public $name;
public $email;
private $password;
public function __construct() {
$DBH = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function login($email,$password,$type){
$DBH = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$password_hash = sha1($password);
try{
if ($type != "actives") {
throw new Exception("Type Handling Error");
}
$STH = $DBH->query("SELECT id, email, password FROM $type WHERE email='$email' AND password='$password_hash'");
$STH->setFetchMode(PDO::FETCH_ASSOC);
$row_count = $STH->rowCount();
$row = $STH->fetch();
if($row_count == 1){
session_start();
session_regenerate_id();
$_SESSION['id'] == $row[id];
return true;
}
else{
return false;
}
}
catch (Exception $e) {
echo $e->getMessage();
}
}
public function loggout(){
session_destroy();
setcookie(session_name(), session_id(), 1, '/');
}
Make the database handle a private member within the class:
class user
{
public $id;
public $name;
public $email;
private $password;
private $dbh;
public function __construct(PDO $dbh)
{
$this->dbh = $dbh;
}
public function login($email, $password, $type)
{
$dbh = $this->dbh;
...
}
Usage:
$pdo = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$user = new User($pdo);
Sooner or later, you may need the PDO connection object somewhere else in your code(outside your user object). Therefore,
I would suggest to use one class which will provide static method as below to get the PDO connection object everywhere you want.
class Database{
private static $datasource='mysql:host=HOST dbname=DB';
private static $username='USER';
private static $password='PASS';
private static $db;
//make the constructor private and empty so that no code will create an object of this class.
private function __construct(){}
//the main public function which will return the required PDO object
public static function getDB(){
if(!isset(self::$db)){
try{
self::$db=new PDO(self::$datasoure,self::$username,self::$password);
}
catch(PDOExceptin $e)
{
$error=$e->getMessage(); //variable $error can be used in the database_error.php file
//display database error file.
include('database_error.php');
exit();
}
}
return self::$db;
}
}
Then you can use the static method like below any time you want PDO connection
$conn=Database::getDB();
Use ceejayoz' proposal or add a global function which is responsible for establishing the database connection. The function is written as to connect to the database at most 1 time per HTTP request:
function getPDOConnection() {
// Thanks to the static-specifier, this variable will be initialized only once.
static $conn = new PDO("mysql:host=HOST;dbname=DB", "USER", "PASS");
return $conn;
}
This enables you to maintain the same connection in your whole application (but of course you don't have to use the function everywhere). Basically, this is a very simple application of the Singleton pattern. See the documentation of the static specifier/keyword and try to get familiar with the Singleton pattern.
Add your database connection as a parameter in the constructor, So you when you create a new instance from the user class it automatically runs across the instantiated object
The variable $DBH needs to be a member variable of your class.
Below private $password;, add private $DBH; and you're good to go with deleting the new-connection code from your login function.

How to combine multiple php/mysql database connections?

When building dynamic websites I use a php include to make my connections to the database. The included file is very basic:
mysql_connect($hostname = 'host', $username = 'user', $password = 'password');
mysql_select_db('database');
This works fine.
In some places I use an AJAX system to create drag-and-drop reordering of database records which updates the database at the same time, it is adapted from something I found on the internet. This uses its own connection code:
class SortableExample {
protected $conn;
protected $user = 'user';
protected $pass = 'password';
protected $dbname = 'database';
protected $host = 'host';
public function __construct() {
$this->conn = mysql_connect($this->host, $this->user, $this->pass);
mysql_select_db($this->dbname,$this->conn);
}
This also woks fine.
What it means, however, is that I have to add the username, password, host and database to two separate files. Sometimes the second one is forgotten and causes the website to fail.
My question is, how can I either combine both connection files into one, OR how can I get the second block of code to accept external variables so that I only have to enter the actual values in one place?
Your last question is easy.
db.config.php
$host = '';
$user = '';
$pass = '';
$db = '';
db.plain.php
include 'db.config.php';
$conn = mysql_connect($host, $user, $pass);
mysql_select_db($db,$conn);
db.class.php
include 'db.config.php';
class SortableExample
{
protected $conn;
public function __construct()
{
global $host, $user, $pass, $db;
$this->conn = mysql_connect($host, $user, $pass);
mysql_select_db($db,$this->conn);
}
}
You can simply put the database connection code in another file and included that everywhere you need it.
Create a single entry point for your database connection.
Use a Singleton with lazy instantiation for that:
class ConnectionProvider {
protected $conn;
protected $user = 'user';
protected $pass = 'password';
protected $dbname = 'database';
protected $host = 'host';
private static $__instance;
private function __construct() {
$this->conn = mysql_connect($this->host, $this->user, $this->pass);
mysql_select_db($this->dbname,$this->conn);
}
public static function getInstance() {
if ( self::$__instance == null) {
self::$__instance = new ConnectionProvider();
}
return self::$__instance;
}
public function getConnection() {
return $this->conn;
}
}
And then, from your code
ConnectionProvider::getInstance()->getConnection();
to use the connection wherever you need it.
SortableExample would thus become:
class SortableExample {
protected $conn;
public function __construct() {
$this->conn = ConnectionProvider::getInstance()->getConnection();
}
...
}

Categories