Declare a global object for multiple functions - php

So I have a list of functions that i'm using in a web app. Most of the functions however, make calls to a database class. Here are two functions for example:
function add_post($userid,$body,$cat_id,$user_link){ //This function inserts new posts into the db
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
$now = date("Y-m-d H:i:s");
$insertData = array(
'user_id' => $userid,
'body' => $body,
'stamp' => $now,
'cat_id' => $cat_id,
'link' => $user_link
);
$db->insert('posts', $insertData);
}
function grab_username($userid){ //This function takes a user id, and returns the associated user_name
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
$params = array($userid);
$results = $db->rawQuery("SELECT username FROM users WHERE id = ?", $params);
//print_r($results);
foreach($results as $arrays){
foreach($arrays as $name){
return $name;
}
}
}
the problem is that I am constantly writing the line:
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
Is there a way I can declare the variable globally, have something more like this:
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
function add_post($userid,$body,$cat_id,$user_link){ //This function inserts new posts into the db
$now = date("Y-m-d H:i:s");
$insertData = array(
'user_id' => $userid,
'body' => $body,
'stamp' => $now,
'cat_id' => $cat_id,
'link' => $user_link
);
global $db->insert('posts', $insertData);
}

There are multiple ways to handle this but the best implementation can vary depending on the structure and needs of your action application.
You mentioned a global variable which would work, but is typically considered poor structure:
someFunction(){
global $db;
$db->insert('posts', $insertData);
}
Another method, which is slightly cleaner but still not as well organized is to pass the $db connection to the functions:
function myFunction( $db, ... ){
$db->query( // etc );
}
myFunction( $db );
A better version of this would be to organize your related functions in a class, and then inject the database:
class Posts {
public function listPosts( $db, ... ){
$db->query( // etc );
}
public function getPost( $db, ... ){
$db->query( // etc );
}
}
$posts = new Posts();
$posts->listPosts();
However, you can also decide to make these static methods, you could pass $db to the class constructor and store it, there are many choices.
You could also make a central wrapper class of your own that returns the connection. There are a million implementations but it's hard to specifically recommend one without knowing more about your application structure, how your classes share resources, how the application bootstraps everything - if it does. Etc

Try This:
db.php
class DB
{
public $conn;
private static $instance;
private function __construct()
{
$server = 'localhost';
$user = 'root';
$pass = 'root';
$database = 'my_db';
$this->conn = mysqli_connect($server,$user,$pass,$database);
}
public function query($sql)
{
$result = mysqli_query($this->conn,$sql);
return $result;
}
public static function getInstance()
{
if (!isset(self::$instance))
{
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
}
Now in any page you can do:
require("db.php");
$dbh = DB::getInstance();
$sql = "your sql query ..";
$dbh->query($sql);

Related

Variable scope in OOP

I've got the classes in my structure:
require_once("classes/config.php");
require_once("classes/database.php");
require_once("classes/template.php");
require_once("classes/route.php");
require_once("classes/user.php");
$config = new config;
$route = new route();
$db = new database ($config['db_host'], $config['db_name'], $config['db_user'], $config['db_pasw']);
Class 'template' uses template files.
What is the right way to work with database or user class, for example, in the template class?
How it's working now:
global $user;
But it's not 'good coding practices', right?
So, in User class you can do something like this object-oriented way:
class User{
private $user;
public function getUser(){
return $this->user;
}
}
And to get user in your script,
$userObj = new User();
$user = $userObj->getUser();
Dependency injection.
Collect the data that you want to render in your template in a data set that will be injected into the template render function, like:
// This can be class or array, as you prefer
$viewData = array(
'user' => $user,
'something_else' => ...
);
$template = new Template('id');
$template->render($viewData);
Yes it's not very good.
You can specify the connection variables in a separate configuration class.
Then, connect it to the constructor where necessary and use it.
Thus, there will not be a single global variable in your project.
Here is an example. But you must know. It's a bicycle, just for example. But here there is no DI (Dependency injection):
Config
namespace Model;
class ConfigModel
{
public $db = 'name_db';
public $user = 'user_u';
public $pass = '123456';
public $protocol = 'https:';
public $brand = 'MyBrand.com';
public $salt = 'ougfhdsihgsugsdopgf';
public $path = __DIR__;
}
DB class
namespace Model;
class DB
{
protected $host = 'localhost';
protected $db = '';
protected $charset = 'utf8';
protected $user = '';
protected $pass = '';
public function __construct() {
$config = new ConfigModel();
$this->db = $config->db;
$this->user = $config->user;
$this->pass = $config->pass;
$dsn = "mysql:host=$this->host;dbname=$this->db;charset=$this->charset";
$opt = array(
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC
);
try {
$this->DBconnect = new \PDO($dsn, $this->user, $this->pass, $opt);
}
catch( PDOException $Exception ) {
echo $Exception->getMessage();
echo $Exception->getCode();
if(!empty($Exception->getCode())){
die('Error');
}
}
}
public function showAllUser() {
$db = $this->DBconnect->prepare('SELECT `mail` FROM `users`');
$db->execute();
return $db->fetchAll();
}
}
I don't understand, doyou want to render global the user information like as, for example, the name of the user?I think the correct way to use the user details or the login details is using $_SESSION and after with $_SESSION['user'] you can render the user details in template.

Mysqli query error outside class

I made a db connection class like this :
class db_connector{
const host = "localhost";
const user = "root";
const password = "";
const dbname = "test";
public $db;
public function __construct()
{
$database = $this::dbname;
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
}
}
When i create an object for the class it works fine enough, though then i want to make a query within the class i get the following error:
Fatal error: Call to undefined method db_connector::query()
The object is as follows (outside the class):
$con = new db_connector;
$con->query("SELECT * FROM test");
The connection gets established, just the query gives the error. I thought the object would inherit the mysqli methods since I returned it. Can anybody help me fix this one? Im fairly new in OOP so maybe my logic is not the best.
How about a little magic:
class db_connector{
protected $connectionData = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'dbname' => 'test',
);
protected $db;
public function __construct($connectionData=array())
{
$cd = array_merge($this->connectionData, $connectionData);
$this->db = new mysqli($cd['host'], $cd['user'], $cd['password'], $cd['dbname']);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}
}
public function foo()
{
return 'I am foo, i exist so i will be called even if mysqli::foo would exist, because i am defined in this class!';
}
public function __get($property)
{
if(property_exists($this->db, $property)){
return $this->db->$property;
}
}
public function __call($name, $args)
{
if(method_exists($this->db, $name))
return call_user_func_array(array($this->db, $name), $args);
}
}
And call it like:
$db = new db_connector();
$result = $db->query('SELECT foo FROM bar LIMIT 1');
// this should also work:
echo $result->num_rows;
The class constructor won't return the mysqli object as you expect.
$con = new db_connector;
//$con is a db_connector object not a mysqli object
$con->query("SELECT * FROM test");
Try:
$con = new db_connector;
$con->db->query("SELECT * FROM test");
instead. $con->db is a public field which holds the mysqli object.
Edit
Also change:
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
To:
$this->db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}

mdb2 prepare statement with multiple types

I'm using PHP 5.1.6 and MDB2 and trying to wrap my prepare/execute/fetchAll into a class so I can execute a select query with one line. The following code shows the Class that I've created and also doing the same query directly:
<?php
include_once "MDB2.php";
class db {
private static $dsn = array(
'phptype' => "mysqli",
'username' => "username",
'password' => "pass",
'hostspec' => "localhost",
'database' => "dbname"
);
private static $instance = NULL;
private static $statement = NULL;
private static $resultset = NULL;
private function __construct() {}
private function __clone() {}
public static function getInstance() {
if (!self::$instance) {
self::$instance =& MDB2::factory(self::$dsn);
}
return self::$instance;
}
public static function execQuery($sql, $types, $values) {
if (self::$instance === NULL) {
self::getInstance();
}
self::$statement = self::$instance->prepare(
$sql, $types, MDB2_PREPARE_RESULT);
self::$resultset = self::$statement->execute(array($values));
if (PEAR::isError(self::$resultset)) {
// (this is where it fails)
echo('Execute Failed: ' . self::$resultset->getMessage());
return false;
}
return self::$resultset->fetchAll(MDB2_FETCHMODE_ASSOC);
}
}
echo "<pre>";
$dsn = array(
'phptype' => "mysqli",
'username' => "username",
'password' => "pass",
'hostspec' => "localhost",
'database' => "dbname"
);
$sql = "select * from testtable where id = ? order by id LIMIT ?"
$t = array('text','integer');
$v = array('ABC',3);
// GOING DIRECT
$db =& MDB2::factory($dsn);
$stmt = $db->prepare($sql, $t, MDB2_PREPARE_RESULT);
$res = $stmt->execute($v);
$out = $res->fetchAll(MDB2_FETCHMODE_ASSOC);
print_r($out);
// GOING THROUGH CLASS
print_r( db::execQuery($sql, $t, $v) );
?>
The output of going direct works as expected, but the second attempt, going through the class fails with the PEAR error message "MDB2 Error: not found". I can't see what the difference is between these two approaches.
The Class works properly if I only pass a SQL statement with one ? replacement and don't use 'array()' to hold the types and values. If I change these, it works:
$sql = "select * from testtable where id = ? order by id"
$t = 'text';
$v = 'ABC';
I found the problem with my code. The line in the execQuery method that reads:
self::$resultset = self::$statement->execute(array($values));
should be:
self::$resultset = self::$statement->execute( $values );
OOP is new to me so I was convinced the problem was with the class and method.
(I'm not sure if I should be answering my own question, or just put it in the comments)

Fatal error: Call to a member function query() on a non-object in

I am using pdo and I have set the connection string in a config file such as
$db = new PDO("mysql:host=localhost;dbname=mydbname", 'root', 'pass');
I have this query in a class , in a method/function
$query = $db->query("select aUsername,aPassword,aOnline,aLastlogin from tbl_admins where aUsername = '$username'");
and when I run my site I get that error. is it possible that the $db object is not global?
Not a great way of doing this but you should be able to get it working by adding global to your method/function:
function get_user($username) {
global $db;
$query = $db->query("select aUsername,aPassword,aOnline,aLastlogin from tbl_admins where aUsername = '$username'");
...
}
Working with globals this way you need to be very careful that you don't overwrite the variable at any point.
If you declared/instatiated $db in the global scope (or any scope other than the function/method), and tried to use it in a function/method, it will not work. Read this.
If your PDO object failed to instantiate or was unset before the method call, you may also recieved this error. Try var_dump(is_object($db)); and/or var_dump($db); to check.
You need to do one of the following:
Instantiate the PDO object within the method (probably not practical or best option):
function foo () {
$db = new PDO( ... );
...
$query = $db->query( ... );
}
Instantiate the PDO object in the global scope and use the global keyword to import it into the method:
$db = new PDO( ... );
function foo () {
global $db;
$query = $db->query( ... );
}
Instantiate the PDO object in the global scope and use the superglobal $GLOBALS array to access it.
$db = new PDO( ... );
function foo () {
$query = $GLOBALS['db']->query( ... );
}
Instantiate the PDO object in the global scope and pass it as a parameter to your method.
$db = new PDO( ... );
function foo ($db) {
$query = $db->query( ... );
}
foo($db);
Instantiate the PDO object in the global scope and pass into your object as a property.
$db = new PDO( ... );
class foo {
public $db;
public function bar ($db) {
$query = $this->db->query( ... );
}
}
$foo = new foo;
$foo->db = $db;
$foo->bar($db);
I recommend to use a good pattern called Registry.
And a simple implementation in PHP:
abstract class Registry {
private static $_tools = array();
public static function set($name, $value) {
self::$_tools[$name] = $value;
}
public static function get($name) {
return (isset(self::$_tools[$name]) ? self::$_tools[$name] : null);
}
}
Usage:
$db = new PDO("mysql:host=localhost;dbname=mydbname", 'root', 'pass');
Registry::set('db', $db);
//In some other part of code
$query = Registry::get('db')->query("select aUsername,aPassword,aOnline,aLastlogin from tbl_admins where aUsername = '$username'");

MySQL database config in a separate class

Is it possible to keep all my database related configuration (hostnames, usernames, passwords, and databases) as well as the function to connect to and select the correct database in a separate class?
I tried something like this:
class Database
{
var $config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
function __construct() {
$this->connect();
}
function connect() {
$db = $this->config;
$conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
}
And then in another class I would use in the classes __construct function:
require_once('database.php');
var $db_conn = new Database();
But this doesnt save the connection, it ends up defaulting to the servers local db connection. Or do I have to do the database commands everytime before I execute some database commands?
I modified your class to work as you seem to be expecting it to:
<?php
class Database
{
var $conn = null;
var $config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
function __construct() {
$this->connect();
}
function connect() {
if (is_null($this->conn)) {
$db = $this->config;
$this->conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$this->conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
return $this->conn;
}
}
Usage:
$db = new Database();
$conn = $db->connect();
Note that you can call connect() as many times as you like and it will use the current connection, or create one if it doesn't exist. This is a good thing.
Also, note that each time you instantiate a Database object (using new) you will be creating a new connection to the database. I suggest you look into implementing your Database class as a Singleton or storing it in a Registry for global access.
You can also do it the dirty way and shove it in $GLOBALS.
Edit
I took the liberty of modifying your class to implement the Singleton pattern, and follow the PHP5 OOP conventions.
<?php
class Database
{
protected static $_instance = null;
protected $_conn = null;
protected $_config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
protected function __construct() {
}
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection() {
if (is_null($this->_conn)) {
$db = $this->_config;
$this->_conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$this->_conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
return $this->_conn;
}
public function query($query) {
$conn = $this->getConnection();
return mysql_query($query, $conn);
}
}
Usage:
$res = Database::getInstance()->query("SELECT * FROM foo;");
or
$db = Database::getInstance();
$db->query("UPDATE foo");
$db->query("DELETE FROM foo");
You can certainly keep your connection info in a separate file.
Just save your connection object - $conn in your connect() function - in a class variable. You'll then be able to reuse it across calls.
In your method connect() $conn is only a local variable that only exists in the scope of that method. As soon as the method returns there will be no (other) reference to the connection resource and it will be collected/disposed.
You'll need at least
$this->conn = mysql_connect(...)
Here comes the singleton with PDO example. Thanks to #hodobave
<?php
/**
* DB Connection class
* Singleton pattern
* An single instance of DB connection is created
**/
class Db{
protected static $_instance = null;
protected $_conn;
protected $_config = [
'username' => 'root',
'password' => '',
'hostname' => 'localhost',
'database' => 'news',
];
protected function __construct(){
}
public function getInstance(){
if(null === self::$_instance){
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection(){
if(is_null($this->_conn)){
//connect here
$db = $this->_config;
$dsn = "mysql:host={$db['hostname']};dbname={$db['database']}";
$this->_conn = new PDO($dsn, $db['username'], $db['password']);
}
return $this->_conn;
}
public function query($sql){
$args = func_get_args();
array_shift($args);
$statement = $this->getConnection()->prepare($sql);
$statement->execute($args);
return $statement->fetchAll(PDO::FETCH_OBJ);
}
}
$res = Db::getInstance();
$users = $res->query("SELECT * FROM `user` WHERE id=?",1);
print_r($users);
?>

Categories