I have a database class, which is used to make select, update, delete MySQL queries.
Now, I want to create a MySQL query inside another class, but if I define $db = new DB(); in index.php, I can't use the $db var in another class. Do I have to define the variable $db over and over again, if I want to make a query? Or is there a way to make the $db var with an object global var?
The cleanest approach would be to aggregate the database class where needed by injecting it. All other approaches, like using the global keyword or using static methods, let alone a Singleton, is introducing tight coupling between your classes and the global scope which makes the application harder to test and maintain. Just do
// index.php
$db = new DBClass; // create your DB instance
$foo = new SomeClassUsingDb($db); // inject to using class
and
class SomeClassUsingDb
{
protected $db;
public function __construct($db)
{
$this->db = $db;
}
}
Use Constructor Injection if the dependency is required to create a valid state for the instance. If the dependency is optional or needs to be interchangeable at runtime, use Setter Injection, e.g.
class SomeClassUsingDb
{
protected $db;
public function setDb($db)
{
$this->db = $db;
}
}
You probably want a singleton. This gives you a way to get an instance of DB anywhere in the code. Then anywhere you want to do a query, first do $db = DB::getInstance();.
An alternative is dependency injection, which passes a DB instance to all classes which need one.
Use the magic function __autoload().
First make your database class a singleton. And then in your new class you can do something like:
class myNewClass{
private $_db;
public function __construct(){
$this->_db = DB::getInstance();
}
}
Define it on a class (separate PHP file). Then require it for every PHP file the var is needed in.
In your index.php file use
require_once('path_to_file_with_class.php');
You may also use include_once, which will give you a warning instead of an error if the 'path_to_file_with_class.php' file is not available.
You might define it as global in your index.php file, and in the class constructor also put $this->db &= $GLOBALS['db'];
Related
I had this error:
Notice: Undefined variable: mysqli in /path/to/component.php on line 24
This was inside this section of code:
class Component {
public function foo() {
// something using $mysqli;
}
}
To fix it, I have to add global $mysqli into my foo method - I understand that, but this isn't very practical for me, as I'd prefer to simply make $mysqli accessible within all classes by default.
Is this possible?
The short answer is: no. There isn't a way to change the way scope is handled in PHP.
Since that's probably not very helpful to you, I'll try to explain a better approach.
The global keyword is one of PHP's idiosyncrasies. Some people think it's dumb, but there's a certain logic to it. Lots of patterns in programming are about keeping dependencies explicit. Stuff that happens inside your class shouldn't depend on the state of the world outside. In your case (or whenever you import a global variable using global, you're coupling your class to the environment it lives in. This is bad for various reasons.
If your class requires a database connection, you should be injecting the dependency explicitly, not implicitly pulling it from global scope.
If the dependency is required for the class to function, the obvious place to inject it is in the constructor.
For example:
<?php
class MyComponent
{
/**
* #param mysqli $mysqli
*/
public function __construct($mysqli)
{
$this->mysqli = $mysqli;
}
public function getBars()
{
$bars = $this->mysqli->query('...');
return $bars;
}
}
// now back in the global scope
$mysqli = mysqli_connect('localhost', 'joe', 'secret', 'foodb');
$component = new MyComponent($mysqli);
$bars = $component->getBars();
For some project it is completely acceptable to have a single database and connection to it. Willing to access it from everywhere without using nasty global or injecting it all over the place make perfect sense.
One way I did is was simply to have my connection in a Static Object which is accessible from anywhere in my project. How I got this to work:
Use Composer to autoload my classes without having to worry about it. There are some details about how to do this here: Using Composer's Autoload
Create a class for the database connection.
class Database
{
public static $connection;
public static function connect($host, $user, $password, $database)
{
self:$connection = mysqli_connect($host, $user, $password, $database);
}
}
You can now access Database::$connection from anywhere as long as Composer includes your Database class. No need for globals...
Static classes can be quite useful for these type of usage. But some people don't like them because they are harder to test in some cases. There is no perfect solution, but do what make sense while trying to avoid bad patterns at all costs.
You may send it into constructor:
class Component {
protected $mysqli;
public function Component($mysqli = null) {
$this->mysqli = $mysqli;
}
public function foo() {
// something using $mysqli;
}
}
or set it by other methot of class after creating:
class Component {
protected $mysqli;
public function SetDBClass($mysqli)
{
$this->mysqli = $mysqli;
}
public function foo()
{
// something using $mysqli;
}
}
Use global keyword before declaration of your variable
Or put it into $GLOBALS array.
For more information about variable scope, take a look at this link: http://php.net/manual/en/language.variables.scope.php#language.variables.scope.global
Suppose the class Account (in account.php file) which uses a $DB variable to store data in the database. The $DB variable is initialized in another globals.php file.
/**** globals.php ****/
$DB = PDO (....);
/**** account.php ****/
public function create($data) {
global $DB;
....
}
Now, suppose that you want to test a function in account class called create using PHPUnit test. How would you initialize the $DB object?
Ideally, you should not use globals you are hiding dependencies and can cause things to happen due to changes in the global variable.
It would be possible for your to hack in and replace your variable so that you can mock it. You can use the setUp method to store what is in the global $DB and then restore it in the teardown() method. This way, you don't accidentally break some other test with your mock.
public function setUp() {
global $DB;
$this->oldDB = $DB;
}
public function testCreate() {
$mockDB = $this->getMock('PDO') ... //More steps to complete mock object
global $DB;
$DB = $mockDB;
//Rest of Test here
}
public function teardown() {
global $DB;
$DB = $this->oldDB;
}
Now just because you CAN do this, doesn't mean you SHOULD do this. It would be better to refactor your code so that it doesn't depend upon the global scope. But if that isn't an option at this time, here is a work around that will at least make the test usable until you refactor.
How would you initialize the $DB object?
Just initialize it as usual:
class TestCase extends PHPUnit_Framework_TestCase
{
function testDatabaseConnection()
{
require 'path/to/globals.php'; // take "global" variable in local scope
$GLOBALS['DB'] = $DB; // make it globally available
$account = new account();
$this->assertInstanceOf('account', $account);
$this->assertTrue($account->create("data"));
...
}
}
Then start with testing and get a feeling how the code behaves when you test it thoroughly.
You can then consider to initialize the database connection before the test-class is created once, so that you can write the test-routines faster.
Chapter 4. Fixtures
Later then you probably write yourself a helper method to instantiate the account class.
That perhaps is then also the time where you realize that injecting the database connection object into the constructor might not be that bad as well.
I have a database class, which is used to make select, update, delete MySQL queries.
Now, I want to create a MySQL query inside another class, but if I define $db = new DB(); in index.php, I can't use the $db var in another class. Do I have to define the variable $db over and over again, if I want to make a query? Or is there a way to make the $db var with an object global var?
The cleanest approach would be to aggregate the database class where needed by injecting it. All other approaches, like using the global keyword or using static methods, let alone a Singleton, is introducing tight coupling between your classes and the global scope which makes the application harder to test and maintain. Just do
// index.php
$db = new DBClass; // create your DB instance
$foo = new SomeClassUsingDb($db); // inject to using class
and
class SomeClassUsingDb
{
protected $db;
public function __construct($db)
{
$this->db = $db;
}
}
Use Constructor Injection if the dependency is required to create a valid state for the instance. If the dependency is optional or needs to be interchangeable at runtime, use Setter Injection, e.g.
class SomeClassUsingDb
{
protected $db;
public function setDb($db)
{
$this->db = $db;
}
}
You probably want a singleton. This gives you a way to get an instance of DB anywhere in the code. Then anywhere you want to do a query, first do $db = DB::getInstance();.
An alternative is dependency injection, which passes a DB instance to all classes which need one.
Use the magic function __autoload().
First make your database class a singleton. And then in your new class you can do something like:
class myNewClass{
private $_db;
public function __construct(){
$this->_db = DB::getInstance();
}
}
Define it on a class (separate PHP file). Then require it for every PHP file the var is needed in.
In your index.php file use
require_once('path_to_file_with_class.php');
You may also use include_once, which will give you a warning instead of an error if the 'path_to_file_with_class.php' file is not available.
You might define it as global in your index.php file, and in the class constructor also put $this->db &= $GLOBALS['db'];
I'm planning to use a PHP PDO wrapper class from here: http://www.imavex.com/php-pdo-wrapper-class/
I initialized the class with:
$db = new db("mysql:host=$sitedbHost;port=3306;dbname=$sitedbName", "$sitedbUser", "$sitedbPass");
The problem is, that I don't like the idea to make global $db on every function on my other classes like this:
class User
{
function getUserDomains($user_id)
{
global $db;
return $db->select("domains");
}
}
Any help will be highly appreciated.
If you class requires it in order to work, you can inject it in the constructor:
class User {
public function __construct(Database $db) {
$this->db = $db;
}
}
You can then access the database object from $this->db. (Note, I assumed $db is an instance of the Database class. Change it as appropriate).
Here is something for you:
Check new PDO Class Wrapper on GitHub
I don't like the idea to make global $db on every function on my other classes
Well, make it global in constructor only (or pass it as a parameter), and then use as a class variable,
return $this->db->select("domains");
Honestly, you can't avoid passing $db to the function somehow. There is no magic. Whatever fashionable way you choose, you'll end up with setting your $db variable this way or another, no matter how it's called, "dependence injection", "global" or "use the Force, Luke".
I'd vote for global as it's plain and simple.
By the way, this framework is inflexible and insecure. Better use safeMysql as it's way more secure and flexible
I have a class userdb in which I am declaring a function that returns the connection to the database:
return $con = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
I have various functions, even across other classes, where I have to access $con (e.g. to pass a query or to fetch data), but I can't access this variable.
Is there a better way to define and use a database class? Remember that I have other classes where I need to access the userdb class.
I would advise you to use the Singleton Pattern for this:
In your userdb class, declare a static property $scon:
private static $scon;
and assuming the function you mention above is named createConnection(), you should create the folowing static method:
public static function connect() {
if (empty(self::$scon)) {
$instance = new userdb();
self::$scon = $indtance->createConnection();
}
return self::$scon;
}
With this, you will be able to access your userdb connection with:
userdb::connect();
Also since this is a singleton, it will only connect once, and use that connection until the end of the script.
Note (on dependency injection): Since #KevinM1 mentioned Dependency Injection, I must add that it is also a possible, and far superior solution. It requires you to create a setConnection() method (or an Abstract ancestor) for all your classes using a database connection, and during the instatiation of these classes you may use a Factory to add the required connection to the object. This should be wrapped inside some class loader, which is avare of your model structure.
See, peace of cake, but for small and fast developement I would stick with the Singleton ;)
If it's in a class, store the instance in a property:
class userDB
{
public $dbCon = false;//because you want to access the instance
//from outside the class, I have made the property public
function connect()
{
$con = new PDO("mysql:host=$host;dbname=$db", $user, $pass);
$this->dbCon = $con;
}
}
TO access it outside of the class:
$useDBInstance->dbCon;
return $this->con
return this way from your class and call it this way..
$this->classObject->con->prepare();
Checkout my video tutorial + code for an alternative to global PHP variables.
You're doing it wrong there. You need to use a static variable if you plan to create your connection in a function and store it there. Otherwise you'll keep connecting on every function call. My tutorial explains this very concept of static variables within regular functions.
If it's not clear enough, let me know and I'll try to answer your questions.
Here's some code to accompany this:
/**
* Arguments are none or [$db, [$user, [$pass[, $host]]]].
*
* #return PDO
*/
function PdoDb(){
static $con = null; // I'm explicit :)
// Every time you pass Arguments you reconnect
if(func_num_args()){
$args = array_pad(func_get_args(), 4, null);
list($db, $user, $pass, $host) = $args;
if(empty($user)) $user = 'root';
if(empty($host)) $host = 'localhost';
$con = new PDO("mysql:host={$host};dbname={$db}", $user, $pass);
}
if(empty($con)){
trigger_error('Provide arguments to connect first.', E_USER_ERROR);
return null;
}
return $con;
}
// First run (provide arguments to connect)
PdoDb($db, $user, $pass, $host);
PdoDb($db); // Works if you connect root#localhost with no password
// From here on (it returns PDO)
PdoDb()->DoStuffOfPdo();
Once connected it stays that way. But you can reconnect at will to by providing arguments.
Well, $con will already be an object, as it's instantiating a new PDO object. Unless you're trying to add functionality to your PDO object, wrapping it is pointless.
That said, the best way to share your userdb/PDO object (depending on whether or not you stick with the wrapper) with other objects is to use Dependency Injection. That's a fancy term for passing your db to whatever object needs it. Since objects are defaultly passed by reference in PHP, if you create your db object first, all objects that receive it as a constructor/method argument will be using that same single instance.
EDIT: Link to Dependency Injection implementation
EDIT2: Clarification on DI in small projects -
The normal DI pattern generally requires a special object called a DI Container. This is a special use object that automatically injects the dependency into the object that needs it. For small projects, it's overkill. The simple, low complexity version of DI is simply:
class SomeClass {
protected $db;
public function __construct($db) {
$this->db = $db;
}
}
class SomeClass2 {
public function SomeMethod($db) {
// do something with the db
}
}
$db = new PDO(/* connection string */);
$obj = new SomeClass($db, /* other constructor args */);
// or
$obj2 = new SomeClass2(/* constructor args */);
$obj2->someMethod($db, /* method args */);
The magic is that since objects are passed by reference by default in PHP, $obj and $obj2 are using the same db connection.
The whole idea is to not blow apart scope or encapsulation by having a static method, and to ensure that classes and their methods are up front about what they require in order to work.
Singletons do the exact opposite. They're accessed through static methods, which bypass scope, and since they're invoked and not passed, they never show up in method signatures, so anyone not familiar with the code won't be aware of that hidden requirement. Even Erich Gamma, one of the people who helped codify the Singleton Pattern has regrets about it:
I'm in favor of dropping Singleton. Its use is almost always a design smell.
In PHP, where there's no concept of shared memory, and where scripts execute once per request, the only reason to want to use a singleton is to have easy access to a single resource. Since objects are passed by reference, a single instance can be shared with multiple objects naturally. From there, it's about good design and delegation.
use singlton class implimentation
class connectdb
{
protected static $_instance = NULL;
private function __construct()
{
}
public function getinstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
public function connect()
{
$this->connection =new PDO("mysql:host=$host;dbname=$db", $user, $pass);
}
}
for using a variable within a class function or independent function you need to place a global keyword
$conn=mysql_connect();
function test(){
global $conn;
}
now $conn will be available within the scope of test function, and it will be available everywhere when defined at the top of the script. For class also you need to do the same thing, make a object of a class and declare it as global within a function