Note: |
At this point in time, you really should be using the MySQLi or the PDO module to access your database. Any host with PHP and MySQL should offer them and they both already contain most of the functionality this wrapper gave the old mysql_* commands. |
Description
- This is not a full script. This is the PHP singleton class (PHP5 specifically) that I use to make all the MySQL database calls in my projects. I run all my MySQL connections through it because it saves me time and space on my code. I don't have to keep typing the database information and other things like that. It also has shortcut functions because I got tired of escaping data, stripping slashes, building UGLY insert and update queries. I'm sharing this class along with some examples for using it.
What is a Singleton and why use it?
- A Singleton in PHP allows you to create a single object ($db) and reuse that same existing object anywhere in your code.
- This allows you to easily reuse the same database connection inside of other functions/classes. Without a singleton, your options would be to either declare the $db object as a global inside every function, or pass the $db object into the function via a parameter. A Singleton object eliminates all this.
Download
v3.1.4 ZIP with all files (Database.singleton.php, sample config, sample how to call, copy of license, examples of queries)
- v3.1.4 Quickview Database.singleton.php (already included in above zip)
- v3.5 (MySQLi and PHP 7.4 compatible version) Quickview or
ZIP (see discussion in comments below)
Requirements & Terms of Use
- This script requires PHP5.0 or higher. If you only have PHP4, you need to use the old PHP MySQL Wrapper v2.
- All current v3 of this script are released under GNU General Public License. Basically this means you are free to use the script, modify it, and even redistribute versions of your own under the same license.
What's new
- 3.1.4 [2010-07-19] [credits] Removed if($pass==null) check in constructor incase logging into mysql with blank password
- 3.1.3 [2010-05-09] First public release. There are older v3 versions, but they were not publicly available
- Changed from v2
- Minor: Ported to PHP5 supported OOP (meaning private, public, "__construct" etc)
- Major: Turned the class into a singleton
- Major: Renamed several functions: fetch_array() to fetch(), fetch_all_array() to fetch_array(), query_update() to update(), query_insert() to insert()
- Minor: Added $debug=false; to allow suppression of oops() error messages
- Minor: Completely stripped out support for table prefixes
Instructions
Calling the Class and configuring the settings
- At the top of your scripts you need to create the initial $db object and pass it all the MySQL server parameters:
// bring in the class file
require("Database.singleton.php");
// create the initial singleton database object
$db = Database::obtain("server_name", "mysql_user", "mysql_pass", "mysql_database"); - What each setting means:
- $db = Database::obtain is how you are going to declare and access the singleton object. You are probably used to declaring an object like $db = new Database however, we are not creating a traditional object. Also note many people like to use the naming convention ::getInstance() instead of ::obtain() and you are welcome to change your version. However, I personally like ::obtain() so it's used here.
- server_name - Name or IP of MySQL server. Usually "localhost" although sometimes it will be like "db1337.perfora.net"
- mysql_user - User Name to log into MySQL database
- mysql_pass - Password to log into MySQL database. If no password, use double quotes with a blank value. Example ""
- mysql_database - Database to select and run the queries on
- What I HIGHLY recommend (optional)
The easiest way to handle this is to create a config.inc.php file. In it put://database server
define('DB_SERVER', "localhost");
//database login name
define('DB_USER', "username");
//database login password
define('DB_PASS', "password");
//database name
define('DB_DATABASE', "username_mydata");
//smart to define your table names also
define('TABLE_USERS', "users");
define('TABLE_NEWS', "news");
Then at the top of your scripts, call in the config.inc.php and the Database.singleton.php files and then create the $db objectrequire("config.inc.php");
require("Database.singleton.php");
$db = Database::obtain(DB_SERVER, DB_USER, DB_PASS, DB_DATABASE);
The reason why this is a good idea is because then if you change hosts etc, you can edit the single config.inc.php file, change the settings, and it is reflected on all your pages. No need to change the info on each of your pages.
- Any place (other than the initial declaration) you want to use the existing object in your script:
// grab the existing $db objectYou do not need to pass it any of the database info (user/pass/server) except the very first time when you create the $db object.
$db=Database::obtain();
Turning debug on/off
- Near the top of Database.singleton.php setting there is a public $debug = true; setting. Setting this to false will suppress any error messages triggered by oops(). It's there to help with security, but I recommend leaving it on until you are absolutely sure everything is working correctly.
- It would also be possible to turn debugging on (or off) for a specific page by calling $db->debug = true; up at the top of the page right after you initially create the $db object.
Examples of how to call things
Code:
// every page needs to start with these basic things
// I'm using a separate config file. so pull in those config values
require("config.inc.php");
// pull in the file with the database class
require("Database.singleton.php");
// create the $db singleton object
$db = Database::obtain(DB_SERVER, DB_USER, DB_PASS, DB_DATABASE);
// connect to the server
$db->connect();
#####
// your main code would go here
// with the singleton you can use the $db object inside other classes or functions
function get_user_exists($user){
// get the already existing instance of the $db object
$db = Database::obtain();
$sql="SELECT `uid` FROM `account` WHERE `user`='".$db->escape($user)."'";
$row = $db->query_first($sql);
// if user exists
if(!empty($row['uid']))
return true;
else
return false;
}
#####
// good practice to close the connection when finished
// however, PHP will auto-close it when the page ends if you forget
$db->close();
Code:
// update an existing record using update()
$data['comments'] = 3536;
$data['title'] = "New Article";
// special cases supported for update: NULL and NOW()
$data['author'] = "NULL";// it knows to convert NULL and NOW() from a string
// also supports increment to the current database value
// will also work with a negative number. eg; INCREMENT(-5)
$data['views'] = "INCREMENT(1)";
// update() parameters
// table name (ideally by calling a constant defined in config file)
// assoc array with data (does not need escaped)
// where condition
$db->update(TABLE_NEWS, $data, "news_id='46'");
// would create the query:
// UPDATE `news` SET `comments`='3536', `title`='New Article',
// `author`=NULL, `views`=`views` + 1 WHERE news_id='46'
Code:
// insert a new record using insert()
$data['news_id'] = 47;
$data['title'] = "You're Top"; // insert() will auto escape it for us
$data['created'] = "NOW()";// it knows to convert NULL and NOW() from a string
// insert() parameters
// table name (ideally defined as a constant, but did not for this example)
// assoc array with data (does not need escaped)
// insert() returns
// primary id of the inserted record. you can collect or ignore
$primary_id = $db->insert("news", $data);
// then use the returned ID if you want
echo "New record inserted: $primary_id";
// would create the query:
// INSERT INTO `news` (`news_id`,`title`,`created`)
// VALUES ('47', 'Your\'re Top', NOW())
Code:
// escape() query() and fetch()
// pullout the first 10 entries where referrer came from google
// using defined TABLE_USERS table name from config
// $db->escape() escapes string to make it safe for mysql
$url = "http://www.google.com/";
$sql = "SELECT user_id, nickname FROM `".TABLE_USERS."`
WHERE referrer LIKE '".$db->escape($url)."%'
ORDER BY nickname DESC
LIMIT 0,10";
$rows = $db->query($sql);
while ($record = $db->fetch($rows)) {
echo "<tr><td>$record[user_id]</td>
<td>$record[nickname]</td></tr>";
}
Code:
// using escape() and fetch_array()
// pullout the first 10 entries where url came from google
// using defined TABLE_USERS table name from config
// $db->escape() escapes string to make it safe for mysql
$url = "http://www.google.com/";
$sql = "SELECT user_id, username FROM `".TABLE_USERS."`
WHERE referer LIKE '".$db->escape($url)."%'
ORDER BY username DESC
LIMIT 0,10";
// feed it the sql directly. store all returned rows in an array
$rows = $db->fetch_array($sql);
// print out array later on when we need the info on the page
foreach($rows as $record){
echo "<tr><td>$record[user_id]</td>
<td>$record[username]</td></tr>";
}
Code:
// using query_first()
// get user's nickname using their unique ID
// using defined TABLE_USERS table name from config
$sql = "SELECT username FROM `".TABLE_USERS."`
WHERE user_id=$user_id";
// since user_id is unique, only one record needs returned
// I use $db->query_first() instead of $db->query() and fetch()
// $db->query_first() will return array with first record found
$record = $db->query_first($sql);
// since it only returns one record, query_first() does the fetching
// I can print off the record directly
echo $record['username'];
// query_first() is also good for things like
$sql = "SELECT COUNT(*) AS amount FROM `".TABLE_NEWS."` WHERE `comments` >= 50";
$row = $db->query_first($sql);
echo $row['amount'] ." articles have 50 or more comments";
Code:
// delete a specific entry
$sql = "DELETE FROM `".TABLE_USERS."` WHERE `user_id`=$user_id";
$db->query($sql);
Code:
// using $db->affected_rows
// returns the number of rows in a table affected by your query
// can be used after UPDATE query (to see how many rows are updated)
// can be used after SELECT query (to see how many rows will be returned)
$sql = "SELECT `username` FROM `".TABLE_USERS."` WHERE `user_status`='1'";
$row = $db->query($sql);
if($db->affected_rows > 0){
echo "Success! Number of users found: ". $db->affected_rows;
}
else{
echo "Error: No user found.";
}
Comments
July 7, 2010 - 4:37pm — Anonymous
Bug: Error with multiple connections
When trying to follow the example for connecting to two databases I get the following error
WARNING: No link_id found. Likely not be connected to database.
Could not connect to server: .
If I comment each one out individually it connects fine, but it seems adding the second connection is throwing an error. Any suggestions would be appreciated. Great script so far though.
July 8, 2010 - 2:32pm — ricocheting
Answer - Using multiple connections with a singleton
You really shouldn't really use a singleton if you need to handle multiple connections from the same page. However, you can use the v3 class as a normal object with a few changes to Database.singleton.php file. Edit it and change
Then you can create your database objects the old-school way like:
July 15, 2010 - 12:15am — Anonymous
Bug: In the case of no password
While using a blank password isn't best practice, the documentation suggests:
If no password, use double quotes with a blank value. Example ""
But the singleton has a check in the constructor for a null password that will generate a big nasty oops.
Thanks!
July 19, 2010 - 11:01pm — ricocheting
Version Updated
Good catch. Version has been updated to 3.1.4 to include this fix. If you want a name/nickname in your credits, let me know.
October 7, 2010 - 2:43pm — Anonymous
Suggestion: Improvement connect()
This is some modification :
December 19, 2010 - 5:32pm — dengine
Where to put the $db in a new class
Hey ricocheting,
Great Singleton class. I'm just starting to rewrite my codes using classes and this Singleton looks very promising. Your explanation is very clear and so are the examples. I do have one question though. You say if you need to call the $db object in a function or class you can do this by using: $db=Database::obtain(); I have the following class example:
I get an error when calling the $db this way. If i put the $db object inside the get_images function it works correct. Is there a way to put the $db at the top of the class so I can use it through the entire class or is that bad practice? I hope you can clear things up for me.
BTW, the error I'm getting is:
PHP Parse error: syntax error, unexpected T_VARIABLE, expecting T_FUNCTION in /Users/dengine/Documents/myWebserver/910_develop/classes/kp_classes/images.class.php on line 6
Many thanks in advance!
Jerome
December 20, 2010 - 1:34pm — ricocheting
Answer
You generally put $db=Database::obtain(); inside each function that interacts with the database. it's made to behave that way and it would be fine. If you really want to use it just once in the class, you can declare it in an object contructor (which gets called when you create the Images object) then use $this->db->whatever to call it.
January 13, 2011 - 5:56pm — Anonymous
set connection charset to UTF8
Great class! However, I need to set the charset to UTF8 after connecting to mysql for a specific job. Normally, you'd set it after mysql_connect with mysql_set_charset('utf8',$db);
I'm not exactly fluent in singleton OOP, how can I achieve this within the class?
cheers and thanks,
peter
January 22, 2011 - 2:40pm — Aquaguy
UTF8
Add @mysql_set_charset('utf8',$this->link_id); to the connect() function.
June 2, 2011 - 4:44am — Demeus
paging
I would like so that the class was provided paginal division
June 5, 2011 - 6:25pm — ricocheting
Answer
the problem is that short of running the query twice (once to get results count, once to get the actual results with a modified query with a limit) I've never found a good way to do it. I'm open to suggestions from anyone and would love to include something like that.
October 25, 2011 - 5:22pm — jeffrydell
Suggestion: Check for a db connection
Rather than just trusting that I'm connected to my db after running the two includes, creating the object, and issuing $db->connect(), I needed some kind of test that would throw an error to the end user should the connection attempt fail.
So I whipped up this little gem, maybe you can include it in a future version?
November 11, 2011 - 11:02pm — Anonymous
adding floats in update/insert statement
Quick question: When using increment option to insert/update statement and I have decimal numbers like 32.50 it always resets the database field to 0.00, but if I use 32 than it's fine:
Resets field to 0.00
$val = -16.32;
$data['amount_due'] = "increment($val)";
$db->update("content",$data,"id = 1");
Works fine
$val = -16;
$data['amount_due'] = "increment($val)";
$db->update("content",$data,"id = 1");
How can I use the first example? Thanks
November 14, 2011 - 6:40pm — ricocheting
Answer
The increment() doesn't currently support decimals/floats. You'll need to change a line in the function update() from
elseif(preg_match("/^increment\((\-?\d+)\)$/i",$val,$m)) $q.= "`$key` = `$key` + $m[1], ";
toelseif(preg_match("/^increment\((\-?[\d\.]+)\)$/i",$val,$m)) $q.= "`$key` = `$key` + $m[1], ";
I'll make that fix in a future version and give you credit.
March 28, 2012 - 6:40am — Anonymous
Suggestion: adding INET_ATON
Class is great, but i had problems with inserting INET_ATON ip addresses.
So i made quick update of update function:
TASK: $data['lastIP'] = "INET_ATON(".$_SERVER['REMOTE_ADDR'].")";
April 9, 2012 - 2:10pm — Anonymous
Field Names as Variables
Hi. I've been using your DB class for a LONG time now and love it!
I do have a question though: I have a table (called tbl_globals) where I store several site-wide variables (which are echoed throughout the site and on more than one occasion). They are variables such as org's phone number, site's name, etc. This particular table only has one row (and no additional rows are ever added)
In the past I have queried the table in the database and assigned a variable to each resulting value, which is fine. I have a script that has the query and then a variable for each returned value.
Is there a way that I can use the table's field name AS the variable name (and its value as the value)? I know variable variables can do this, as well as the foreach loop, but I have been unsuccessful in getting this to work with your class.
Any help would be appreciated!
April 10, 2012 - 8:58pm — ricocheting
Answer (global site config settings from database)
Specifically for what you're asking, you'd probably have to use eval() like:
However, I would recommend using PHP's define() to set your site-wide variables as constants. Something like:
April 16, 2012 - 12:24pm — Anonymous
Use on Big website
how much use of this class is advised for a large website ? eg. high traffic website ?
April 16, 2012 - 2:54pm — ricocheting
Answer
It really depends on how big is "high traffic?" If your database is on a single server, yes this class will work well.
However, if you use multiple database machines (or plan on upgrading to multiple db machines in the future) then you should use a database script written to handle multiple servers. Not necessarily because this class is inefficient, but because handling multiple master/slave connections and queries requires something different.
The only efficiency caution I have (this applies to everyone) is:
August 25, 2012 - 1:59pm — Anonymous
mysqli
I've used your wrapper some time now and are pleased with it. Now I'm thinking about using mysqli and prepared statements. Any change your wrapper will support this in the future?
August 28, 2012 - 11:01am — ricocheting
Answer
It is possible, but unlikely to happen until the mysql_* commands are depreciated (right now they're just "not recommended" in favor of MySQLi or PDO). After which I might make a MySQLi version so people using the wrapper can move to MySQLi without redoing their code. However, I will most likely stop development at that point.
If you have access to them, I would recommend you directly use MySQLi (or PDO) as they already have most of the functionality this wrapper gave the old mysql_* commands.
October 29, 2018 - 8:15am — Mark
Anything new on recoding for
Anything new on recoding for the use with mysqli_*()?
October 30, 2018 - 2:32pm — Justin Anderson
I just converted the script to use mysqli_* functions
Justin updated the old class to use MySQLi functions and kindly pasted the code here. You can view/download Justin's changes. Everything should be fully backwards compatible with the old version.
November 3, 2018 - 9:22pm — ricocheting
Thank you!
Very nice job. Appreciate you putting in the time and work to convert this and then sharing it with all of us. Also, if anyone wants to take this a step further, you could also go:
and eliminate some of the redundancies for functions MySQLi already handles. It would also allow you to inherit all the other MySQLi functions and use them within your $db Database object. eg;
December 13, 2018 - 2:36pm — Justin Anderson
You are very welcome
You are very welcome! I had to upgrade PHP and MySQL on our company server and my PHP framework I wrote used your database class, so I ported it over after seeing that it hadn't been done on here yet, I posted for all to see and use. Thanks for providing such a great database class to use.
August 25, 2012 - 4:40pm — Anonymous
Delete function
Just cosmetic modification:
May 31, 2013 - 7:46am — Koen Thomeer
NULL
a proposition for inserting/updating NULL values in version 3.1.4
line 203:
if(strtolower($val)=='null'||$val == NULL) $q.= "`$key` = NULL, ";
line 225:
if(strtolower($val)=='null'||$val == NULL) $v.="NULL, ";
May 31, 2013 - 1:46pm — ricocheting
Possible implementation