Hi there,
PHPdev wrote:Im looking for the best way to get a db connection into a class method.
After looking at your initially proposed implementation, you might benefit from looking into the Singleton Pattern.
The Singleton Pattern
The singleton pattern is the OOP nuts Global variable.
The benefits of implementing this pattern for your DB object would be of the following:
Don't let the term scare you, your class can be made into a singleton (in php 4 - although it's easier with php 5) by adding a single public static method into your DB Abstraction Class' API and a quick check in the constructor.
The keyword behind all the magic is the "static" keyword. It lives withiin the method get_instance() and since static variables are persistant to the local scope of the method throughout the life of the script. It will remember its last assigned value.
For more on the static keyword, check out php.net 🙂
The implementation can be something to the effect of:
function &get_instance()
{
static $instance = false;
if (!$instance) {
static $instance[0] =& new MySQL(M_E);
}
return $instance[0];
}
Then in your constructor:
function MySQL($key=false)
{
if ($key == M_E) {
$this->_connect();
$this->_selectDB();
} else {
trigger_error('This is a singleton implementation,
please use static get_instance() method);
}
}
Configuration file
Another thing that was already mentioned is that you should have your connection details etc in a separate file. The file doesn't necessary have to be out of the webroot directory, although this is ideal, but if you MUST have it inside the webroot, a neat trick is setting permissions so that only the owner has access to the file(s). Remove permissions from public and groups.
What I used to do was store the access details in a file as CONSTANTS and call them from my scripts. This includes system, library and framework directories constant definitions.
config.php (CHMOD 700) - I think 😉
define('HOSTNAME', 'some.mysql.server');
define('USERNAME', 'myname');
define('PASSWORD', 'mypassword');
define('DATABASE', 'some_db');
So if you did want to make your MySQL Abstraction Class into a singleton, it'd look something like this:
MySQL Class
<?
class MySQL
{
/**
* Database Server address
*
* @var string
* @access private
*/
var $_hostname = HOSTNAME;
/**
* Database Username
*
* @var string
* @access private
*/
var $_username = USERNAME;
/**
* Database Password
*
* @var string
* @access private
*/
var $_password = PASSWORD;
/**
* Database name
*
* @var string
* @access private
*/
var $_database = DATABASE;
/**
* Connection ID
*
* @var mixed
* @access private
*/
var $_connection_id;
/**
* Constructor
* Checks for a key which is passed by the get_instance()
* method upon instantiation.
*
* The key can be anything, but I've seen the PHP constant M_E
* used in other examples and I'm too lazy to think up my own
* convention.
*
* @param mixed $key
* @access public
*/
function MySQL($key = false)
{
if ($key == M_E) {
$this->_connect();
$this->_selectDB();
} else {
die('this class is a Singleton, Please
instantiate using get_instance() method');
}
}
/**
* Get an instance of the object.
* if the object instance does not exist, create it
*
* @return unknown
* @access public
*/
function &get_instance()
{
static $instance = array();
if (!$instance) {
$instance[0] =& new MySQL(M_E);
}
return $instance[0];
}
/**
* Connection id accessor
*
* @return mixed
* @access public
*/
function get_connection_id()
{
return $this->_connection_id;
}
/**
* Connect to database
*
* @access private
*/
function _connect()
{
$conn = mysql_connect($this->_hostname,
$this->_username,
$this->_password);
if (!$conn) {
//handle the error somehow or just die
die('Could not connect to database');
} else {
$this->_connection_id = $conn;
}
}
/**
* Select the database
*
*/
function _select_DB()
{
$select = mysql_select_db($this->_database,$this->get_connection_id());
if (!$select) {
//handle the error somehow or just die
die('Could not connect to database');
}
}
//... Whatever other APIs the class should have
function query()
{
//...
}
}
?>
Procedural Usage
require_once 'config.php'; // we'll assume that we've saved in the same directory
$db =& MySQL::get_instance();
$result = $db->query($sql);
Composition
contact.php
<?php
class Contact
{
/**
* MySQL Object
*
* @var MySQL
*/
var $db;
function Contact()
{
$this->db =& MySQL::get_instance();
}
function getContact()
{
// Query the database and retun the results
$sql = "SELCT * FROM contact";
$results = $this->db->query($sql);
}
function validateContactId( $id )
{
//...
}
}
?>
A quick word about using the Singleton pattern
The singleton pattern is in essence a global variable, but not quite. It cannot be changed accidentally. But it is susceptible to abuse. There is nothing stopping you from accessing a singleton class instance from all over your application, and for this reason, if you use it haphazzardly, you might start finding calls to it all over your application, turning the well meaning class into a virus.
Remember to follow some simple OOA & D rules Aggregation over Composition, and Composition over Inheritance.
With your proposed implementation, it seems like your contact class should be aggregating the database since it uses it instead of the database class being a part of the contact class.
I'd rewrite the relationship using one of the rules mentioned above:
Aggregation over Composition
<?php
class Contact
{
/**
* MySQL Object
*
* @var MySQL
*/
var $db;
function Contact(&$mysql)
{
$this->db =& $mysql;
}
function getContact()
{
// Query the database and retun the results
$sql = "SELCT * FROM contact";
$results = $this->db->query($sql);
}
function validateContactId( $id )
{
//...
}
}
$mysql =& MySQL::get_instance();
$contact =& new Contact($mysql);
?>
Yipe! :o got carried away. oh well, maybe I'll use part of this to put on my blog heh.
If you have any questions regarding this, or are interested for more resources on the Singleton Pattern or Patterns in general, let me know, I've plenty of sources should you require it.
Regards,