My database class works perfectly and I call it like this $db = new Database('user', 'pass', 'db', 'host');. I top of my script I am defining this database, but later in the script I am trying to use the connection inside a function, but it seems to that the $db is not global, so my function can't access it. I have the possibility to create a new database class connection inside every function in my script, but I really would like to access the $db as a global access point.
Here is some code:
require_once('database_class.php');
$db = new Database('user', 'pass', 'db', 'host');
// I can reach the $db here and make the $db->PDO->'statement'();
function userExists($user) {
$bool = false;
// But in here I can't access $db...
$query = $db->PDO->query('SELECT * FROM login WHERE username = "$user"');
$result = $query->fetch();
if ($result) {
// User exists
$bool = true;
}
return $bool;
}
Put global $db at the beginning of the function like so:
function userExists($user) {
global $db;
// Rest of code here
Variables within functions only exist locally by default in PHP. To use a variable declared outside of a function which is not passed as an argument (e.g $user) you need to use a global variable as shown above.
You could probably just modify your functions to take the $db var as an argument. e.g. :
function userExists($user, $db) {
...
}
Objects are passed by reference by default (see here) so you won't be inadvertently making copies of the $db object with each call.
I use classes for connections and queries too. But it would help that you define already in your class the variables needed for connection, so you don't have to repeat them in every page of code.
and maybe this? use the db as an argument.
function userExists($user, $db) { //codecodecode }
Related
I am learning PHP and trying to wrap my head around both PHP and OOP. I've created this function to allow me to select which database to connect to and then create an object. I want to then use that object outside of the function to run queries, but I'm not quite sure how to do this. Here's what I've got:
function connectToDB($database) {
switch($database) {
case 'DB1':
$host = DB1_HOST;
$user = DB1_USER;
$pw = DB1_PW;
$dbname = DB1_NAME;
$port = DB1_PORT;
break;
case 'DB2':
$host = DB2_HOST;
$user = DB2_USER;
$pw = DB2_PW;
$dbname = DB2_NAME;
$port = DB2_PORT;
break;
}
$db = new MySQLi;
$db->connect($host, $user, $pw, $dbname, $port);
return $db;
}
So what I'm trying to do is tell the function to either connect to 'DB1' or 'DB2', create a MySQLi object and make a database connection for that database, and then give me back the object ($db) so that I can do other things with it outside of the function, but I can't figure out how to get $db to exist outside of this function.
You could do this:
$dbObj = connectToDB('DB1');
$dbObj->someFunction();
The function ConnectToDB() returns an instance of MySQLi. You have to assign the returned instance to a variable. This can be done by doing:
$my_object = connectToDB('DB1') //in case of DB1
Then you can simply call the methods defined on the object in the following way:
$my_object->method_to_call();
In other words, you let a specific instance of a certain class (MySQLi) do some action, method_to_call() in this case.
There are several methods you could use. The one you are using should work as long as you are assigning the returned object to a variable. Another method would be to create the object somewhere else in a higher level class and then use the object variable in the connection method (this code probably doesn't work, it's just to illustrate the concept).
// declare database connection variable outside of functions in class
private $db;
...
function init() {
...
$this->db = new MySQLi; // or call this inside the connection function
}
function connectToDB($database) {
$this->db->connect($host, $user, $pw, $dbname, $port);
}
Another method would be to pass the object in by reference to the function:
$db = new MySQLi;
function connectToDB($database, &$db) {
// the & in front of the param indicates that it is passing in the original object by reference instead of making a copy in the local scope.
$db->connect($host, $user, $pw, $dbname, $port);
// since the object is passed in by reference, we don't need to return it
}
I'm trying to refactor some code but I'm kinda confused. I define my database connection like so:
try{
global $conn;
$conn = new PDO("mysql:host=$host",$root,$pw); [...]
Now I'd like a function for retrieving table rows but it needs $conn. Is there any way in which I can pass $conn into this function? I tried to set it as a default value but that doesn't work:
function get($table,$conn=$conn,$limit=10){ [...]
I then tried the use keyword but I think it's only available for anonymous functions:
function get($table,$limit=10)use($conn){
$query = $conn->query(" [...]
How do other people do this? Am I missing something obvious here?
function get($table, $limit=10)
As you already wrote in your question, this function header is incomplete. The function itself can not do what it needs to do without having $conn.
As this is a function in the global namespace, the most straight forward thing could be to use a global variable:
function conn_get($table, $limit=10) {
global $conn;
I also name-spaced the function to make the relation clear. The problem with this are two things:
global functions are expensive to maintain
global variables are expensive to maintain
So what you normally do in that case is to wrap this into a class:
class Conn
{
private $conn;
public function __construct(PDO $conn) {
$this->conn = $conn;
}
public function get($table, $limit=10) {
$query = $this->conn->query("[...]");
...
}
}
You then pass around a Conn object which can be used:
$pdo = new PDO("mysql:host=$host", $root, $pw);
$conn = new Conn($pdo);
And then:
$conn->get('ColorTable', 200);
The private variable takes over the role of the global variable with the benefit that every method inside the same object can access it. So everything now is in it's own space and contrary to the global space, will not go into each others way that fast. This is easy (easier) to change and maintain over time.
When you call the function i.e:
$table_rows = get($table, $conn);
You are passing local variables inside the function scope.
However you can't define a not-static variable as default: $conn=$conn will throw a fatal error.
In PHP, use is the way to go for anonymous / lambda-functions but not for ordinary functions.
If you have the database connection flying around in global scope, you can either pass it as a normal variable to your functions like so:
function get(PDO $conn, $table,$limit=10) {
$query = $conn->query(" [...]
}
Other than that (bad practice!) is to get the global $conn variable into the function like so:
function get($table,$limit=10) {
$query = $GLOBALS['conn']->query(" [...]
}
However, an object oriented approach is recommended! You might want to inject the Database Class via dependency injection into the classes, where you need it.
the most simple thing you can do is to create a function that will return you the $conn variable
function conn (){
$conn = NULL;
...some database connection setup etc...
return $conn;
}
and call it to other functions that you need to use it
function getDb(){
conn()->query(" [...]");
}
the conn() function will be available to all your functions on your PHP script.
but if you plan to make a more complex web application I recommend you to use a PHP framework or make a PHP class and apply OOP principles that would handle the database connection for you.
I am fairly new to object oriented programming. I made this class which connects to mysql database to be called from models. Is there any way i can include 'database.class.php'(my db class file)
in index.php, make it global and then access it from any object like so
$object = new object;
$object->dofunc();
Also another question is dofunc() expects an array for argument, how do i make this array also global so it can be accessed from ANY where!
Here is my db class
<?php
class Database {
private $db;
public function connect($config) {
if (is_array($config)) {
extract($config);
$db = mysqli_connect($host, $username, $password);
if ($db) {
echo "balbabla";
if (mysqli_select_db($db, $database)) {
}
else {
throw new exception("<br/><strong>Could not connect to $database under $host</strong>");
}
}
else {
throw new exception("<br/><strong>Could not connect to mySQL database! Please check your details</stromg>");
}
}
}
}
?>
Also this is the file that contains the array
<?php
//Configuration for the MVC Framework
$_SETTINGS = array();
//Routing settings!
//Default controller(This controller will be loaded if there is none mentioned in the URI)
$_SETTINGS['default_controller'] = 'User';
//Default method(This will be the default method run if no method is mentioned in the URI)
$_SETTINGS['default_method'] = 'Register';
//Database settings
$DB_SETTINGS['host'] = 'localhost';
$DB_SETTINGS['username'] = 'root';
$DB_SETTINGS['password'] = 'foobar';
$DB_SETTINGS['database'] = 'freelance';
?>
Thanks in advance
Is there any way i can include 'database.class.php'(my db class file) in index.php, make it global
You can, but you shouldn't.
Also another question is dofunc() expects an array for argument, how do i make this array also global so it can be accessed from ANY where!
Again you shouldn't.
Dependency injection is the way to go.
To access a global variable from within a function, use the global keyword. For example, to access $DB_SETTINGS from Database::connect(), you could do:
public function connect() {
global $DB_SETTINGS;
...
The array would then be accessible inside that function.
As for globally accessible classes, they are automatically just that. Defining a class makes it available anywhere.
I am confused on how to handle the logic of reusing the database object and configuration variables or constants that stands global for the application.
the way i have been doing till now is, i created a config.php file in Config directory and declare all the config elements for example my typical config.php file would look like this.
#Start Session
session_start();
#Start Output Buffering
ob_start();
#Set Default TimeZone
date_default_timezone_set('Asia/Kolkata');
#Define Time Constant
define('DATE', date("d-F-Y/H:ia"));
#Define Paths
define('CLASSES',$_SERVER['DOCUMENT_ROOT'].'/resources/library/classes/');
define('MODELS',$_SERVER['DOCUMENT_ROOT'].'/resources/library/models/');
define('LOGS',$_SERVER['DOCUMENT_ROOT'].'/resources/logs/');
#Define Connection Constant
define('HOST','localhost');
define('USERNAME','username');
define('PASSWORD','password');
define('DATABASE','dbname');
try
{
#Connection String.
$dbh = new PDO('mysql:host='.HOST.';dbname='.DATABASE,USERNAME,PASSWORD);
#Set Error Mode to ERRMODE_EXCEPTION.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
#Print Errors.
echo $e->getMessage();
#Log Errors into a file
file_put_contents(LOGS.'Connection-log.txt', DATE.PHP_EOL.$e->getMessage().PHP_EOL.PHP_EOL, FILE_APPEND);
}
#Autoload Classes.
function __autoload($class) {
require_once CLASSES.'class.'.strtolower($class).'.php';
}
and in index.php file i would include this file once and re-use it in every object.
my index.php typically consist of controllers like this.
if(isset($_GET['users'])) {
require_once(MODELS.'users/users.php');
} else if(isset($_GET['categories'])) {
require_once(MODELS.'categories/categories.php');
} else if(isset($_GET['search'])) {
require_once(MODELS.'search/search.php');
}
and in Models i would instantiate the object i want and use it for example in users/users.php i would instantiate it like this
$user = new User($dbh);
all is working fine but the problem is for each and every class i have to pass the database handle object through constructor and re-use it in the class which is kind of ugly for me. and this approach creates a problem for me if i want to use class which contains static methods and properties that holds the Application settings that is to be retrieved from database. my requirement is such that.
I want to create a static method using singleton pattern that will hold the database object that is to be used across the application without the need to pass the the $dbh object each and everytime through constructor for each and every class. and i am very much confused on how should i deal with this.
thank you
I have a similar design pattern where I connect and store the connection in a global.
You do not need to pass the database variable to every class as it is a global.
You can use it anywhere like this:
$GLOBALS['dbh'];
To hide this I have actually created a function named get_db_connection() which first checks if there is a valid connection in the $GLOBALS array and then returns it. Also if there is no valid connection there, it creates a new connection stores it in the $GLOBALS and returns it.
It has the added benifit that I do need to instantiate the connection manually anywhere, it is automatically created on the first call to get_db_connection and is available everywhere subsequently.
The get_db_connection function looks something like this:
function get_db_connection(){
if(isset($GLOBALS['db_conn']) && get_class($GLOBALS['db_conn']) == 'PDO'){
return $GLOBALS['db_conn'];
}else{
$conn = new PDO(/* parameters */);
$GLOBALS['db_conn'] = &$conn;
return $conn;
}
}
defence in favour of global
I consider this to be an excusable usage of globals for following reasons:
The variable $dbh is already a global as it is not inside a function
or a class.
You are not accessing the global directly anywhere in
your program, but only through a single function get_db_connection
so the problem that anyone can change the value of the global is not
here.
The only way around this is by using a Singleton, which may be
unnecessary for such a simple problem.
Of these I consider the 2nd reason to be most concrete.
here is what i came up with finally.
class DB {
protected static $_dbh;
const HOST = 'localhost';
const DATABASE = 'dbname';
const USERNAME = 'usname';
const PASSWORD = 'passwo';
private function __construct() { }
public static function get_db_connection() {
if(!isset(self::$_dbh)) {
self::$_dbh = new PDO('mysql:host='.self::HOST.';dbname='.self::DATABASE,self::USERNAME,self::PASSWORD);
self::$_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$_dbh;
}
}
Applying singleton pattern and with static method call i can easily access the database object without passing the database object through class constructor.
To call the database object within or outside the class scope all i have to do is call a single line of code.
DB::get_db_connection();
this sounds more feasible isn't it? :)
You could try something like this:
function cnn() {
static $pdo;
if(!isset($pdo)) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME, DB_USER, DB_PASS);
$pdo->setAttribute(PDO::ATTR_TIMEOUT, 30);
$pdo->setAttribute(PDO::ATTR_PERSISTENT, true);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
return $pdo;
} else {
return $pdo;
}
}
After that, you can call any queries you want:
echo cnn()->query('SELECT firstname FROM user WHERE id=4;')->fetch(PDO::FETCH_COLUMN)
Second query (object is reused)
echo cnn()->query('SELECT title FROM news WHERE id=516;')->fetch(PDO::FETCH_COLUMN)
I wrote this code
require('Database.class.php');
function get_info (){
$db = new Database($config['server'], $config['user'], $config['pass'], $config['database'], $config['tablePrefix']);
$db->connect();
$sql = $db->query('SELECT * FROM ja_cat');
while ($options = $db->fetch_array($sql)) {
$cat[].=" ".$options['title'];
}
$db->close();
return $cat;
then I get this Mysql error
Mysql Error : No database selected .
but when I put the require instruction inside the function it's work fine
My guess is Database.class.php creates some variables that are probably global in scope that it relies upon. If you require it inside the function and it works, that supports that theory. Is that your class? Can you change it? Can you post it?
Basically $config needs a global qualifier inside the function.
Make this the first line IN your get_info() function:
global $config;
Also, you may want to define $db outside of the function at the start of the code and then close the connection at the end, instead of having to re-connect multiple times.
You have to import the global variable $config into the scope of the function:
function get_info() {
global $config;
}