/    Sign up×
Community /Pin to ProfileBookmark

OOPHP5 singleton database class

If i made my database class a singleton would I call it in the constructors of the classes it is required in?

Database class so far

[code=php]
<?php
class database
private $query;
private $results;

private static $database;

//connect to the database
private function __construct()
{
$this->link = mysql_connect($constants::DBHOST, $constants::DBUSER, $constants::DBPASS) or die(mysql_error());
mysql_select_db($constants::DBNAME) or die(mysql_error());
}

public static function singleton()
{
if (!isset(self::$database)) {
self::$database = new database();
}

return self::$database;
}

public function query($query)
{
$this->query = $query;
$this->execute();
}
//execute the mysql query
private function execute()
{
$this->results = mysql_query($this->query, $this->link) or die(mysql_error());
}

//protect against SQL injection attacks
public function antisql($data)
{
if (is_array($data))
{
foreach ($data as $name=>$value)
{
$data[$name] = mysql_real_escape_string($value);
}
}
else
{
$data = mysql_real_escape_string($data);
}
return $data;
}

//retrive the insert id
public function insertid()
{
return mysql_insert_id();
}

//return an array of results
public function returnArray()
{
return mysql_fetch_array($this->results);
}
}
[/code]

a quick knockup example of registration class

[code=php]
class registration
{

public function __construct()
{
$database = database::singleton();
}

public function checkunique($email)
{
$this->query(“SELECT * FROM `members` WHERE email=’$email'”);
}

public function add()
{
$this->query(“INSERT INTO `members` (`name`, `address`) VALUES (‘$name’, ‘$address’)”);
}
[/code]

to post a comment
PHP

37 Comments(s)

Copy linkTweet thisAlerts:
@chazzyMar 22.2008 — Well for one, based on how PHP works, having a singleton doesn't help you at all. Read through this bug report, PHP Bug 39946

The problem is that as the zend engine runs, it's only at request/response level. You could throw it into a session, but PHP stops you from putting DB connection info into session (since it requires a persistent connection and other hoopla). You can verify that a new instance is created each time by adding some kind of debug statement in the singleton method that would only be executed if it's creating a new instance.

As for your question at hand, the whole point to a singleton is that you only ever call the getInstance() method. getInstance() creates a new instance if needed, you should never be creating the instance in any other class.
Copy linkTweet thisAlerts:
@NogDogMar 22.2008 — @knowj: Yes, you can call it in each constructor as needed, and assign it to a class variable:
[code=php]
require_once 'database.class.php';

class example
{
private $db;

public function __construct()
{
$this->db = database::singleton();
}

public function example()
{
$result = $this->db->query('SELECT * FROM table');
}
}
[/code]


@chazzy: Singleton !== persistence. As you point out, PHP is not great at dealing with persistence, but a singleton can still be useful even in a non-persistent context.
Copy linkTweet thisAlerts:
@NogDogMar 22.2008 — PS: Here's a singleton class which extends the mysqli class, allowing you to use all the mysqli methods in a singleton context:
[code=php]
<?php
/**
* Database class to implement singleton pattern on top of mysqli
*/
class Database extends mysqli
{
/**
* @var object Singleton instance
*/
private static $instance = null;

// DB connection parameters:
private $dbHost = 'localhost';
private $dbUser = 'xxxxxx';
private $dbPwd = 'yyyyyy';
private $dbName = 'zzzzzzzz';

/**
* Constructor
* @return void
*/
private function __construct()
{
@parent::__construct(
$this->dbHost,
$this->dbUser,
$this->dbPwd,
$this->dbName
);
if(mysqli_connect_errno())
{
throw new Exception(
mysqli_connect_error(),
mysqli_connect_errno()
);
}
}

/**
* Do the singleton thing
* @return object Database
*/
public function getInstance() {
if(self::$instance === null)
{
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}

public function __clone()
{
throw new Exception("Cannot clone ".__CLASS__." class");
}
}
[/code]
Copy linkTweet thisAlerts:
@chazzyMar 22.2008 — oops, read your post wrong knowj. Nogdog's correct, you should be calling the singleton/getInstance method in the referencing classes.

as for the persistent/nonpersistent stance, it doesn't matter in this case anyways. the same connection will be returned in the context, as per this definition from PHP.net mysql_connect


If a second call is made to mysql_connect() with the same arguments, no new link will be established, but instead, the link identifier of the already opened link will be returned. The new_link parameter modifies this behavior and makes mysql_connect() always open a new link, even if mysql_connect() was called before with the same parameters. In SQL safe mode, this parameter is ignored.
[/quote]
Copy linkTweet thisAlerts:
@NogDogMar 23.2008 — Yeah, in normal usage (at least with the mysql functions) there's no big functional gain, but it may cut down on a bit of unnecessary processing. Besides, it's cool. ?
Copy linkTweet thisAlerts:
@knowjauthorMar 23.2008 — Is there anywhere that people discuss things on this level as information like this is hard to come by.

Most places you look for discussions on PHP people are asking why they are getting a parse error.

(Don't get me wrong if you search this forum I was asking them questions)

It's just helpful/interesting to discuss/see how people use these methods/techniques. Working with PHP4 held me back a huge deal looking back at the code i could be writing now.


Do you use mysqli_connect as the documentation i have read on this just says its pretty much pointless and you need to be really careful with it. Is it something for use on a dedicated rather than on shared host?

Again cheers for the information much appreciated.
Copy linkTweet thisAlerts:
@knowjauthorMar 23.2008 — 
[code=php]
require_once 'database.class.php';

class example
{
private $db;

public function __construct()
{
$this->db = database::singleton();
}

public function example()
{
$result = $this->db->query('SELECT * FROM table');
}
}
[/code]

[/QUOTE]


I assign results to an object within the database class via the $this->database->query("query"); function.

results become $this->database->results; (it is private and can only be accessed via methods within the database class.)

Could this become a problem?
Copy linkTweet thisAlerts:
@chazzyMar 23.2008 — I think most of the issue is that PHP, in its infancy, was not meant to be object oriented, but rather procedural and page/event driven. Since PHP's meant to be RAD (rapid application development) oriented, most people couldn't really care to delve deep into how PHP's meant to work to fine tune performance on it, rather all they care about is that it works. There's the site for the Object Oriented PHP book, http://www.objectorientedphp.com/ but they don't have a forum ?

What documentation are you reading that mysqli is pointless? MySQLi is a MySQL extension for PHP that works with MySQL 4.1.3 and up, providing an OO approach to MySQL, but still remaining functionally alive. The only reason you wouldn't want to use it, in my opinion, is if you have to support older MySQL versions.

Though on the other hand you have PDO, a PHP data abstraction layer that is both object oriented and database independent. http://us2.php.net/pdo
Copy linkTweet thisAlerts:
@NogDogMar 23.2008 — I assign results to an object within the database class via the $this->database->query("query"); function.

results become $this->database->results; (it is private and can only be accessed via methods within the database class.)

Could this become a problem?[/QUOTE]

Seems like that [i]could[/i] be a problem in a singleton context in the (probably rare) event that one object using the DB singleton object does a query, then a different object does a query and its result overwrites the first object's, and then the first object tries to do something with its query result and instead ends up using the 2nd query's result.

As far as mysqli_connect() goes, perhaps you are confusing that with mysql_[b]p[/b]connect()?

PS: There are at least a few more regular members over at webdeveloper.com's sister forum [url=http://phpbuilder.com/board/]PHPBuilder.com[/url] who discuss OOP and other more "advanced" issues. There are still a lot of "parse error" questions, but these are mostly in the Newbie forum there.
Copy linkTweet thisAlerts:
@knowjauthorMar 23.2008 — Yes I was thinking mysql_pconnect() that will teach me for writing a post late at night.

I will join over at phpbuilder.com and have a read through.

I have come across this issue with the duplicate but solved it in a bit of a bodge way by dropping $this->db->results into a page side variable. But now this is private this would not be possible. Would it be worth setting up a 2nd results object or would there be a method to create a duplicate.

Where i can see this being an issue would be when a loop within a loop is required. Category->SubCategory the results from SubCategory would write over the results from Category.

The issue would be how to handle this scenario :S
Copy linkTweet thisAlerts:
@NogDogMar 23.2008 — The way I would handle it is to have a separate result object/class which can be returned to whatever client process is executing the query. (This is what the mysqli->query() method does.) I did something like that [url=http://www.charles-reace.com/PHP_and_MySQL/MySQL_Class/]here[/url] in my formative days of learning OOP. Less OOP but still workable is simply to return the result resource ID from the mysql_query() call directly to the caller.

My preference now is simply to extend the mysqli (or PDO or PEAR?? class and use the result object built into that implementation.
Copy linkTweet thisAlerts:
@knowjauthorMar 23.2008 — Would it be worth me just using the mysqli extension then rather than messing about with what I currently have?
Copy linkTweet thisAlerts:
@NogDogMar 24.2008 — Well, it's certainly a good learning exercise, and I still use a version of my mysql class for situations where I'm stuck working in an environment where I have to use the "old" mysql functions. (There are still an amazing number of servers out there running PHP4.) But certainly the better direction when the opportunity is there is to use the new, object-oriented interfaces, particularly since they support prepared statements, and they let you get right into using them in your OOD application without having to reinvent the wheel.
Copy linkTweet thisAlerts:
@knowjauthorMar 24.2008 — Would you still have a database class when using mysqi?

Also if you have would you make it a singleton?

Sorry for being a pain and cheers for the help.
Copy linkTweet thisAlerts:
@NogDogMar 24.2008 — The mysqli extension supports usage as both a set of procedural functions or as OOP classes and methods. As my example in post #4 of this thread shows, it is a fairly simply matter to extend the mysqli class as a singleton class, and then simply use that extended class just as you would the mysqli class. (This is what I am currently doing on a project I'm working on.)
Copy linkTweet thisAlerts:
@jaegerschnitzelMay 08.2008 — I use your database class and I really like it

But I have one question

Is there a possibility to log all statements that are made with this class?

If so how could this look like? I hope you can help me!
Copy linkTweet thisAlerts:
@bokehMay 08.2008 — Why would you want to make your class a singleton anyway? It's just another global variable. Anyway mysql_connect is a singleton anyway so it doesn't matter how many times your script calls it you will only ever have one connection to any given DB.
Copy linkTweet thisAlerts:
@NogDogMay 08.2008 — Why would you want to make your class a singleton anyway? It's just another global variable. Anyway mysql_connect is a singleton anyway so it doesn't matter how many times your script calls it you will only ever have one connection to any given DB.[/QUOTE]
I use it so that I don't have to pass my DB object from object to object to object etc., when some of those objects may have no need for the DB object other than to pass it to an object that does need it. It's a trade-off between the convenience and cleaner interface provided by the global nature of a singleton weighed against the closer coupling it instills into the application. It's not right for every situation, but in some circumstances I find the simplicity and convenience wins out.
Copy linkTweet thisAlerts:
@NogDogMay 08.2008 — I use your database class and I really like it

But I have one question

Is there a possibility to log all statements that are made with this class?

If so how could this look like? I hope you can help me![/QUOTE]

I suppose you could overwrite the applicable method(s) to add the desired logging.
[code=php]
public function query($query, $resultmode = null)
{
parent::query($query, $resultmode);
// do whatever sort of logging you want to do hear
}
[/code]
Copy linkTweet thisAlerts:
@jaegerschnitzelMay 09.2008 — Sorry but I'm not well schooled in PHP and mySQL.

If I try to use your function I got the following problem:

Fatal error: Call to a member function fetch_row() on a non-object in D:http_roottobimodulescorefunktionen.php on line 56


[code=php]$db = Database::getInstance();
$sql = "SELECT id, LogOnUser FROM personaldaten WHERE LogOnUser = '$user' ";
$ergebnis = $db->query( $sql );
$zeile = $ergebnis->fetch_row();
return $zeile[0];[/code]



Can you help me? I don't know what exactly to do that my code works again and to log $sql with your db-class.

Tanks for your help!
Copy linkTweet thisAlerts:
@NogDogMay 09.2008 — Probably because I forgot you need to return something. This might work:
[code=php]
public function query($query, $resultmode = null)
{
$result = parent::query($query, $resultmode);
// do whatever sort of logging you want to do here, then...
return $result;
}
[/code]
Copy linkTweet thisAlerts:
@bokehMay 09.2008 — Singletons suck

Singleton controversy

I like this quote: "[I]It should be considered a violation of the Single Responsibility Principle of OO design to have an object responsible for its own singularity as well as its normal tasks[/I]".
Copy linkTweet thisAlerts:
@chazzyMay 09.2008 — Most languages use the locator concept when creating singletons. See for example: Spring framework for java.
Copy linkTweet thisAlerts:
@NogDogMay 09.2008 — Singletons suck[/quote]
Which ends with, "Do as the Jedi do and [i]use them with consideration and responsibly[/i] [my emphasis]." In other words, in spite of the sensationalist first sentence of that last paragraph, the author eschews blanket abolition of singletons and still leaves it to the designer/implementer to use his own judgment to determine when it may be appropriate to use them.

There are potential drawbacks to every non-trivial pattern you can conceive of using in software design (and I agree that singletons have definite drawbacks that [i]must[/i] be considered before using them). I am [i]not[/i], in fact, advocating the use of [i]any[/i] patterns, from singletons to MVC, without first understanding both their pros and cons, and then making the best decision you can for the specific situation being addressed at the time.
Copy linkTweet thisAlerts:
@jaegerschnitzelMay 10.2008 — @NogDog

thanks it works!

does this problem (described in #10) exist for your class of post #4?

if so how could I avoid it?



@bokeh

in your opinion what exactely is wrong in using singletons?

how is your db class arranged?
Copy linkTweet thisAlerts:
@NogDogMay 10.2008 — @NogDog

thanks it works!

does this problem (described in #10) exist for your class of post #4?

if so how could I avoid it?
[/quote]

I don't think that should be a problem, as you are only putting a wrapper around the mysqli class.

@bokeh

in your opinion what exactely is wrong in using singletons?

how is your db class arranged?[/QUOTE]

To quote Matt Zandstra in Chapter 9 of [i]PHP 5 Objects, Patterns, and Practice:[/i]

[INDENT]The problem is that the global nature of the Singleton lets a programmer bypass the lines of communication defined by class interfaces. When a Singleton is used, the dependency is hidden away inside a method and not declared in its signature. This can make it harder to trace the relationships within a system. Singleton classes should therefore be deployed sparingly and with care.

Nevertheless, I think that moderate use of the Singleton pattern can improve the design of a system, saving you from horrible contortions as you pass objects unnecessarily around your system.[/INDENT]
Copy linkTweet thisAlerts:
@jaegerschnitzelMay 10.2008 — ok I see it's mainly a problem of "clearly arranged code" and that singletons are complicated for testing purpose...

Another question:

Is this singleton class slower as other db connections particularly in situations with many db queries?


by the way is my english understandable? ;-)
Copy linkTweet thisAlerts:
@bokehMay 11.2008 — Is this singleton class slower as other db connections particularly in situations with many db queries?[/QUOTE]Using a class to access a function (mysql_connect for example) is always going to be slower than calling the function directly, singleton or otherwise. Comparing singleton access of the object compared to conventional access singleton would theoretically be slower because it involves an additional layer but you have to offset this against sending your connection to other objects by reference. In practice though I think you would see little or no difference even in a microsecond bench test. And don't forget, the lion-share of the time wasted is communicating with or waiting for the database server, not some nanosecond saved accessing a variable by an alternative method.


by the way is my english understandable? ;-)[/QUOTE]About as understandable as my Spanish. ?
Copy linkTweet thisAlerts:
@jaegerschnitzelMay 16.2008 — [code=php]public function query($query, $resultmode = null)
{
$result = parent::query($query, $resultmode);
$db = self::getInstance();
$query_bereinigt = mysql_escape_string($query);
$sql = 'INSERT INTO logging (UserID, SQLStatement) VALUES ("'.$_SESSION['UserID'].'", "'.$query_bereinigt.'" );';
$ergebnis = $db->prepare( $sql );
$ergebnis->execute();
return $result;
}[/code]



ok the logging is fine, but I only want to log INSERT, DELETE or UPDATE sql statements

how could I do that?
Copy linkTweet thisAlerts:
@jaegerschnitzelMay 18.2008 — no idea how I can manage this?
Copy linkTweet thisAlerts:
@NogDogMay 19.2008 — I can see two approaches (or maybe there's a way to combine them?):

  • 1. Use a string search to see what the query is, e.g.:
    [code=php]
    if(preg_match('/^s*(insert|delete|update)/i', $query))
    {
    // log it
    }
    [/code]

  • 2. Look at the return value type, as a successful SELECT or other query that returns a result set will return an object, while the insert/update/delete types will return a boolean true, plus any invalid query will return a boolean false and probably should be logged, too:
    [code=php]
    if(is_bool($result))
    {
    // log it
    }
    [/code]
  • Copy linkTweet thisAlerts:
    @NogDogMay 19.2008 — Another thought: to make the class more portable, perhaps it might be better to simply add an additional (perhaps optional) boolean parameter to the method to indicate whether or not logging should be done?
    Copy linkTweet thisAlerts:
    @jaegerschnitzelJul 16.2008 — @NogDog

    did you update your DB class ?

    if so can you please post your latest version?
    Copy linkTweet thisAlerts:
    @jaegerschnitzelSep 01.2008 — *push*
    Copy linkTweet thisAlerts:
    @NogDogSep 02.2008 — I mostly just use the built-in MySQLi class these days, possibly extending it if I need to add some functionality. I set up my application to create a MySQLi object, and then pass it to each class that needs to use it as part of its contructor. Thus there is no need for a singleton class nor any of the potential downside of using one.
    Copy linkTweet thisAlerts:
    @jaegerschnitzelOct 26.2008 — I set up my application to create a MySQLi object, and then pass it to each class that needs to use it as part of its contructor. [/QUOTE]

    Can you explain this more detailed?

    A code example would be great ?
    Copy linkTweet thisAlerts:
    @NogDogOct 26.2008 — Can you explain this more detailed?

    A code example would be great ?[/QUOTE]


    [code=php]
    class Example
    {
    private $db;

    public function __construct(mysqli $db)
    {
    $this->db = $db;
    }

    public function doSomething()
    {
    $result = $this->db->query('SELECT * FROM table');
    }
    }

    // USAGE:
    $dbObj = new mysqli('localhost', 'foo', 'bar', 'database');
    $test = new Example($dbObj);
    $test->doSomething();
    [/code]
    ×

    Success!

    Help @knowj spread the word by sharing this article on Twitter...

    Tweet This
    Sign in
    Forgot password?
    Sign in with TwitchSign in with GithubCreate Account
    about: ({
    version: 0.1.9 BETA 5.20,
    whats_new: community page,
    up_next: more Davinci•003 tasks,
    coming_soon: events calendar,
    social: @webDeveloperHQ
    });

    legal: ({
    terms: of use,
    privacy: policy
    });
    changelog: (
    version: 0.1.9,
    notes: added community page

    version: 0.1.8,
    notes: added Davinci•003

    version: 0.1.7,
    notes: upvote answers to bounties

    version: 0.1.6,
    notes: article editor refresh
    )...
    recent_tips: (
    tipper: @AriseFacilitySolutions09,
    tipped: article
    amount: 1000 SATS,

    tipper: @Yussuf4331,
    tipped: article
    amount: 1000 SATS,

    tipper: @darkwebsites540,
    tipped: article
    amount: 10 SATS,
    )...