how to solve oop error - php

I'm newbie here. I tried to solve this for a day and searching all the way but I still couldn't.
The error shown up
Notice: Undefined variable: db (in dbFunction)
Fatal error: Call to a member function query() on a non-object (in dbFunction)
and code is
dbConnect.php
class dbConnect{
public $db;
public function __construct(){
$db = new mysqli("localhost", "root", "", "onlineshopping");
$db->set_charset("charset");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
}
}
dbFunction
class dbFunction extends dbConnect {
function __construct() {
}
public $db;
public function UserRegister($fname, $lname, $tel, $email, $pass){
$pass = md5($pass);
$db->query("INSERT INTO member(fname, lname, tel, email, password) values('".$fname."','".$lname."','".$tel."','".$email."','".$pass."')") or die(mysqli_error());
return $db;
}
}

First of all, your $db is a local variable in the __construct() method and not a class property and second, as pointed by #wogsland it gets overwritten by the child.
You might want to review a bit the OO basics
dbConnect.php
class dbConnect{
public $db;
public function __construct(){
$this->db = new mysqli("localhost", "root", "", "onlineshopping");
$this->db->set_charset("charset");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
}
}
dbFunction
class dbFunction extends dbConnect {
public function UserRegister($fname, $lname, $tel, $email, $pass){
$pass = md5($pass);
$this->db->query("INSERT INTO member(fname, lname, tel, email, password) values('".$fname."','".$lname."','".$tel."','".$email."','".$pass."')") or die(mysqli_error());
}
}
Notice the replacement of $db with $this->db in both methods.
edit:
My post was an exact answer to your question but, as others have pointed in comments, there are quite a few things you need to improve and my hope was that this code is for your own personal play not something for 'production'.
So, directly related to the code example, two obvious things you might want to expand:
at least escape all your input to avoid sql injection
don't use MD5 for hashing passwords as it is very weak, check a more secure way to hash passwords

Your dbFunction class redefines __construct() to do nothing, so the $db property remains undefined. If you don't want to change the constructor in dbFunction, just omit it and the parent constructor will be used. If, on the other hand, you want to add to the functionality of the constructor then make sure to call the parent constructor:
function __construct() {
parent::__construct();
// additional dbFunction construction here
}

First of all, there's no need to make dbFunction a child class of dbConnect. They aren't related at all. In fact, I couldnt find any need to make dbConnect a class at all. All you need is the module __construct(). I would propose that you read up on OOP concepts :)
Still, answering your question - your __construct() function in dbConnect should return the db variable! You can assign this return value to your db variable in dbFunction.
Add the following line to your dbConnect's __construct() outside the if clause:
return $db;
And then instantiate an object of your class dbConnect in your dbFunction and assign db to that return value. (I'm assuming that dbFunction is not a child of dbConnect now...)
dbConn = new dbConnect()
db = dbConn.__construct()

First learn some basics of OO programming it seems like you should first read about objects and instances before using them in any fashion. Try this http://litove.com/object-oriented-programming-2/ you will find all your answers.

Related

Set $mysqli as a global variable for OOP

Ok,
This is sort of an involved problem, but any help or advice would be incredibly appreciated.
So I'm working with a site that (using .htaccess) redirects all traffic to a load.php. For any sql functionality, I have an abstract class that has a lot of query statements as functions that pass parameters to define the specifics of each query.
e.g.
$table->update("constraints")
I'm trying to figure out how to set the connection to the database on load.php, and then set the connection as a variable ($mysqli) that can then be referenced in my abstract query class without having to pass the parameter to every single query function call.
Again, any help or advice would be appreciated.
Here's an example of a function:
function clearTable (){
$mysqli = dbConnect::connect();
$sql = "TRUNCATE TABLE $this->tablename";
$mysqli->query($sql);
}
If I connect to the database in a construct function and set $this->mysqli and replace $mysqli = dbConnect::connect(); with $mysqli = $this->mysqli, none of the queries work. Though they work with a fresh reconnect on each call.
You should use Dependency Injection for this.
Basically it means that the class that needs the database connection doesn't create the connection, it just receives the already instasiated instance.
Example
In some init file:
// Create the db connection
$db = new Mysqli(......);
// Pass it to the query class that needs it
$queryClass = new QueryClass($db);
Then in your class file:
class QueryClass
{
protected $db;
public function __construct($db)
{
// $this->db will now be the same Mysql instance
$this->db = $db;
}
public function doSomeQuery()
{
$this->db->query(....);
}
}
A bonus for this is that you don't need to touch the QueryClass, if you ever want to start making some unit tests. You only need to pass a DB connection to a test database instead.
After looking through this a bit more, I can also create my db::connect() function to look like this:
class dbConnect {
private static $db;
private $mysqli;
private function __construct() {
$this->mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
}
function __destruct() {
$this->mysqli->close();
}
public static function connect() {
if (self::$db == null) {
self::$db = new dbConnect();
}
return self::$db->mysqli;
}
}
and pass that as $this->mysqli in the query functions file

Call to undefined method DB_Class::prepare()

I know this probably has been asked before, but the answers in other topics I found didn't seem to help for me :/. I'm not sure what I'm doing wrong when I follow the answers but its bugging me out.
The error: Call to undefined method DB_Class::prepare() on line 15. I've put a comment line to mark it.
The code is in Dutch, I hope that ain't a problem.
Reg_Functies.php (or the relevant part atleast):
<?php
require_once("Reg_Config.php");
class Gebruiker
{
private $db;
public function Gebruiker()
{
$this->db = new DB_Class();
}
public function check_login($email, $wachtwoord)
{
$wachtwoord = md5($wachtwoord);
$resultaat = $this->db->prepare("SELECT Log_ID, Log_Bevoegdheid from login WHERE Log_Email = ? and Log_Wachtwoord = ?"); //this line gives the error
$resultaat->bind_param("ss", $email, $wachtwoord);
$resultaat->execute();
Reg_Config.php:
<?php
define('DB_Server', 'localhost');
define('DB_Gebruikersnaam', 'root');
define('DB_Wachtwoord', 'password');
define('DB_Database', 'database');
class DB_Class
{
function __construct()
{
$connection = mysqli_connect(DB_Server, DB_Gebruikersnaam, DB_Wachtwoord, DB_Database);
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
}
}
?>
It's probably something very simple I'm looking over, but help is really appreciated.
Your code is trying to call DB_Class::prepare(), because $this->db has been assigned new DB_Class().
First, your constructor should keep a copy of the mysqli connection object, so add a field
private $connection;
to the DB_Class class, and update the constructor so that it calls
$this->connection = $connection;
at the end.
Then, you either need to define a prepare method that calls mysqli_prepare($this->connection) in DB_Class(), or call $this->db->connection->prepare(). If you choose the last option, you need to make it a public field, instead!
Another, much better option, is to avoid creating this redundant DB_Class and use PDO instead.

One connection for ALL ,OOP PHP

struggling to grip the varying levels of variables in OOP PHP. I want to have one connection file which can be accessed from all classes and functions through out my project. I include 'config.php' but the $mysql variable cannot be found. Any help greatly appreciated;
I have a file
config.php
public $mysqli = new mysqli('localhost', 'usernameEG', 'passwordEG', 'databaseEG');
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
and a few class files
class.User.php
<?php
class User {
private $mysqli = null;
function __construct(){
//=========
//include config.php here?
}
function myProfile($Id){
$stmt = $this->mysqli->prepare("SELECT First_Name, Last_Name, Email, DateJoined FROM members WHERE Id = ? ");
$stmt->bind_param('i',$Id);
$stmt->execute();
$stmt->bind_result($fname,$lname,$email,$date);
$stmt->store_result();
$stmt->fetch();
echo "<p>User#: ".$Id."</p>";
echo "<p>First Name: ".$fname."</p>";
echo "<p>Last Name: ".$lname."</p>";
echo "<p>Email: ".$email."</p>";
echo "<p>Date Joined: ".$date."</p>";
$stmt->close();
}
function example(){
EXAMPLE CODE HERE WILL ALSO NEED TO BE ABLE TO USE SAME CONNECTION
}
}
So rather than a singleton approach as hinted at in the other answer. Let me offer you a dependency injection approach. For a lot of experienced developers, dependency injection is a preferred approach, as it allows the class receiving the dependency to have looser coupling to the dependency (for example, you don't need to even know the name of the class for instantiating the dependency like you would with a singleton approach). You just need to know that the dependency being passed meets some interface requirement (i.e. it implements some interface or is a certain type of class).
So that would look more like this:
class User {
protected $mysqli = null;
// other properties
public function __construct(mysqli $mysqli) {
$this->mysqli = $mysqli;
// other constructor operations
}
// other methods
}
So what is happening here? Here you enforce the requirement to pass an object of type mysqli when instantiating this class. This could be a mysqli object or perhaps even your own custom class which extends mysqli, the User class doesn't care, so long as all mysqli class methods are implemented
Usage could be like
require('/path/to/db_config.php'); // an include which creates a mysqli object or provides a factory for one, or whatever
$user = new User($mysqli);
$user->foo(); // do something
Interestingly enough, you might at times see use of singelton pattern along with dependency injection. So say you had a mysqli_singleton_factory class with singleton functionality to provide you the single instantiated mysqli object. The usage might look like this:
require('/path/to/db_config.php'); // an include which provides a singleton
$user = new User(mysqli_singleton_factory::get_instance());
$user->foo(); // do something
$other_class = new other_class_requiring_mysqli(mysqli_singleton_factory::get_instance());
$other_class->bar(); // do something
Here you have both guaranteed that you only have one instantiated mysqli object during script execution AND you have decoupled your User class from having to do any instantiation of the object itself.
For reference, a singleton pattern may look like this:
class mysqli_singleton_factory {
protected static $mysqli = null;
protected function __construct() {
// assume DB connection setting have been pre-defined as constants somewhere
$mysqli = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
// make sure connection worked
if($mysqli->connect_error) {
throw new Exception(__METHOD__ . ' at line ' . __LINE__ . ' failed with: ' . $mysqli->connect_error);
}
self::mysqli = $mysqli;
}
public static function get_instance() {
if (false === self::mysqli instanceof mysqli) {
self::__construct();
}
return self::mysqli;
}
}
I've done this a number of times previously and I have found that it is easy to implement "singleton-like" class to serve as a database connector which then can be referenced by any object in the application.
config.php
Config.php is where the credentials are set and the database class is actually constructed.
$dbHost = 'localhost';
$dbUser = 'someUser';
$dbPass = 'somePass';
$dbSchema = 'someSchema';
Database::$db = new MySQLi($dbHost, $dbUser, $dbPass, $dbSchema);
classes.php
classes.php is where my classes are defined, which is kept separate from the rest of the code.
class Database {
public static $db;
}
class User {
...
function example()
{
$stmt = Database::$db->prepare('SELECT TRUE');
...
}
}
index.php
index.php is the entry point for the app, and is used to handle the request and exhibit the desired behavior using the supplied classes.
require_once('classes.php');
require_once('config.php');
/* Application Goes Here */
This ensures that all objects in my project use the same connection, I only have to invoke it once and I don't have to mess about with scope too much.

Use MySQLi object based within a class

require("classes/controller.class.php");
$db = new mysqli(__DBHOST, __DBUSER, __DBPASS, __DBDATA);
if ($db->connect_errno) {
printf("Connect failed: %s\n", $db->connect_error);
exit();
}
$controller = new Controller;
Within the Controller class I want to use the $db MySQLi connection. So within a public function I used:
$result = $db->query("SELECT * FROM colors");
var_dump($result);
But I get this error:
Fatal error: Call to a member function query() on a non-object in /home/cloud/public/teamdresser/controllers/IndexController.php on line 8
Am I doing something wrong? Do I need to pass the $db connect first, or use global or something?
Thanks in advance
If $db is a global variable, you'll need to import it in your function:
public function YourFunction()
{
global $db;
$result = $db->query("SELECT * FROM colors");
..
}
Instead of doing this, you might want to consider encapsulating $db in a database class and create it as a singleton, or, arguably better, pass it to the constructor of Controller. But that remark might lead to new discussions about design, while complete books have been written about this subject. So for now, just import the global like described above. ;)
You could use a class with a singleton instance
class Database {
private static $_instance;
public static function getInstance() {
//If the connection is not initialized, this creates it and store
if (empty(Database::$_instance))
Database::$_instance = new Database();
return Database::$_instance;
}
private $_db;
private function __construct() {
$this->_db = new mysqli(__DBHOST, __DBUSER, __DBPASS, __DBDATA);
if ($db->connect_errno) {
printf("Connect failed: %s\n", $db->connect_error);
exit();
}
}
private function __destruct() {
$this->_db->close();
}
public function query($query) {
return $this->_db->query($query);
}
}
You called in this manner
$result = Database::getInstance()->query("SELECT * FROM colors");
You have created a connection and executed with calling a function. Once the script is terminated, the connection will be autorealeased by the destructor.
This should be only a start point. You could create all functionality that you want (e.g. escape parameters, insert, update, delete, and so on...)

PHP Problem accessing the global variable

I'm having problem with global variable in PHP. I have mysqli config file which contains only following data:
$mysqli = new mysqli("localhost", "user", "pass", "db");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
I have the following class on another file:
class user{
function username_exists($username){
global $mysqli;
if ($stmt = $mysqli->prepare("SELECT username FROM users WHERE username=?")) {
$stmt->bind_param("s", $username);
$stmt->execute();
$stmt->store_result();
$count=$stmt->num_rows;
$stmt->close();
}
return ($count > 0 ? true : false);
}
...
some more functions
...
}
Now this works fine, but in my previous question on SO, i was told that it is a bad practice to access global variable like I'm doing in above class. So, I'm trying to pass the global variable in the constructor, in following way:
private $mysqli;
function __construct()
{
global $mysqli;
$this->mysqli = $mysqli;
}
function username_exists($username){
//global $mysqli;
if ($stmt = $this->mysqli->prepare("SELECT username FROM users WHERE username=?")) {
And I get the following error:
Fatal error: Call to a member function prepare() on a non-object in...(line number)
Can you please tell me whats problem with it and how this can be fixed? Thanks.
Edit: Sorry for the spelling mistake of __construct. It was only mistake typing here, and the error isnt because of that.
Well... having global in your constructor kindof beats the point. Consider passing it in as a parameter __construct($mysqli).
public function __construct($mysqli)
{
$this->mysqli = $mysqli;
}
What you're trying to do here is called dependency injection.
I think the problem is you misstyped __construct try changing your __cuntruct to the right name for the constructor.
The global in username_exists is also useless.
You should also write a constructor which takes the variable as argument and avoid using global completly :
class User {
var $mysqli;
function __construct($mysqli) {
$this->mysqli = $mysqli;
}
[ ... some functions ... ]
}
You must create your object like this :
$myuser = new User($mysqli);
$myUser->prepare();
Your constructor is not getting called because it is not the constructor at all
__cuntruct
should be
__construct
Couple of things. I think it would work OK if you changed __cuntruct to __construct.
You're still using the global declaration inside the username_exists function. Why not just pass the $mysqli variable in the constructor?
function _construct($mysqli) {
$this->mysqli = $mysqli;
}
then you have no globals in the class.
The code as written was not really what the others on SO were attempting to encourage you to do.
function __construct($mysql_handler){
$this->mysql = $mysql_handler;
}
This is passing in the parameter into the object scope at construction. When you create an instance of your object, you would pass in the MySQL handle.
$mysqli = new mysqli("localhost", "user", "pass", "db");
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$u = new User($mysqlli);
Then you should be able to call mysqli member functions on the property itself.
Your constructor is also misspelled. It will only work properly with the magic method name __construct().
Remove global $mysqli; in the function username_exists(), it does not makes sense.
The global $mysqli; is not required/does not makes sense since you want this variable to store the reference the connection IN THE CONTEXT of your object.
change __cuntruct() to __construct()

Categories