TalkPHP
 
 
Account Login
Latest Articles
» The basic usage of PHPTAL, a XML/XHTML template library for PHP
» Vulnerable methods and the areas they are commonly trusted in.
» Simple way to protect a form from bot
» The Basics On: How Session Stealing Works
» How to keep your forms from double posting data
Advertisement
Associates
Associates
techtuts Darkmindz
CSS Tutorials Tutorialsphere.com - Free Online Tutorials
Boston PHP SurfnLearn
Reply
 
LinkBack (4) Thread Tools Search this Thread Display Modes
Old 10-20-2007, 09:19 PM   4 links from elsewhere to this Post. Click to view. #1 (permalink)
The Gregarious
Advanced Programmer Top Contributor Good Samaritan 
 
sketchMedia's Avatar
 
Join Date: Oct 2007
Location: Manchester, UK
Posts: 532
Thanks: 26
sketchMedia is on a distinguished road
Big Grin Using the factory pattern (mad rantings of a mind without coffee)

Yo,

This is my first tutorial of sorts, i came across this method of doing things a few months ago when i needed some form of database abstraction. This is what i came up with (albeit this version is greatly stripped down to its core so that it will show the workings better), if you have any improvements to this method or if there are any better ways of doing it i'm open to ideas :) .

The first point I'd like to make is the use of an interface in this script, its important to understand that i needed a database abstraction class and the interface allows me to specify which methods a class must implement, therefore i can have a mysql class and a mssql class, both must have the same methods so that the rest of the system will work the same when i decide i want to change over from mysql to mssql.

For example i have a class called mysql, it has a method called query() for sending querys to the database, and i have used it in my system extensively, then i decide i don't want to use mysql anymore and i want to switch over the mssql. The new mssql class i code also has a query method, but without thinking i call it doQuery() or something, now the rest of the system where the method query() was called will fail. The interface allows you to apply a kind of rule to the class, so if i tried naming a method doQuery() and in the interface it said that it must have a query() method, PHP will throw an error and more importantly i don't have to go through hundreds of lines of code changing names of method calls.

with that hopefully explained i can get on (hopefully)

Right, i will be combining two patterns here really, the database object is instantiated with a factory pattern but the database object is singleton.

here is the database classes and interface:

PHP Code:
interface DB
{
    public function 
connect();
    public function 
query();
    public function 
disconnect();
}

class 
DBmysql implements DB
{
    public function 
connect()
    {
        
//database connection stuff
    
}
    public function 
query()
    {
        
//perform database query stuff
    
}
    public function 
disconnect()
    {
        
//disconnect database
    
}
}

class 
DBmssql implements DB
{
    public function 
connect()
    {
        
//database connection stuff
    
}
    public function 
query()
    {
        
//perform database query stuff
    
}
    public function 
disconnect()
    {
        
//disconnect database
    
}

as you can see both classes 'implement' the interface meaning that they must conform with that interface (as they do).

now the factory class, this will determine the type of database that we want to use, then create the object for us:

PHP Code:
class DBfactory
{
    public static 
$pDB;

    public static function &
factory($szType "")
    {
        if(!
is_object(self::$pDB))
        {
            switch(
$szType)
            {
                case 
'mysql':
                    
self::$pDB = new DBmysql;
                    break;
                case 
'mssql':
                    
self::$pDB = new DBmssql;
                    break;
                default:
                    
self::$pDB = new DBmysql;
                    break;
            }
        }
        return 
self::$pDB;
    }

First thing to notice is that i have a static variable 'pDB' and as the naming convention should tell you, i'm going to be using this as an object pointer (well php reference to an object, as C pointers and PHP references are slightly different but thats out of the scope of this tutorial, they both do practically the same thing), its static because it needs to remain without having an instance of the object to be created, i'm assuming you know what a static is as this is in the advanced PHP forum :cool: .
This static allows us to make the database object a singleton (read karl's great tutorial on the singleton design pattern, if your not sure what im going on about:
How to use the Singleton design pattern).

Next i have a static method called factory, this is the method we call to create a database object, its static like the object pointer so that i don't actually have to have an instance of this class in order to use it, also at the start of the function name is the "&" symbol, this basically tells PHP the i will be returning a reference (returns by reference instead of by value).
The method has a string param 'szType' with a blank default value, this will be how we tell the class which database type we need, for this example i will be using mysql.

Next the string param goes into a switch but before it does it checks the static 'pDB' to see if there is already an object created, if there is just reuse that object, if there isn't it then creates the object from the correct class.

notice that because 'pDB' is static it isn't instantiated in the object, so $this-> wont work as we don't have a reference to the current object ($this is a reference to the calling object which is usually the object to which the method belongs although it can be others too)
thus we have to use the self keyword, along with the the Scope Resolution Operator "::" so that we can access the static variable 'pDB'.

Because we have a default value of szType (szType = "") if nothing is passed into the method, the switch will go to the default (saves you having to type the database type if the default is all you need) or alternatively you could have a named constant in config file or something.

right moving on, inside the switch (after it has determined the type, for example we have passed in the string 'mysql' into the param) it goes and creates an instance of the class 'DBmysql' then assigns the static variable 'pDB' with the reference to that object, then returns the reference of the reused/newly created object, out of the method.

To use this we could do this:
PHP Code:
$db DBfactory::factory("mysql");
$db->query(".."); 
or just use the default:
PHP Code:
$db DBfactory::factory();
$db->query(".."); 
We could alternitivly chain it all together (as discussed in another topic) this is my favourate way, it just makes it look nicer:
PHP Code:
DBfactory::factory()->query(".."); 
hope that made sense, right im off to drink alot of coffee now, im sure when i return and re-read this i will notice a whole load of incomprehensable and vomit inducing grammatical and general errors, thats the risk you take when you do something that requires your brain to be working without feeding it coffee beforehand.
(i appologise if its seems a bit jumbled and crazy, its just they way i am when i havent had my daily coffee to calm my brain down abit, so basicly this tutorial is a result of my coffee withdrawal insanity)

ciao for now
__________________
sketchMedia is offline  
Reply With Quote
The Following 2 Users Say Thank You to sketchMedia For This Useful Post:
Alcar (01-17-2008), Tanax (01-23-2008)
Old 10-20-2007, 11:12 PM   #2 (permalink)
The Gregarious
Upcoming Programmer Inquisitive 
 
Join Date: Sep 2007
Posts: 748
Thanks: 85
Tanax is on a distinguished road
Default

That looks really awesome! :D

Great article :)
Although, I still don't understand why you use interface here =// Is there any article on this site explaining the advantage of interfaces?
Tanax is offline  
Reply With Quote
Old 10-21-2007, 02:24 AM   #3 (permalink)
The Gregarious
Advanced Programmer Top Contributor Good Samaritan 
 
sketchMedia's Avatar
 
Join Date: Oct 2007
Location: Manchester, UK
Posts: 532
Thanks: 26
sketchMedia is on a distinguished road
Default

hehe i didnt get them at first, and i guess my explanation was a bit lacking.

i havnt seen any articles on this site that explain interfaces, my applogies if i have missed them.
Ill have another go at explaining them :D

Basicly the interface is there as a kind of template for any classes that implement it i.e.
PHP Code:
class DBmysql implements DB 
this means that this class must have all the methods listed in the interface. So if i was to code another class, for example for oracle, i would tell it that this new oracle class implements the interface like so
PHP Code:
class DBoracle implements DB 
what this basicly means is that the oracle class must contain everything that is in the interface, it doesnt matter about how the new class works, for example the DBmysql query() method can and will go about sending a query to the database in a different way than the oracle class will, because they are different systems. In this case there is 3 methods stated in the interface:

Our interface looks like this:
PHP Code:
interface DB
{
    public function 
connect();
    public function 
query();
    public function 
disconnect();

now in our new oracle class we must implement each of these methods.
implemented class looks like this:
PHP Code:
class DBoracle implements DB
{
    public function 
connect() { /*do whatever php needs to connect to oracle */ }
    public function 
query(){ /* do oracle query stuff */ }
    public function 
disconnect(){ /* disconnect from oracle */ }

its really away of telling the PHP engine to make sure that those methods are in the implemented class (aswell as making sure that each method has the same visibility i.e. any method with public in the interface must be public within an implemented class of that interface, same applys for parameters, the implemented method must have at least the same number of parameters specified in the interface, it may have more just not less).
Its not totally neccisary but it really helps, especially when you havent coded it yourself, for example someone within the development team (who didnt develop the exsisting system) has been told to extend the system and add support for oracle, instead of reading through all the code finding out the different method names within the exsisting database classes, with an interface he is able to open the interface and find out right away all the method names so he can code the new class accordingly and be 100% sure that when he tries his new oracle class it will work 100% with the rest of the system (as the api will be the same).

To sum it up: Basically, an interface is a contract or a blueprint. When you implement an interface, you must always implement the methods, propeties, and params that it asks of you. In this case it ensures that any new databse connection class will have the same api that the other classes use therefore will have the same api that the rest of the system uses, enabling a smooth transition of database providers, without having to redesign everything again.

i hope i have made things a bit clearer, it confused the hell out of me for a while but it does eventually make sense, especially when you start using it for projects with more than one coder.
__________________
sketchMedia is offline  
Reply With Quote
Old 10-21-2007, 09:55 AM   #4 (permalink)
The Gregarious
Upcoming Programmer Inquisitive 
 
Join Date: Sep 2007
Posts: 748
Thanks: 85
Tanax is on a distinguished road
Default

That was a really good explenation!

However, if I have an interface like you said, can I use other functions ASWELL as the ones in the interface, in the class that implents the interface??
Tanax is offline  
Reply With Quote
Old 10-21-2007, 04:30 PM   #5 (permalink)
The Gregarious
Advanced Programmer Top Contributor Good Samaritan 
 
sketchMedia's Avatar
 
Join Date: Oct 2007
Location: Manchester, UK
Posts: 532
Thanks: 26
sketchMedia is on a distinguished road
Default

Yes you can, aslong as the implemented class contains at least all of the methods etc in the interface you can have extra methods in the implemented class.

An important note that i forgot is that the interface class is a totally abstracted class, in other words it doesnt do anything, there are no workings or body to it, it has methods etc but no guts to it, its up to the implemented classes to take care of that.

glad you liked it :)
__________________
sketchMedia is offline  
Reply With Quote
Old 10-22-2007, 11:26 AM   #6 (permalink)
The Reckoner
Advanced Programmer Top Contributor 
 
Karl's Avatar
 
Join Date: Sep 2007
Posts: 438
Thanks: 22
Karl is on a distinguished road
Default

Thanks for contributing sketch, there's some good information in this thread.
__________________
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
Karl is offline  
Reply With Quote
Old 11-01-2007, 02:24 PM   #7 (permalink)
The Gregarious
Upcoming Programmer Inquisitive 
 
Join Date: Sep 2007
Posts: 748
Thanks: 85
Tanax is on a distinguished road
Default

I have another question.
Your static function factory, can I name it something else?

PHP Code:
public static function &getType($szType '') {
if(!
is_object(self::$pDB))
        {
            switch(
$szType)
            {
                case 
'mysql':
                    
self::$pDB = new DBmysql;
                    break;
                case 
'mssql':
                    
self::$pDB = new DBmssql;
                    break;
                default:
                    
self::$pDB = new DBmysql;
                    break;
            }
        }
        return 
self::$pDB;
    } 

PHP Code:
$db DB::getType('mysql');
$db->connect()->query('...'); 
???
Tanax is offline  
Reply With Quote
Old 11-01-2007, 02:32 PM   #8 (permalink)
Moderateur
RegEx Guru PHP Guru Top Contributor Advanced Programmer 
 
Salathe's Avatar
 
Join Date: Apr 2007
Posts: 753
Thanks: 2
Salathe is on a distinguished road
Default

Yes you can name it something else, it needn't necessarily be called factory.
__________________
Salathe is offline  
Reply With Quote
Old 11-01-2007, 02:34 PM   #9 (permalink)
The Gregarious
Upcoming Programmer Inquisitive 
 
Join Date: Sep 2007
Posts: 748
Thanks: 85
Tanax is on a distinguished road
Default

Okey! But I do need the "&" sign before the name of the function?
Tanax is offline  
Reply With Quote
Old 11-01-2007, 06:20 PM   #10 (permalink)
Super Moderator
Advanced Programmer 
 
bluesaga's Avatar
 
Join Date: Sep 2007
Posts: 164
Thanks: 0
bluesaga is on a distinguished road
Default

I may be wrong, but i believe you do not need the & before the function name. As PHP since PHP 5 does class/function referencing by default, and seen as though this is PHP 5 OOP, its not needed.

Disclaimer: I've never really understood this ampersand crap in the past, so don't quote me on anything! :D But I'm 90% sure its just for making a reference when refering to a variable/class/function instead of cloning it.
__________________
Halo 3 Cheats
bluesaga is offline  
Reply With Quote
Old 11-02-2007, 11:58 PM   #11 (permalink)
The Gregarious
Upcoming Programmer Inquisitive 
 
Join Date: Sep 2007
Posts: 748
Thanks: 85
Tanax is on a distinguished road
Default

Okey, I have another question.

Let's say I have DBmysql.php, and in it I have the class for mysql.
I also have DBoracle.php, and in it I have the class for oracle.


Then I have the file DB.php, where I have this static function.. how would I make an object of classes in other files?

PHP Code:
switch($type) {
                    
                    case 
'mysql':
                        include(
'DB'.$type.'.php');
                        
self::$DB = new DBmysql;
                        break;
                        
                    case 
'mssql':
                        include(
'DB'.$type.'.php');
                        
self::$DB = new DBmssql;
                        break;
                        
                    default:
                        include(
'DB'.$type.'.php');
                        
self::$DB = new DBmysql;
                        break;
                        
                } 
????
Tanax is offline  
Reply With Quote
Old 11-08-2007, 06:35 PM   #12 (permalink)
The Gregarious
Advanced Programmer Top Contributor Good Samaritan 
 
sketchMedia's Avatar
 
Join Date: Oct 2007
Location: Manchester, UK
Posts: 532
Thanks: 26
sketchMedia is on a distinguished road
Default

hey,

sorry i havent posted in a while, ive been busy.

anyway
bluesaga is correct about PHP5 and referencing, i do know that the new keyword returns a reference by default but i am unsure about returning from functions and i must admit i havent tested to see (if sum1 else knows please let me know :) ).

@Tanax i am unsure about your question, can u elaborate more?

just add to the switch for oracle (if thats what your question was :confused:)
__________________
sketchMedia is offline  
Reply With Quote
Old 11-08-2007, 07:49 PM   #13 (permalink)
The Gregarious
Upcoming Programmer Inquisitive 
 
Join Date: Sep 2007
Posts: 748
Thanks: 85
Tanax is on a distinguished road
Default

Quote:
Originally Posted by sketchMedia View Post
hey,

sorry i havent posted in a while, ive been busy.

anyway
bluesaga is correct about PHP5 and referencing, i do know that the new keyword returns a reference by default but i am unsure about returning from functions and i must admit i havent tested to see (if sum1 else knows please let me know :) ).

@Tanax i am unsure about your question, can u elaborate more?

just add to the switch for oracle (if thats what your question was :confused:)
Nono, that wasn't my question ;)

My question was that if I have several database classes, for instance mysql, mssql, and oracle.

Then I will have the switch, and creates objects of the class depending on what type.
But how will it create an object of a class, in ANOTHER file.

I have this DBfactory.php where the static factory method is.
Then I have a new file for each of the database types.

DBmysql.php
DBmssql.php
DBoracle.php

Which means, that I have to include them in order to create the object like so:
PHP Code:
new DBmysql(); 
And where do I include them?

Like this?
PHP Code:
switch($type) {
                    
                    case 
'mysql':
                        include_once(
'DB'.$type.'.php');
                        
self::$DB = new DBmysql;
                        break;
                        
                    case 
'mssql':
                        include_once(
'DB'.$type.'.php');
                        
self::$DB = new DBmssql;
                        break;
                        
                    default:
                        include_once(
'DB'.$type.'.php');
                        
self::$DB = new DBmysql;
                        break;
                        
                } 
Tanax is offline  
Reply With Quote
Old 11-08-2007, 08:37 PM   #14 (permalink)
The Gregarious
Advanced Programmer Top Contributor Good Samaritan 
 
sketchMedia's Avatar
 
Join Date: Oct 2007
Locati