One of the unique features of PHP is the ability to use implemented constants from interfaces. In most other languages, an interface is wholly abstract. You can only add abstract methods to an interface—no properties can be placed in an interface, abstract or concrete. This little post shows how to use constants in an interface, along with an abstract method, to create a re-usable connection application. In using this little application, I’ve found it to be easy to re-use, and while it is not a design pattern, it is so flexible and loosely coupled that it reflects the spirit of design patterns and OOP in PHP.
The Interface
Like all interfaces, you want to have a general structure outlined. In this interface, I’ve included all of the connection information needed to use in conjunction with PHP’s Mysqli class. Note, this uses Mysqli—with an “i” for ‘improved’.
//Filename: IConnectInfo.php
interface IConnectInfo
{
const HOST ="localhost";
const UNAME ="phpDP";
const PW ="looseCoupling";
const DBNAME = "dpExpress";
public function doConnect();
}
?>
|
There’s also the abstract method, doConnect(), that is written as all abstract methods. It has no parameters and is to be accessesed publicly. (It has public visibility.) Each of the constants are assigned a concrete value that is passed to the class that implements it.
A Universal Connection Class
Next, we can create a universal connection class containing the single method defined in the interface. We can also use the constants’ values to make the connection. To do this requires using the scope resolution operator (::) because there are no instances of the constants. We cannot instantiate an instance of an interface, but we can get the values of the constant using the scope resolution operator as shown in the following implementation:
//ini_set("display_errors","1");
//ERROR_REPORTING(E_ALL);
include_once('IConnectInfo.php');
class UniversalConnect implements IConnectInfo
{
private $server=IConnectInfo::HOST;
private $currentDB= IConnectInfo::DBNAME;
private $user= IConnectInfo::UNAME;
private $pass= IConnectInfo::PW;
public function doConnect()
{
$hookup=new mysqli($this->server, $this->user, $this->pass, $this->currentDB);
if (mysqli_connect_error())
{
die('Connect Error (' . mysqli_connect_errno() . ') ' . mysqli_connect_error());
}
return $hookup;
}
}
?>
|
Each of the connection constant’s values are encapsulated in private variables and then used in the implemented method, doConnect(). Importantly, the class has no constructor function; so it doesn’t fire off as soon as it’s instantiated.
Doing Connections
In Figure 1, you can see the relationship between the different objects that make up the connection structure. In some respects the UniversalConnect class is like an abstract class in that nothing happens when it is instantiated. (Of course you cannot instantiate an abstract class). It is used by concrete classes to make a connection by calling the doConnect() method in the UniversalConnect object.
So how does this make life easier and more productive for the programmer? Instead of putting in the entire connection routine, along with all of the correct connection names, it only takes two simple lines to make a connection:
$operation=new UniversalConnect();
$this->hookup=$operation->doConnect();
The private variable $this->hookup is the name of the connection variable that uses the doConnect() method from the UniversalConnect class. To get a better idea of how it works, the following class, Update, is a simple PHP class for updating data in a table.
include_once('../UniversalConnect.php');
class Update
{
private $hookup;
private $sql;
private $tableMaster="productTable";
private $id;
private $field;
private $fValue;
public function __construct()
{
//Two lines for entire connection operation
$operation=new UniversalConnect();
$this->hookup=$operation->doConnect();
//
$this->id=$_POST['recordID'];
$this->field=$_POST['fieldNow'];
$this->fValue=$_POST['changeNow'];
$this->sql ="UPDATE $this->tableMaster SET $this->field='$this->fValue' WHERE id='$this->id'";
if ($result = $this->hookup->query($this->sql))
{
echo "Record $this->id in field [$this->field] has been changed to: $this->fValue";
}
$this->hookup->close();
}
}
$doUpdate=new Update();
?>
|
Given the loose coupling between the implementation and the connection values, you can easily change the connection values without having to re-write the connection information for a whole set of classes that use a different set of values. For example, if you have two customers who want roughly the same back end, instead of revising all of the classes where a different set of connection values are used, you can just change the connection values in the single interface without having to change anything else.
Naturally, I’d be very interested in any feedback, questions or comments you might have about using this kind of loose coupling with PHP-MySQL connections.
Copyright © 2012 - All Rights Reserved
This technique works if you are operating on single database..What will you do if you want read data from multiple database ..
Hi Machi,
That’s an interesting issue Machi. What do you currently use when simultaneously using two different databases?
Thanks,
Bill
Ups sorry !
server=$server;
$this->user=$user;
$this->pass=$pass;
}
public function doConnect(){
mysql_connect($this->server, $this->user, $this->pass);
OR die (‘Database is error’);
}
public function ChangeDoConnect($PortDB){
mysql_select_db ($PortDB)
OR die (‘Database is error’);
}
}
$PortDB = new UniversalConnect (‘localhost’, ‘root’, ”);
$PortDB -> doConnect();
$PortDB-> ChangeDoConnect(‘test1′);
echo ‘Database DB3 is Connected’. ”;
$PortDB = new UniversalConnect(‘localhost’, ‘root’, ”);
$PortDB -> doConnect();
$PortDB-> ChangeDoConnect(‘test2′);
echo ‘Database DB3 is Connected’.”;
*/
Hi Zlumberjay,
Tell us something about this. Is this a way to deal with more than one database? Is there a way to do this using mysqli instead of mysql, or did you choose mysql so that you could separate the server/user/pw from the database?
Let us know.
Thanks,
Bill
The script above not complete and I keep it in pastebin http://pastebin.com/Vq4DsBs0. It’s only concept for multidatabase and I think it’s not ideal coz not using “singleton” and “static”. And I’m not use the MySQLi coz usually use MySQLi to PDO.
Hi Zlumberjay,
You do not need to use a Singleton nor static, even though using static properties and methods can be helpful.
Take care,
Bill
why not use the abstract class?
Hi Zlumberjay,
Sorry for taking so long. I was at a conference out of the country. Why not use an abstract class? You could. Generally, interfaces are more abstract than abstract classes, and I like the idea of being able to have constants in an interface. I’ve found using interfaces to be very flexible, and since that’s what I’m after–flexibility–I selected an interface over an abstract class.
Kindest regards,
Bill
great article
I would rewrite the “UniversalConnect” class like this :
——
This implementation has the following advantages :
1) IConnectInfo Constants are used as defaults, but can be overridden by passing parameters to the “doConnect” method.
2) Parameters are passed to the “doConnect” as an array (or – alternatively – an object), which grants total freedom with respect to the number of parameters passed and the order of the parameters, as long as the names of the indexes match.
3) There’s no need for the private config properties, which are completely redundant in this implementation.
——
In case you want multi-layered defaults (default overriding defaults of parents) or you just want to define custom defaults at a class level, here’s an variation of the same implementation, which adds a protected static array named $_defaults :
The array is protected, so it’s available in children of this class but not anywhere else. It is static, because all instances of the same class are supposed to have the same defaults.
Thanks John!
Those are some interesting additions. One thing I was not sure about was your use of the “protected” static $_defaults. Why not “private?”
Kindest regards,
Bill
Hi Bill,
While I realize that it is generally considered a good practice to (1) default to the lowest visibility required and (2) favor object composition over class inheritance, (a) requirements do tend to change as projects evolve and (b) basic inheritance is often the easiest way to modify an existing codebase as well as (c) an easy solution for creating classes that share >80% of their functionality.
In most cases, I prefer to leave the option of inheriting a class and its unexposed properties / methods unless I have a very good reason not to do so, but I guess that’s just a very personal (experience based) preference.
Do you consider that a bad practice? If so, feel free to enlighten me on what I may be overlooking.
Hi John,
Generally, I like your reasoning for flexibility and using a protected visibility over a private one and, as you say, it does provide the option of expansion. It also maintains encapsulation, which is the purpose of using private visibility; so nothing is broken on that score.
I have reused this particular utility so much with success and ease over the years, I tend to go the other way—namely toward non-expandability and simplicity. It’s one of those little packages–un peu de structure— that you can drop on the floor and run over with a Mack truck, and it keeps on working. I’ve had a wide range of clients using different servers and sever configurations as well as applications and design patterns motoring those applications, and I’ve used the interface and UniversalConnect class, and Bob’s your uncle! it makes the connection in a single line. The only change I have to make in the interface is the value of the constants.
The irony lies in the fact that the flexibility is in the constants in the interface. Even more so, the constants are set with string literals! However, only four values are required for connections using mysqli() [as opposed to mysql()]. Once those are set, the connection program can be reused with the same database information on as many apps as you want. You can create as many tables as you like, including ones in a relational database without having to change anything in the connection app.
Your thoughtful comments have had an inspirational effect, and I have made some adjustments to the UniversalConnect class over the years. So, I’m going to put together a new post on this little routine, and place the blame for re-thinking some of the issues squarely on you! …for which I am most grateful…
Kindest regards,
Bill