I'm trying to add a static member to each of my classes that contains the default database connection that they should use when instantiated. Here's how I'm trying to do it:
<?php //other classes extend Generic
class Generic {
public static $defaultDatabase;
public $db;
function __construct (&$pDatabase = null){
if ($pDatabase!==null)
$this->db = &$pDatabase;
else
$this->db = &$defaultDatabase;
}
}
?>
<?php
include_once("/classes/class.Database.php");
$db = new Database ("localhost", "username", "password", "TestDatabase");
$classes = array("Generic", "Member");
foreach ($classes as $class){
include_once("/classes/class.$class.php");
$class::defaultDatabase = &$db;//throws error here, unexpected "="
}
?>
What am I doing wrong? Is there a better way to do this, or do I have to set the defaultDatabase for each class individually? I'm using php 5.3, which I understand should support something like this.
In this code
$class::defaultDatabase = &$db
You should add $ before defaultDatabase, since static properties are accessed via
ClassName::$staticProperty
Unlike the others which are accessed via
$class->property;
Use self::$propertyName to access static properties:
function __construct (&$pDatabase = null){
if ($pDatabase!==null)
$this->db = &$pDatabase;
else
$this->db = self::$defaultDatabase;
}
Also note, that using the reference operator &$var is meaningless if $var is an object. This is because all objects in PHP are actually references.
Related
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'm reading up on php design patterns, and I saw this code:
<?php
require_once("DB.php");
class DatabaseConnection
{
public static function get()
{
static $db = null;
if ( $db == null )
$db = new DatabaseConnection();
return $db;
}
private $_handle = null;
private function __construct()
{
$dsn = 'mysql://root:password#localhost/photos';
$this->_handle =& DB::Connect( $dsn, array() );
}
public function handle()
{
return $this->_handle;
}
}
print( "Handle = ".DatabaseConnection::get()->handle()."\n" );
print( "Handle = ".DatabaseConnection::get()->handle()."\n" );
?>
I understand it all except the last two print statements. I've been messing around with it, but I don't understand the static function somehow calling a public non-static function.
I've notice I can do:
DatabaseConnection::get()->get()->get()->handle();
but I can't so something like:
DatabaseConnection::get()->handle()->get();
I just don't understand what this is doing, other than calling the get function then calling the handle function.
This works because the static function returns a new object. This type of construction is typically referred to as a Singleton, since it is attempting to enforce that only one instance of a DatabaseConnection is ever available.
Notice that the constructor is private, so you cannot explicitly call new DatabaseConnection() unless you are already inside the class. Solutions utilizing a Singleton will have a property, initally null, that is then set to a non-null value upon object instantiation. The 'getInstance' (or get in this case) method will only return a new object if the property is null.
DatabaseConnection::get() creates an instance of DatabaseConnection and returns it.
So...
DatabaseConnection::get()->handle();
...could also be written as follows...
$db = DatabaseConnection::get();
$db->handle();
I started using PHP classes and looked into OOP.
I played around by creating a Database class that looks something like this (snippet):
class Database
{
private $link;
private $result;
private $count;
function connect()
{
$this->link = mysql_connect(DB_HOST, DB_USER, DB_PW);
$return = $this->link ? 'Connected to database.' : 'Failed to connect.';
$this->open(); // I have another function in this class to select the db
echo $return;
}
function query($query)
{
$this->result = mysql_query($query);
}
function fetch()
{
return mysql_fetch_assoc($this->result);
}
function count()
{
$this->count = mysql_num_rows($this->result);
return $this->count;
}
}
Just to test, I created another class called Guestbook that looks like this:
class Guestbook
{
function fetch()
{
Database::query('SELECT * FROM guestbook LIMIT 2');
while($row = Database::fetch()){
$result_array[] = $row;
return $result_array;
}
}
Now I tested calling the functions:
$db = new Database();
$db->connect();
$db->query('SELECT * FROM test');
while($row = $db->fetch()) {
echo $row['id'].'<br />';
}
echo $db->count();
$db->close();
This works as expected. So I went on with the Guestbook class:
$db->connect();
$guestbook = new Guestbook();
$results = $guestbook->fetch();
foreach($results as $gb) {
echo $gb['id'];
}
echo $db->count(); // HERE'S MY PROBLEM !
$db->close();
Everything works as I wanted, but the second time I call $db->count() it echos the previous count and not 2 (as I set LIMIT 2 in the Guestbook fetch function).
How do I interact with these classes properly, so I can use something like $db->count() globally?
Thanks in advance for any hints or solutions!
First off, there is no reason to wrap mysql_* implementations in OOP. IF you are going to do OOP then just use Mysqli or PDO which have OOP classes you can extend.
Secondly you want to hold an instance of the Database in the GuestBook class, not make it an extension of the Database class itself.
It is hard to give you a definitive advice on how you could wrap your database accessor with OOP and still access the count() globally.
Clearly your code is wrong.
by using the notation
Database::query
you are referring to some kind of static methods on the Database class which does not seem to be the intent here. It looks like your code does not crash because of some backward compatibility with PHP4.
you could rewrite Guestbook along this way :
class Guestbook
{
private $db;
function __construct(&$db) {
$this->db = $db;
}
function fetch($option)
{
$this->db->query('SELECT * FROM guestbook LIMIT 2');
while($row = $this->db->fetch()){
$result_array[] = $row;
return $result_array;
}
}
and then
$db = new Database();
$db->connect();
$guestbook = new Guestbook($db);
$results = $guestbook->fetch();
echo $db->count();
This technique uses a pattern known as "Dependency Injection"
Why would you use count from the db class when you have the results from the guestbook->fetch() method?
This does exactly the same trick:
echo count( $resutls );
see the $db is a object of the class Database
never access the function OR property of the Guestbook
you can get this directly with count( $resutls )
OR may have to go with the inheritance concept
Thanks
You mixing static with instantiated versions of your class. If you want to be able to create multiple instances of a class, you should have a __construct function. While it's not required, it's best practice to have one.
You are creating a new instance and assigning it to the $db variable ($db = new Database();). But then in your GuestBook class you are referencing the database class statically (Database::). That syntax references the "base" class which is the same across all instances. What you want to do is reference the instance of the database class you created. Typically you would pass the database instance to use into the GuestBook class in the constructor, store that in a class variable, then reference it using $this.
class Guestbook
private $db = null;
{
function __construct($db) {
$this->db = $db;
}
function fetch($option) {
$this->db->query('SELECT ...');
...
}
}
$db = new Database();
$guestbook = new Guestbook($db);
I have a database class, which an instance is declared in the main index.php as
$db = new Database();
Is there a way for the $db variable to be globally recognized in all other classes without having to declare
global $db;
in the constructor of each class?
No. You have to declare Global $db in the constructor of every class.
or you can use the Global array: $_GLOBALS['vars'];
The only way to get around this is to use a static class to wrap it, called the Singleton Method (See Here for an explanation). But this is very bad practice.
class myClass
{
static $class = false;
static function get_connection()
{
if(self::$class == false)
{
self::$class = new myClass;
}
else
{
return self::$class;
}
}
// Then create regular class functions.
}
The singleton method was created to make sure there was only one instance of any class. But, because people use it as a way to shortcut globalling, it becomes known as lazy/bad programming.
StackOverflow Knowledge
How to Avoid Using PHP Global Objects
Share Variables Between Functions in PHP Without Using Globals
Making a Global Variable Accessible For Every Function inside a Class
Global or Singleton for Database Connection
I do it a little different. I usually have a global application object (App). Within that object I do some basic initialization like creating my db objects, caching objects, etc.
I also have a global function that returns the App object....thus (application object definition not shown):
define('APPLICATION_ID', 'myApplication');
${APPLICATION_ID} = new App;
function app() {
return $GLOBALS[APPLICATION_ID];
}
So then I can use something like the following anywhere in any code to reference objects within the global object:
app()->db->read($statement);
app()->cache->get($cacheKey);
app()->debug->set($message);
app()->user->getInfo();
It's not perfect but I find it to make things easier in many circumstances.
you could use
$GLOBALS['db']->doStuff();
or alternatively using some kind of singleton access method
Database::getInstance()->doStuff();
Why not create a class that contains the global $db; in it's constructor, then extend all other classes from this?
You could use a Registry class to store and retrieve your Database instance.
class Registry
{
protected static $_data = array();
public static function set($key, $value)
{
self::$_data[$key] = $value;
}
public static function get($key, $default = null)
{
if (array_key_exists($key, self::$_data)) {
return self::$_data[$key];
}
return $default;
}
}
Registry::set('db', new Database());
$db = Registry::get('db');
If I create an object inside of the main scope:
INDEX.PHP:
$db = new database();
Then how can I use this same object inside of a completely different class?
ANYTHING.PHP:
class anything {
function __construct(){
$db->execute($something); # I want to use the same object from INDEX.PHP
}
}
Would I need to make $db a global or is there a 'better' more obvious way?
You could just use global to find it:
class anything {
function __construct(){
global $db;
$db->execute($something);
}
}
Or, you could pass it in when creating a new anything:
class anything {
function __construct($db) {
$db->execute($something);
}
}
It really depends on what makes the most sense for you.
For the DB you may want to use Singleton pattern
class anything
{
public function load($id)
{
$db = DB::getInstance();
$res = $db->query('SELECT ... FROM tablename WHERE id = '.(int)$id);
// etc...
}
}
You may want to extend it if you need different DB connections at the same time (i.e main db and forum's db). Then you'll use it like DB::getInstance('forum'); and store instances in associative array.
You could pass it as an argument, like this
function __construct($db){
$db->execute($something);
}
then when you instance anything, do it as anything($db)
As Paolo and ONi suggested you can define $db as global inside the method or pass it into the constructor of the class. Passing it in will create a reference to that object so it will in fact be the same $db object. You could also use the $GLOBALS array and reference $db that way.
$GLOBALS["db"];
I'm assuming that index.php and anything.php are linked together somehow by include() or require() or some similar method?
In Paolo's post:
After you pass it, you can then assign it to a class variable like this:
class anything {
var $db_obj;
function __construct($db) {
$this->db_obj = $db;
}
function getUsers() {
return $this->db_obj->execute($something);
}
}