This question already has answers here:
Call to a member function on a non-object [duplicate]
(8 answers)
Closed 9 years ago.
I have this code:
<?php
class guildData {
public $email = NULL;
public $hash_pw = NULL;
public $user_id = NULL;
public $clean_username = NULL;
public $display_username = NULL;
public function selectGuild($g_id)
{
global $db,$db_table_prefix;
$this->g_id = $g_id;
$sql = "SELECT
name
FROM
guild
WHERE
id = '".$g_id."'";
$result = $db->sql_query($sql);
$row = $db->sql_fetchrow($result);
return ($row['name']);
}
}
?>
<?php echo $guildData->selectGuild(1); ?>
I'm simply getting a 500 error and IDEone gave me this also:
Fatal error: Call to a member function selectGuild() on a non-object in /home/VT00Ds/prog.php on line 32
I can not see the error, can you help me?
You are doing it wrong.
Get rid of the global variables. Instead , if the class needs DB access , then you should inject it in the constructor:
class GuildData
{
// ... snip
protected $connection;
public function __construct( PDO $connection )
{
$this->connection = $connection;
}
// ... snip
}
Your code has potential for SQL injections. Instead of concatenating the query, you should use prepared statements:
$statement = $this->connection->prepare(
'SELECT name FROM guild WHERE id = :id'
);
$statement->bindParam( ':id', $this->g_id, PDO::PARAM_INT );
if ( $statement->execute() )
{
$data = $statement->fetch( PDO::FETCH_ASSOC );
}
You have to instantiate object, before you can use them:
$pdo = new PDO('mysql:host=localhost;dbname=myMagicalDB;charset=UTF-8',
'username', 'password');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$guild = new GuildData( $pdo );
$guild->selectGuild(42);
You might consider separating the part with deals with DB from the domain logic. Essentially letting some other class to handle the fetching and storing the data, while the Guild class manages the logic. You might find this and this answer relevant.
Do not use public variables. You are breaking the encapsulation of the object by directly exposing the internal data. Instead you should define them as protected or private.
You might also take a hard look at what you are actually keeping there. Why does GuildData need $hash_pw or $clean_username ?
You haven't instantiated $guildData. If you want to use this method without instantiating an object, the method should be static.
class guildData {
public static function selectGuild($g_id) { ... }
}
Then you can call it from
echo guildData::selectGuild(1);
Otherwise, you need to instantiate an object
$guildData = new GuildData();
echo $guildData->selectGuild(1);
Also, you should have some sort of __construct() method in there, in order to set up your member variables.
UPDATE I also noticed an error in your selectGuild() method:
$this->g_id = $g_id;
Sets the value of g_id, which is not defined as a member variable in the class. You must declare g_id as a member variable in your class definition:
class guildData {
public $email = NULL;
public $hash_pw = NULL;
public $user_id = NULL;
public $clean_username = NULL;
public $display_username = NULL;
public $g_id = NULL;
.
.
.
}
Finally, sql_query() is not a PHP method that I've ever heard of. Unless you're using a library that defines these methods, I think you mean mysql_query(). If this is the case, you should stop using mysql_* functions. They're being deprecated. Instead use PDO (supported as of PHP 5.1) or mysqli (supported as of PHP 4.1). If you're not sure which one to use, read this SO article.
Related
This question already has answers here:
Reference: What is variable scope, which variables are accessible from where and what are "undefined variable" errors?
(3 answers)
Closed 6 years ago.
I've been working on a databaseHandler php class, which should connect to the database and then be usable in other PHP files, atleast this was the plan. I've come across the problem that it cannot use any PDO related functions in my PHP class, i've tried checking if it was null, or not set at all (but it was) and i've also tried using a dummy function that just echos something which was to test if the class isn't undefined in others.
<?php
class databaseHandler {
private $db;
private static $instance;
function __construct() {
$this->buildDatabaseConnection();
}
public static function getInstance() {
if(!self::$instance) {
self::$instance = new databaseHandler();
}
return self::$instance;
}
private function buildDatabaseConnection() {
require 'dbconfig.php';
try {
$this->db = new PDO("mysql:host=" . HOST . ";dbname=" . DATABASE . ";charset=utf8", USER, PASSWORD);
if(isset($this->db)) {
if(!is_null($this->db)) {
echo "isn't null";
}
}
} catch(PDOException $e) {
$e->getMessage();
}
}
public function getConnection() {
return $this->$db;
}
public function getSomeShit() {
echo "some shit";
}
}
?>
The problem might be with the getConnection() php function, does it actually return the PDO object here? Because the main problem lays with the fact that i get this error when i use my getConnection function in other classes:
Notice: Undefined variable: database in F:\Websites\DevProject\php\functions.php on line 69
Fatal error: Uncaught Error: Call to a member function prepare() on null in F:\Websites\DevProject\php\functions.php:69 Stack trace: #0 F:\Websites\DevProject\php\functions.php(51): register('qwe', 'sdasd', 'asdasd', 'asdas', '01-01-1970') #1 F:\Websites\DevProject\register.php(123): datelessRegister('qwe', 'sdasd', 'asdasd', 'asdas') #2 {main} thrown in F:\Websites\DevProject\php\functions.php on line 69
and line 69 would be this:
$stmnt = $database->prepare("SELECT $username, $email FROM " . USERTABLE);
Where the $database variable is the class instance:
$database = databaseHandler::getInstance();
If i try to var_dump it in my registration php file, it'll tell me this:
object(databaseHandler)#1 (1) { ["db":"databaseHandler":private]=> object(PDO)#2 (0) { } }
However, when i use it inside of my function, it will say it is 'NULL', why is it null in a function but defined globally?
All the errors relate to the object, and in there it tells me the pdo is undefined (i've tried checking if it was null, but it wasn't!)
Any help would be great, thanks in advance.
In the return statement of getConnection method you should write the class variable without $:
return $this->db;
Also, you must do the queries on the PDO connection instead of your database handler class, so $database = databaseHandler::getInstance(); should be
$database = databaseHandler::getInstance()->getConnection();
or you can simply use the __call magic method to keep your code as it is right now:
public function __call($method, $args)
{
return call_user_func_array(array($this->db, $method), $args);
}
Also, to clarify how to use this class, you should call it inside every method you need the database to be used, not only once on the top of your file, because defining it there it will not be available inside your methods/functions unless you don't use the global statement to import the variable. However usage of the global variable is not encouraged and it is considered as a bad practice. An example on how to use the database would be:
public function doSomething()
{
$db = databaseHandler::getInstance()->getConnection();
$db->prepare(....);
}
public function doSomethingElse()
{
$db = databaseHandler::getInstance()->getConnection();
$db->prepare(....);
}
If you know there are many methods inside your class that uses the database then you can define it as a class variable and set the db instance in the constructor.
You seem to be having issues with scopes.
$database = databaseHandler::getInstance();
function doSomething()
{
// $database is not defined in this scope
$database->getConnection()->prepare(/* ... */);
}
You have to declare it as part of the global scope
function doSomething()
{
global $database;
$database->getConnection()->prepare(/* ... */);
}
The problem seems to be in the getConnection() function. Since $db is field you must do.
return $this->db;
However this is a very complex class structure for a simple problem.
You can directly extend your class from PDO
class DatabaseHandler extends PDO {
function __construct() {
parent::__construct('mysql:host=host_name;dbname=db_name', 'user_name', 'password');
parent::setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
}
And create an object of this whenever you want to do database transaction.
$db = new DatabaseHandler();
$stmt = $db->prepare(".....");
$stmt->execute();
I am currently trying to get a PDO SELECT statement to work with a variable passed in through the parameter of the function it is contained within. I am using this function to return values to the class constructor.
Here is the function for which i am encountering a "Fatal error: Call to a member function prepare() on a non-object"
public function select($field) {
global $pdo;
$query = $pdo->prepare("SELECT `".$field."` FROM `iwp_incident` WHERE `incident_id=`". $this->oid ."`");
$query->execute();
$result = $query->fetch();
return $result[0];
Your main problem is that the assumed PDO instance is not what you think it is (my guess is null).
To alleviate this problem, you should remove the reliance on globals and instead use dependency injection. For example
namespace Some\Namespace;
use \PDO;
class YourClass {
/**
* #var PDO
*/
private $pdo;
private $oid;
public function __construct(PDO $pdo, $oid) {
$this->pdo = $pdo;
$this->oid = $oid;
$someField = $this->select('some_field');
}
public function select($field) {
$stmt = $this->pdo->prepare(sprintf(
'SELECT `%s` FROM `iwp_incident` WHERE `incident_id = ? LIMIT 1',
$field));
$stmt->bindParam(1, $this->oid);
$stmt->execute();
return $stmt->fetchColumn();
}
}
To use this, you simply pass in your PDO instance when creating the class, eg
$pdo = new PDO('mysql:...', 'username', 'password', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]);
$obj = new \Some\Namespace\YourClass($pdo, 123);
The error message is quite helpful. $pdo is a non-object, as stated. presumably your initial connection failed. It might be good practice to check that $pdo is valid, though the initial connection (wherever that is in your code) should throw an exception if the connection fails. (Did you catch that exception and swallow it without reporting anything...?)
As an aside, may I suggest that instead of string concatenation you do something like this which handles quoting and escaping to help prevent mistakes that allow injection attacks:
$query = $pdo->prepare("SELECT `".$field."` FROM `iwp_incident` WHERE `incident_id`= :oid");
$params = array(":oid"=>$this->oid);
$success = $query->execute($params);
if($success) {
$results = $query->fetchAll(PDO::FETCH_ASSOC);
// process results
}
I'm trying to add a static member to each of my classes that contains the default database connection that they should use when instantiated. Here's how I'm trying to do it:
<?php //other classes extend Generic
class Generic {
public static $defaultDatabase;
public $db;
function __construct (&$pDatabase = null){
if ($pDatabase!==null)
$this->db = &$pDatabase;
else
$this->db = &$defaultDatabase;
}
}
?>
<?php
include_once("/classes/class.Database.php");
$db = new Database ("localhost", "username", "password", "TestDatabase");
$classes = array("Generic", "Member");
foreach ($classes as $class){
include_once("/classes/class.$class.php");
$class::defaultDatabase = &$db;//throws error here, unexpected "="
}
?>
What am I doing wrong? Is there a better way to do this, or do I have to set the defaultDatabase for each class individually? I'm using php 5.3, which I understand should support something like this.
In this code
$class::defaultDatabase = &$db
You should add $ before defaultDatabase, since static properties are accessed via
ClassName::$staticProperty
Unlike the others which are accessed via
$class->property;
Use self::$propertyName to access static properties:
function __construct (&$pDatabase = null){
if ($pDatabase!==null)
$this->db = &$pDatabase;
else
$this->db = self::$defaultDatabase;
}
Also note, that using the reference operator &$var is meaningless if $var is an object. This is because all objects in PHP are actually references.
I have multiple classes set up and they all need to access the database, which they do. The trouble comes when I want to use a function from one class inside another.
class General
{
private $_db = NULL;
private $_db_one;
private $_db_two;
private $offset;
public function __construct ( PDO $db ) {
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->offset = 10800;
}
public function getTableNames() {
$sql = 'SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = "BASE TABLE" AND TABLE_SCHEMA="' . $this->_db_two . '"';
$statement = $this->_db->query($sql);
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
This works fine and then my other class connects the same way. As you will see in my "Distributors" class below, I instantiate my "General" class in the constructor. As I am learning while I write, I cant help but feel that there is a more versatile way or efficient way to connect.
class Distributors
{
private $_db = NULL;
private $_db_one;
private $_db_two;
private $_source_tbl;
public $lights;
public function __construct ( PDO $db ) {
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->_source_tbl = 'distributors';
// is this the best way to get functions from another class inside of this class? I have 10 classes I will need to repeat this for.
$this->lights = new General($db);
}
public function getInventorySources() {
// calling function from General class inside my distributor class
$tables = $this->lights->getTableNames();
// using result of General function inside of a function from Distributors class
$sql = 'SELECT * FROM `' . $tables . '` WHERE `exclude` = 0';
$statement = $this->_db->query($sql);
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
Singleton is just another form of global state, which is bad. You should always avoid it.
From your code example,
public function __construct ( PDO $db ) {
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->_source_tbl = 'distributors';
// is this the best way to get functions from another class inside of this class? I have 10 classes I will need to repeat this for.
$this->lights = new General($db);
}
When you do instantiate this way $this->lights = new General($db); you take General class from global scope. So that, mocking and unit-testing is almost impossible.
Instead you should, inject an instance of General just like as you do for PDO.
Like this:
public function __construct (PDO $db, General $general)
{
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->_source_tbl = 'distributors';
// is this the best way to get functions from another class inside of this class? I have 10 classes I will need to repeat this for.
$this->lights = $general;
}
And you would use it this way:
$pdo = new PDO(...);
$pdo->setAttribute(...);
$general = new General($pdo);
$distributors = new Distributors($pdo, $general);
is this the best way to get functions from another class inside of
this class? I have 10 classes I will need to repeat this for.
Yes, you should repeat that, not instantiation, but dependency injection. This makes your code more maintainable and does not introduce global state.
Apart from that, your General class seems obvious violation of the Single-Responsibility Principle.
You should use singleton for getting the DB in your classes,
or use some ORM.
about mysql class with singleton:
Establishing database connection in php using singleton class
I don't know what problems you run into but i think the function getTableNames whil return an object or an array so the result in $tables is not a string do a var_dump($tables); to see what is in $tables
Try to google your way out from there.
I am trying to convert code from mysql to mysqli.
The code uses a single mysql_connect in a file which is included by every other file.
mysql_connect returns a MySQL link identifier that is a superglobal so you can rely on having a database connection available in any of your own functions.
It looks like with mysqli_connect this is not the case, the object returned isn't global.
Does this mean I have to add : global $mysqli; at the top of every function, or is there an way of making it a superglobal?
Relying on the fact that PHP will use the last opened connection resource if you don't specify one, is probably not a very good idea.
What happens if your application changes and you need two connections, or the connection is not there?
So it seems you need to do some refactoring anyway.
Here's a solution similar to Karsten's that always returns the same mysqli object.
class DB {
private static $mysqli;
private function __construct(){} //no instantiation
static function cxn() {
if( !self::$mysqli ) {
self::$mysqli = new mysqli(...);
}
return self::$mysqli;
}
}
//use
DB::cxn()->prepare(....
I usually make a function:
$mysqli = new mysqli(...);
function prepare($query) {
global $mysqli;
$stmt = $msqyli->prepare($query);
if ($mysqli->error()) {
// do something
}
return $stmt;
}
function doStuff() {
$stmt = prepare("SELECT id, name, description FROM blah");
// and so on
}
and then call that. That being said, I've since abandoned mysqli as being too bug-ridden to be considered usable. Shame really.
A very simple way to do this would be with a fixed database class, just to hold the mysqli connection object:
class Database {
public static $connection;
}
Database::$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE);
Then you can access it in the normal ways:
$sql = 'SELECT * FROM table';
$result = Database::$connection->query($sql);
$result = mysqli_query(Database::$connection, $sql);
echo 'Server info ' . mysqli_get_server_info(Database::$connection);
To introduce some oop to you and solve your problem, you could use a class like this:
class MyDatabase
{
private static $_connection;
public static function connect()
{
$mysqli = new mysqli(...);
self::$_connection = $mysqli;
}
public static function getConnection()
{
return self::$_connection;
}
}
In your database-connection file you would load this class and execute MyDatabase::connect(); once.
To get the $mysqli-connection anywhere in your script, just call MyDatabase::getConnection();.