Issue with Classes, Instances and Objects - php

I'm fairly new to PHP; however I think I have a good grasp on it, but I'm having an issue understanding why I'm not able to access a second version of a connect to DB class I have.
I connect globally to the first one in a config script.
$db = new db(array('login info'));
I access it in my controller using the following.
public $db;
public function __construct(){
$this->db = $GLOBALS['db'];
}
Then inside my controller I have a function where I create a new instance of this to connect to a different DB.
$ordersDB = new db(array('login info'));
Now I would like to access each database separately using something like
$this->db->select("SELECT * FROM ada Yada
and
$ordersDB->select("SELECT * FROM Yada Yada
However both are still accessing the first db. I know this because I am getting a table does not exist error when executing the second one and it tells me what db its querying. However a var_export of them shows they are infact different! Am I completely misunderstanding how classes work or can I only have one DB open at a time? Thanks for any help.
Edit: Is this what you are looking for?
$db = new db(array(connect info));
$controller = new controller();
$controller->connectToSecondDB();
class db {
public $dbHost;
public $dbUser;
public $dbName;
public $dbPassword;
public function __construct() {
$arguments = func_get_args();
if(!empty($arguments)){
foreach($arguments[0] as $key => $property){
if(property_exists($this, $key)){
$this->{$key} = $property;
}
}
}
}
public function connect() {
if(!isset(self::$connection)) {
self::$connection = new mysqli($this->dbHost, $this->dbUser, $this->dbPassword, $this->dbName);
}
}
class controller {
public $db;
public function __construct(){
$this->db = $GLOBALS['db'];
}
public function connectToSecondDB(){
$ordersDB = new db(array(connect info));
$ordersDB->select("SELECT * FROM `customer` WHERE email = 'email#address.com' LIMIT 1")
$this->db->query("SQL");
}
}
EDIT Select Query Function
private function _sel($table, $where="", $order_by="", $limit="", $group_by="",$database = NULL){
if($database === NULL) { $database = $this->db; }
if($limit == 1){ $single = true; }else{ $single = false; }
//if( is_array($where) ){ $this->_buildWhere(); }
$where = (strlen($where) > 0) ? "WHERE $where " : $where;
$group_by = (strlen($group_by) > 0) ? "GROUP BY $group_by " : $group_by;
$order_by = (strlen($order_by) > 0) ? "ORDER BY $order_by " : $order_by;
$limit = (strlen($limit) > 0) ? "LIMIT $limit " : $limit ;
$sql = "SELECT *
FROM `$table`
$where
$group_by
$order_by
$limit";
// Debug
//if(INCLUDE_CHECK){ echo "<HR>".$sql."<HR>"; }
$results = $database->select($sql);
if($single && count($results) > 0 ){
return($results[0]);
}else{
return($results);
}
}

A solution is to reuse your $controller object. Add a query method. Then you can run the query separate from the connection method so the controller object can be reused
You need error checking each step along the way.
Note that I removed $this->db->query("SQL"); as I am not sure what exactly it did. The concept in the answer should get you started redesigning your class.
UPDATE
Based on comment, I changed the $db from a super global and passed it to the controller object.
$db = new db(array(connect info));
// try passing the $db object so it does not need to be a super global
$controller = new controller($db);
// You can keep your $controller object as a global - I use a session variable for this to reuse it between pages.
$controller->connectToSecondDB();
// run the query separate from the connection so the controller object can be reused
$result = $controller->ordersQuery("SELECT * FROM `customer` WHERE email = 'email#address.com' LIMIT 1")
class db {
public $dbHost;
public $dbUser;
public $dbName;
public $dbPassword;
public $connection;
public function __construct() {
$arguments = func_get_args();
if(!empty($arguments)){
foreach($arguments[0] as $key => $property){
if(property_exists($this, $key)){
$this->{$key} = $property;
}
}
}
}
public function connect() {
if(!isset($this->$connection)) {
$this->connection = new mysqli($this->dbHost, $this->dbUser, $this->dbPassword, $this->dbName);
}
}
class controller() {
public $db;
public $ordersDB; // ADD THIS
public function __construct($db2){
$this->db = $db2;
}
public function connectToSecondDB(connect info){
$ordersDB = new db(array(connect info));
}
public function ordersQuery($sql){
$this->ordersDB->query($sql)
// Fetch and return you result set here
}
}

Related

pass database connection to phpunit test

i am trying to test a function that executes a select statement, but im a bit confused about the way i should pass the connection to this function and how to assert the data.
this is the class with the function i want to test:
class AdProviders {
public $providers = null;
protected $db = null;
function __construct() {
}
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db;
}
return $this->db->getConnection();
}
function setDbh($db) {
$this->db = $db;
}
function getProviders() {
if ($this->providers == null){
$DBH = $this->getDbh();
$query = "select * from providers";
$STH = $DBH->query($query);
$STH->setFetchMode(PDO::FETCH_ASSOC);
while($provider = $STH->fetch()) {
$this->providers[$provider['id']] = $provider;
}
}
return $this->providers;
}
}
and this is my test:
require dirname(__FILE__).'/../../src/vendor/autoload.php';
class AdProvidersTest extends PHPUnit_Extensions_Database_TestCase
{
/**
* #return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', 'LWw6A$cXvvf');
return $this->createDefaultDBConnection($pdo, 'testdb');
}
/**
* #return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__).'/../dbTest/fixtures/providers.xml');
}
public function testgetProviders_compareResult()
{
$db = $this->getConnection();
$fixture = new AdProviders($db);
$res = $fixture->getProviders();
}
}
when i run the test function now i get the following error:
1) AdProvidersTest::testgetProviders_compareResult
Trying to get property of non-object
why am i getting it? and how can i get the data inside the providers.xml
using the getProviders? thx
You have to change the constructor of AdProviders class to that the PDO instance can be passed to it. Otherwise $fixture = new AdProviders($db); will not work.
Please change
function __construct() {
}
to
function __construct($db) {
$this->db = $db;
}
UPDATE:
I saw that you are not storing a PDO instance in your AdProviders::$db property. If your are passing a PDO instance to your __construct you should also change your method setDbh from
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db;
}
return $this->db->getConnection();
}
to
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db->getConnection();
}
return $this->db;
}

MySQL fetch_array returns false

i'm pretty new with PHP, so maybe my question is so dumb but anyways...
I have this code to check if exist some user and a email address
$username = $_POST['user'];
$email = $_POST['email'];
$isUsernameTaken = false;
$isEmailTaken = false;
// check for email availability
$result = DBConnection::isEmailTaken($email);
if(count($result) > 0) {
$isEmailTaken = true;
$response["error"] = 3;
}
$anotherResult = DBConnection::isUsernameTaken($username);
if(count($anotherResult) > 0) {
$isUsernameTaken = true;
$response["error"] = 2;
}
when i try it with a non existing data, lets say:
username = jfgsjhfsjfjhsfsf
email = hjfgsjhfjsgsdff#something.com
the isEmailTaken($email) function retuns '[]' and the if is false, thats correct!!!
but the isUsernameTaken function returns 'false' so the if is always true, thats incorrect:(
if i change the order of the code now the isUsernameTaken function returns '[]' and the other function return 'false'
Why is that?
PD. When i try it in MySQL console it works fine, so the error is in PHP
my db connection class is the provided by #Evan de la Cruz(Static version)
As others have said, accessing a static member as an instance member may be contributing to the issue. Also, constructing an object with the new keyword (ex: $foo = new Bar();) will never result in false. It will either succeed and return the constructed object, or it will fail with an error or exception.
I can't be sure that these are the only issues, but try using the following (corrected) code and let me know if any issues remain.
<?php
class DBConnection {
private static $db;
private static $host;
private static $user;
private static $password;
private static $connection;
function __construct() {
if (!isset(self::$connection))
{
self::$db = 'myDB';
self::$host = 'localhost';
self::$user = 'root';
self::$password = '1234';
self::connect();
}
}
/**
* Note: You don't really need the connect method at all, unless you are trying to get the connection
* from outside classes.
* You could instead just put this code in the constructor
*/
private static function connect() {
if(!isset(self::$connection)) {
self::$connection = new mysqli(self::$host, self::$user, self::$password, self::$db);
}
}
function isEmailTaken($email) {
$q = "CALL isEmailTaken('".$email."');";
$result = $this -> select($q);
return $result;
}
function isUsernameTaken($username) {
$q = "CALL isUsernameTaken('".$username."');";
$result = $this -> select($q);
return $result;
}
public function query($q) {
$result = self::$connection -> query($q);
return $result;
}
public function select($query) {
$rows = array();
$result = $this -> query($query);
if($result === false) {
return false;
}
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
array_push($rows, $row);
}
return $rows;
}
function tryToLogin($username, $pass) {
$q = "CALL getUserData('".$username."','".$pass."');";
$result = $this -> select($q);
return $result;
}
}
?>
Also, you might consider just making this whole class static. Unless you are going to ever have more than one instance of it with different settings. but, if you are always going to have just this single instance, then it is probably cleaner to make the entire class static. Like so:
<?php
class DBConnection {
private static $db;
private static $host;
private static $user;
private static $password;
private static $connection;
private static function connect($db,$host,$user,$password) {
if(!isset(self::$connection)) {
self::$db = $db;
self::$host = $host;
self::$user = $user;
self::$password = $password;
self::$connection = new mysqli(self::$host, self::$user, self::$password, self::$db);
}
}
public static function isEmailTaken($email) {
$q = "CALL isEmailTaken('".$email."');";
$result = self::select($q);
return $result;
}
public static function isUsernameTaken($username) {
$q = "CALL isUsernameTaken('".$username."');";
$result = self::select($q);
return $result;
}
public static function query($q) {
self::connect("myDV", "localhost", "root", "1234");
$result = self::$connection -> query($q);
return $result;
}
public static function select($query) {
$rows = array();
$result = self::query($query);
if($result === false) {
return false;
}
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
array_push($rows, $row);
}
return $rows;
}
public static function tryToLogin($username, $pass) {
$q = "CALL getUserData('".$username."','".$pass."');";
$result = self::select($q);
return $result;
}
}
?>
You would then call the methods like this:
$isEmailTaken = DBConnection::isEmailTaken("an email address");
$isUserTaken = DBConnection::isUsernameTaken("a user name");
And you will never instantate DBConnection. (In other words, never call new DBConnection())
Explaining the difference between static classes and instance classes is quite difficult, and often even experienced PHP programmers don't have a clue as to the difference. (Programmers in other OO languages tend to understand it better.)
But the idea is this:
An instance class can be "instantiated" unlimited times. And each time, a new instance of the class is created. This is call an object. Each instance of the class can have different properties and so each instance can act different.
A static class cannot be instantiated. There is never an instance. It functions the same way no matter what. It is kind of like having one single instance (the difference being that there is no constructor and members are accessed statically [i.e. using the "self" keyword instead of "this"])
There is a pattern called "singleton" which is actually a single instance. So it is important that you do not think of statics as a single instance, although they are very similar.
Consider the following cases:
Case 1:
You have a class called "Person" with the properties "Name" and "Gender".
For this you would use an instance class, because there can be many people and each person can have different properties:
$john = new Person("John Doe","Male");
$joe = new Person("Joe Smith","Male");
$tina = new Person("Tina Thomson","Feale");
Case 2:
You have a class call "Math" with the methods Add() and Subtract(). Adding and subtracting is ALWAYS the same. There is only one type of math. There is no need to have multiple instances. That is to say, Math is static.
$sum = Math::Add(1,5);
$difference = Math::Subtract(10,5);
Notice that we never construct the static Math class using new Math().
EDIT:
Here is the problem, I believe:
if($result === false) {
return false;
}
I'd have to see your stored procedures to explain why. But I'm fairly sure this is your problem.
Here is the fixed version:
<?php
class DBConnection {
private static $db;
private static $host;
private static $user;
private static $password;
private static $connection;
private static function connect($db,$host,$user,$password) {
if(!isset(self::$connection)) {
self::$db = $db;
self::$host = $host;
self::$user = $user;
self::$password = $password;
self::$connection = new mysqli(self::$host, self::$user, self::$password, self::$db);
}
}
public static function isEmailTaken($email) {
$q = "CALL isEmailTaken('".$email."');";
$result = self::select($q);
return $result;
}
public static function isUsernameTaken($username) {
$q = "CALL isUsernameTaken('".$username."');";
$result = self::select($q);
return $result;
}
public static function query($q) {
self::connect("myDV", "localhost", "root", "1234");
$result = self::$connection -> query($q);
return $result;
}
public static function select($query) {
$rows = array();
$result = self::query($query);
if ($result !== false)
{
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
array_push($rows, $row);
}
} else {
//uncomment this line to see what the underlying problem actually is.
//die("The error from mysql is: ". self::$connection->errno . ": " . self::$connection->error);
}
return $rows;
}
public static function tryToLogin($username, $pass) {
$q = "CALL getUserData('".$username."','".$pass."');";
$result = self::select($q);
return $result;
}
}
?>

Tiny ORM issue in PHP

I'm trying to build a tiny ORM class that models will extend so, if for example I call the method User::find(1) it will give me the 'User' model that haves the id of 1 in the database.
This is my attempt:
class ORM
{
private static $table, $database;
function __construct() {
self::getConnection();
}
private static function getConnection (){
require_once('Database.php');
error_log('Getting connection');
self::$database = Database::getConnection(DB_PROVIDER, DB_HOST, DB_USER, DB_PASSWORD, DB_DB);
}
public static function find($id) {
$obj = null;
self::getConnection();
$query = "SELECT * FROM ? WHERE id = ?";
$results = self::$database->execute($query,null,array(self::$table,$id));
print_r();
if ($results){
$obj = new self($results);
}
return $obj;
}
}
And then, the class User for example.
include('ORM.php');
include('../../config.php');
class User extends ORM
{
public $id, $name;
private static $table = 'user';
public function __construct($data){
parent::__construct();
if ($data && sizeof($data)) { $this->populateFromRow($data); }
}
public function populateFromRow($data){
$this->id = isset($data['id']) ? intval($data['id']) : null;
$this->name = isset($data['name']) ? $data['name'] : null;
}
}
print_r(User::find(1));
I put those includes and that print_r just for testing, it won't remain there after.
The issue is that it seems that the method find doesn't read the $table from the class and it doesn't read nothing. So the query isn't executed fine and returns nothing but an error.
What am I doing wrong?
Change self in your code to static. Note that it will only work in php >= 5.3. Read more about late static binding

Global Variable - database connection?

I am trying to connect to a database (MySQLi) just once, but am having problems doing so.
How do I make a connection global for the entire script? There are multiple files (index.php, /classes/config.class.php, /classes/admin.class.php, etc).
I've tried the following:
In: config.class.php
public static $config = array();
public static $sql;
function __construct() {
// database
db::$config['host'] = 'localhost';
db::$config['user'] = '_';
db::$config['pass'] = '_';
db::$config['db'] = '_';
// connect
db::$sql = new mysqli(db::$config['host'], db::$config['user'], db::$config['pass'], db::$config['db']);
}
Again, in config.class.php
public function contectToDatabase($sql){
$sql = new mysqli(db::$config['host'], db::$config['user'], db::$config['pass'], db::$config['db']);
$this->sql = $sql;
}
I use the class with the following code:
$config = new db();
I really am puzzled at how I'm to do this. Can anyone help?
--- Edit ---
This is my new config.class.php file:
public static $config = array();
public static $sql;
private static $db;
private $connection;
public function __construct() {
// database
db::$config['host'] = '_';
db::$config['user'] = '_';
db::$config['pass'] = '_';
db::$config['db'] = '_';
// connect
$this->connection = new mysqli(db::$config['host'], db::$config['user'], db::$config['pass'], db::$config['db']);
}
function __destruct() {
$this->connection->close();
}
public static function getConnection() {
if($db == null){
$db = new db();
}
return $db->connection;
}
And this is how I'm loading it:
require_once("classes/config.class.php");
$config = new db();
$sql = db::getConnection();
However, running a real_escape_string results in the following errors:
Warning: mysqli::real_escape_string() [mysqli.real-escape-string]: Couldn't fetch mysqli in /home/calico/_/_.com/_/index.php on line 20
Warning: mysqli::query() [mysqli.query]: Couldn't fetch mysqli in /home/calico/_/_.com/_/index.php on line 28
Personally, I use a singleton class. Something like this:
<?php
class Database {
private static $db;
private $connection;
private function __construct() {
$this->connection = new MySQLi(/* credentials */);
}
function __destruct() {
$this->connection->close();
}
public static function getConnection() {
if (self::$db == null) {
self::$db = new Database();
}
return self::$db->connection;
}
}
?>
Then just use $db = Database::getConnection(); wherever I need it.

Using a database class in my user class

In my project I have a database class that I use to handle all the MySQL stuff. It connects to a database, runs queries, catches errors and closes the connection.
Now I need to create a members area on my site, and I was going to build a users class that would handle registration, logging in, password/username changes/resets and logging out. In this users class I need to use MySQL for obvious reasons... which is what my database class was made for.
But I'm confused as to how I would use my database class in my users class. Would I want to create a new database object for my user class and then have it close whenever a method in that class is finished? Or do I somehow make a 'global' database class that can be used throughout my entire script (if this is the case I need help with that, no idea what to do there.)
Thanks for any feedback you can give me.
Simple, 3 step process.
1/ Create a database object.
2/ Give it to your user class constructor.
3/ Use it in the user methods.
Little example.
File Database.class.php :
<?php
class Database{
public function __construct(){
// Connects to database for example.
}
public function query($sqlQuery){
// Send a query to the database
}
[...]
}
In User.class.php :
<?php
class User{
private $_db;
public function __construct(Database $db){
$this->_db = $db;
}
public function deleteUser(){
$this->_db->query('DELETE FROM Users WHERE name = "Bobby"');
}
}
Now, in userManager.php for example :
<?php
$db = new Database();
$user = new User($db);
// Say bye to Bobby :
$user->deleteUser();
If you want the current trendy name of this old technique, google "Dependency Injection". The Singleton pattern in php will fade away soon.
As he said, put all your functions in the database class and use the database object to access those functions from your user class. This should be the best method in your case.
Eg:
global $database;
userclassvar = $database->doSomething();
What I like to do is make the database class with the Singleton pattern in mind. That way, if you already have a database object, it just retrieves it, otherwise creates a new one. For example:
Database.class.php
class Db
{
protected static $_link;
private function __construct()
{
// access your database here, establish link
}
public static function getLink()
{
if(self::_link === null) {
new Db();
}
return self::_link;
}
// etc.
}
User.class.php
class User
{
protected $_link; // This will be the database object
...
public function __construct()
{
$this->_link = Db::getLink();
}
}
And now you can use User's $_link property to do the database functions, like $this->_link->query(...). You don't necessarily have to put the Db::getLink() in the constructor if your class doesn't have to interact with the database that much.
Since you are using the database as an object, why not just add methods to the object that your "users class" can employ to take care of the things it needs to do. The users class can contain a pointer to the database class. The database class will protect your database, and assure that the users class is using it appropriately.
Here is a solution using PDO.
<?php
class Database {
private static $dbh;
public static function connect() {
$host = "mysql:dbname=YOUR_DB_NAME;host=YOUR_DB_SERVER";
$username = "YOUR_USERNAME";
$password = "YOUR_PASSWORD";
try {
self::$dbh = new PDO( $host, $username, $password );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch( PDOException $e ){
$error_message = $e->getMessage();
exit();
}
return self::$dbh;
}
}
class MYObject {
public static $dbh = null;
public function __construct(PDO $db = null) {
if($db === null){
$this->dbh = Database::connect();
} else {
$this->dbh = $db;
}
}
}
class User extends myObject {
public function __construct($id = null, PDO $db = null) {
if($db === null){
parent::__construct();
} else {
parent::__construct($db);
}
if($id !== null){
return $this->select($id);
}
}
public function select($id) {
$retVal =false;
try {
$stmt = $this->dbh->prepare("SELECT...");
$stmt->execute();
if( $stmt->rowCount()==1 ){
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
$retVal =json_encode($row);
}
} catch (PDOException $e ) {
$error_message = $e->getMessage();
exit();
}
return $retVal;
}
}
?>
I think the better aproach would be to create the database class that instatiate right away on its own on a database.php and then include it on user.php. then every time you create a function that needs a database, you globalise the database object.
Check this.
databse.php
<?php
require_once ('includes/config.php');
class MysqlDb{
public $connection;
private $last_query;
private $magic_quotes_active;
private $real_escape_string_exists;
public function __construct() {
$this->open_connection();
$this->magic_quotes_active = get_magic_quotes_gpc();
$this->real_escape_string_exists = function_exists( "mysql_real_escape_string" );
}
public function open_connection() {
$this->connection = mysql_connect(DBHOST,DBUSER,DBPASS);
if(!$this->connection){
die("Could not Connect ".mysql_error());
}else{
$db = mysql_select_db(DB, $this->connection);
}
}
public function close_connection(){
if(isset($this->connection)){
mysql_close($this->connection);
unset($this->connection);
}
}
public function query($sql){
$this->last_query = $sql;
$results = mysql_query($sql, $this->connection);
$this->comfirm_query($results);
return $results;
}
private function comfirm_query($results){
if(!$results){
$output = "Query Failed " .mysql_error()."<br />";
$output .= "Last Query: " . $this->last_query;
die($output);
}
}
public function escape_value($value){
if( $this->real_escape_string_exists ) {
if($this->magic_quotes_active ) { $value = stripslashes( $value ); }
$value = mysql_real_escape_string( $value );
} else {
if( !$this->magic_quotes_active ) { $value = addslashes( $value ); }
}
return $value;
}
public function fetch_array($results){
return mysql_fetch_array($results);
}
public function num_row($results){
return mysql_num_rows($results);
}
public function insert_id(){
return mysql_insert_id($this->connection);
}
public function affected_row(){
return mysql_affected_rows();
}
}
$database = new MysqlDb();
?>
here is the user.php
<?php
require_once ('includes/database.php');
class User {
public $id;
public $fName;
public $lName;
Public $userName;
public $password;
public $email;
public $acess;
public static function find_all(){
global $database;
return self::find_by_sql("SELECT * FROM users");
}
public static function find_by_id($id=0){
global $database;
$results_array = self::find_by_sql("SELECT * FROM users where id={$id}");
return !empty($results_array)? array_shift($results_array) : false;
}
public static function find_by_sql($sql){
global $database;
$results = $database -> query($sql);
$object_array = array();
while($row = $database -> fetch_array($results)){
$object_array[] = self::instantiate($row);
}
return $object_array;
}
public static function instantiate($row){
$user = new self;
foreach($row as $attribute => $value){
if($user -> has_attribute($attribute)){
$user -> $attribute = $value;
}
}
return $user;
}
private function has_attribute($attribute){
$object_vars = get_object_vars($this);
return array_key_exists($attribute, $object_vars);
}
}
?>

Categories