Home » Php » Using a single database connection script for multiple domains – PHP

Using a single database connection script for multiple domains – PHP

Posted by: admin February 23, 2020 Leave a comment

Q(Question):

Hello everyone and happy Sunday. 🙂

Can anyone advise if using a single database class for a single domain and 1 subdomain is the correct way to go? I have:

mydomain.com (and)
sub.mydomain.com

I would like to use a single script to connect to the database. Is this advisable? The subdomain is the private side and the domain is the public side.

Thanks,

Frank

A(Answer):

bump.

Should have started a new post.. I’m losing my mind. 🙂

A(Answer):

im guessing that your pages are protected in some way?

if you do wouldn’t it just require you to use the include statement? using it after the page security would mean access control would have to be passed before being able to see that page

A(Answer):

Could you please tell us more on your public side and private side. basically your domain and subdomain is available for public access right? In other words can anyone access http://sub.yourdomain.com. ?

In generally subdomain is nothing but its again another dir in public_html dir (I’m talking about apache on linux). if you really want to put your secure files out side the public_html folder, you can store them in a top level derectory of your server. then public users or programs can’t access those files. but Php-Engine will be able to read the top level dir s on the server.

-Thanks

A(Answer):

Could you please tell us more on your public side and private side. basically your domain and subdomain is available for public access right? In other words can anyone access http://sub.yourdomain.com. ?

In generally subdomain is nothing but its again another dir in public_html dir (I’m talking about apache on linux). if you really want to put your secure files out side the public_html folder, you can store them in a top level derectory of your server. then public users or programs can’t access those files. but Php-Engine will be able to read the top level dir s on the server.

-Thanks

Hi and thanks everyone for the responses. I will explain what I have done and if someone sees a security issue, please let me know.

mydomain.com is the public access side and then I have admin.mydomain.com which is the private side. The public side uses a database connection script as well as the private side.

I have a login box on the public side and have decided to log the user into the private side using the public side database script. Once the user is on the private side, the private side database script takes over. I just wanted to keep all the queries on the public side seperate from the private side. Is this a good practice?

There are no real "files" to move, it is just a private site with normal html however, only people that are authorized to be there should have access.

A(Answer):

Heya, Frank.

Generally, you should be able to re-use the same class because if you designed your DBAL properly, it won’t have any domain-specific code in it.

The trick comes in when you want to use different login credentials (perhaps the private domain has INSERT and DELETE privileges on certain tables that you don’t want the public domain to be able to run). But all you need to do there is supply a relative path to the credentials.

E.g.,:


$dbCredentials = require_once "{$_SERVER['DOCUMENT_ROOT']}/../includes/dbCredentials.php";
$db = new database($dbCredentials);

Or something to that effect. Since $_SERVER[‘DOCUMENT_ROOT’] will be different depending on the subdomain, you can use the same code, but it will generate different results per subdomain.

A(Answer):

Hey Pbmods,

Thanks for the response. Please picture what I am trying to do here. I have a main domain and a sub domain. The main domain is public and the sub is private and is where my app will reside.

I have a login box on the PUBLIC side which used the DBAL script that we were discussing the other day. I have all of my calls to the dbal class in my process script as you and Atli suggested I do. Works great by the way (Thank you! 🙂 )

The problem arose the other day when I implemented a session store. My process script is the default page which starts the initial session.

I quickly learned that the session that is initiated is the session used for the duration of the visit. That being said, this login box is the authentication method into the sub domain.

Now, the public side uses a dbal also and I was thinking that I should probably only be using 1 dbal script for both sides, then that didn’t seem like it would be secure plus why would I want queries executed from the app whilst the public queries were running off the same script. For some reason it just doesn’t sit well with me. I am really concerned about security on this one.

What I did was use the dbal on the app (private) side to log the user into the subdomain from the public side.

So to recap, user logs into private side from public side using dbal from Private side. dbal on public side starts the session store and runs the queries for the public side.

If you say I should be using 1 script, I will take your suggestion and rework it to use 1 dbal. What are your feelings?

Thanks for the help and direction Pbmods!

BTW: Sorry I didn’t have a chance to check back sooner, I kind of got tied up and it slipped my mind.

Frank

A(Answer):

When you are require()’ing files in PHP, PHP doesn’t particularly care where the files are located. You can put a file in one location and access it from a completely different one (technically, you could even host the file on a totally separate server, but let’s not go into orbit here).

As long as your DBAL script is generic enough that it could run on *any* domain, you only need one. The instant you start putting domain-specific code in there, you lose that flexibility.

So for example, if you are including queries or credentials in your DBAL script, you’ll need a separate one for each domain.

If, on the other hand, you created a DBAL script in a common location that *only* implemented (rough example here:) a connect(), disconnect() and query() method and then stored the queries themselves in separate file on each domain, you could make this setup work quite nicely and only have to maintain a single DBAL script.

So you might have a setup something like this:/webroot/common/includes/dbal.php /webroot/common/includes/session.php /webroot/subdomains/public/includes/sess_public.php /webroot/subdomains/private/includes/sess_private.php
dbal.php would contain your generic DBAL:


class DBAL
{
public function __construct( $hostname, $username, $password, $database ) { ... }
public function connect( ) { ... }
public function disconnect( ) { ... }
public function query( $sql ) { ... }
}

session.php would probably be abstract or an interface:


interface Session
{
public static function start( );
public static function stop( );
}

And then you could put your subdomain-specific code in the session.php file for each subdomain:


require_once '/webroot/common/includes/session.php';
class Session_Public implements Session
{
public static function start( ) { ... /* Login stuff for public subdomain. */ ... }
public static function stop( ) { ... }
}

And a similar file for your private subdomain.

This way you can consolidate all your generic code into a single file while still retaining the flexibility to customize your app’s behavior based on the subdomain.

A(Answer):

If, on the other hand, you created a DBAL script in a common location that *only* implemented (rough example here:) a connect(), disconnect() and query() method and then stored the queries themselves in separate file on each domain, you could make this setup work quite nicely and only have to maintain a single DBAL script.

Ok, this is exactly what I currently have. I am using two completely seperate databases though. One database is for the public side and the other is for the private side.

Actually.. you’ve inspired me. I am going to put the "common" classes in a common location and try and follow your map above. I will post back with any questions. 🙂 Thanks Pbmods!

A(Answer):

There’s two ways that I would approach this situation.

You could create a factory class (I’m a particular fan of this one, especially combined with a singleton DBAL connection):


class DB
{
const
DB_PUBLIC = 'publicDBName',
DB_PRIVATE = 'privateDBName';
public static function getDB( $database = self::DB_PUBLIC )
{
switch( $database )
{
case self::DB_PUBLIC:
case self::DB_PRIVATE:
return new DBAL('hostname', 'username', 'password', $database);
break;
default:
/** Error condition. */
break;
}
/* Or:
switch( $database )
{
case self::DB_PUBLIC:
$username = '...';
$password = '...';
break;
case self::DB_PRIVATE:
$username = '...';
$password = '...';
break;
}
if( isset($username) and isset($password) )
{
return new DBAL('hostname', $username, $password, $database);
}
*/
}
}

And then simply define your DB_NAME on a per-subdomain basis:

On the public subdomain, you might do something like this:


define('DB_NAME', DB::DB_PUBLIC);

And on the private subdomain, you might do something like this:


define('DB_NAME', DB::DB_PRIVATE);

Anywhere you need to create a new DB connection, you could then simply use this code:


$db = DB::getDB(DB_NAME);

Now this works reasonably well, but you would be putting domain-specific code in a common file, which might not be desirable depending on how you want to be able to scale your application.

An alternative approach would be to simply create a credentials file on each subdomain. For example, the public one might contain a getDB.php that looks like this:


return
new
DBAL
(
'public_hostname'
, 'public_username'
, 'public_password'
, 'public_database'
);

You might also want to consider making DBAL into a singleton, but that is beyond the scope of this thread.

A(Answer):

Hmm.. Ok, I see what you are doing I think. I never thought about using a switch for that. Oh… your good Mr. Pbmods. 🙂

What I did since my last post was put the DBAL and session scripts into a common dir under my docroot and changed the inc path, then deleted the second DBAL script I was using. I could not connect afterwards, so I modified the DBAL constructor to accept the DBName of the script calling it then it worked.

Let me ask you.. What would the advantage be of using a switch and DEFINE in the scripts to call the DBAL over the way I have it now?

Thanks PBmods!

EDIT:
I think I can answer my own question. Correct me if I am wrong, but I could just define the DBAL at the top of the script and go. Instead of changing the DBName (the way I currently have it) wherever and however many times I am instantiating the object. Right?

A(Answer):

This is correct, Mr. Fjm.

Incidentally, you might also find this useful if you want to have a separate development database. This way you can test features and fix bugs without affecting your production data.

You might end up with something like this:


switch( $database )
{
/** Most common one goes first b/c switch stops as soon as it hits a match. */
case self::DB_PUBLIC:
break;
case self::DB_PRIVATE:
break;
case self::DB_PUBLIC_DEBUG:
break;
case self::DB_PRIVATE_DEBUG:
break;
}

It’s a lot easier to keep track of your DB credentials when you only have to maintain them in one location.

A(Answer):

I think I may have hit a bug that I don’t know how to fix. I don’t know where or why but after I consolidated the DBAL scripts, I now have a mysql db error at the bottom of the page. "table site.sessions doesn’t exist". The sessions table resides on the "app" database and not on the "site db.

I have this call at the top of my script for the session store:
[PHP]require_once("../common/inc/db.php");
$db = new Database(‘app’);
[/PHP] Then I have this below in a switch for the public website:
[PHP]$db = new Database(‘site’);
$id = (int) $db->QuoteSmart($id);[/PHP] The "app" and "site" are the dbs that are being passed to the constructor.
These are the only two calls in the script and the session table is being updated so I am not quite sure what is happening here. Anyone care to take a stab?
Here are the relavent lines in the db class:
[PHP] private $_LinkID;
public $Query;
public $Row;
public $NRows;
public $Record;

public function __construct($dbName)
{
$this->LinkID = null;
$this->Query = "";
$this->Row = 0;
$this->NRows = 0;
$this->Record = array();
$this->Connect(‘localhost’,’dev’,’password’);
$this->SelectDB($dbName);
}

public function Connect($host,$user,$pass)
{
$this->_LinkID = mysql_connect($host,$user,$pass) or die(mysql_error());
return $this->_LinkID;
}

public function SelectDB($dbName)
{
mysql_select_db($dbName);
return true;
}[/PHP]

Thanks,

Frank

A(Answer):

Are you using a custom session handler (via session_set_save_handler() (http://php.net/session_set_save_handler))?

Somewhere in your ‘site’ code, there’s a query run on the session table (perhaps in a common include file somewhere? The login/logout script, perhaps?).

A(Answer):

Are you using a custom session handler (via session_set_save_handler() (http://php.net/session_set_save_handler))?

Somewhere in your ‘site’ code, there’s a query run on the session table (perhaps in a common include file somewhere? The login/logout script, perhaps?).

Yes Pbmods, I am using session_set_save_handler.

I have looked and looked. I cannot find anything where the session store is using the "site" DBAL.

Is there a debugger or something I can use? Maybe something that will step me through what is happening so I can where it is messing up?

A(Answer):

What’s your session write function look like? Are you storing session data in the database?

You might need to create a separate instance of Database specific to the ‘site’ db inside of your session handling functions/class.

A(Answer):

What’s your session write function look like? Are you storing session data in the database?

Yes, sessions are being stored in the database.

[PHP] public function write($id, $data)
{
$time = time() + $this->life_time;
$newid = mysql_real_escape_string($id);
$newdata = mysql_real_escape_string($data);
$sql = "REPLACE sessions(session_data,expires,session_id) VALUES(‘$newdata’, ‘$time’, ‘$newid’)";
$rs = $this->_LinkID->Query($sql);
return TRUE;
}[/PHP] Pbmods.. I have been at this for quite a while. I just can’t figure it out. I was thinking that maybe my dbal script was not correct. Please let me know what you think.

Thanks,

Frank

A(Answer):

Where is $this->_LinkID initialized?

A(Answer):

Where is $this->_LinkID initialized?

In the constructor of sessions.php. It is being passed from the action/process script which is my default index page from the docroot.

I posted an example of that above. Here it is again:
This is for the sessions in the index page
[PHP] require_once("../common/inc/db.php");
$db = new Database(‘app’);
require_once("../common/inc/sessions.php");
$sess = new SessionManager($db); session_start();
[/PHP]
This is for the site, also in the index page:
[PHP] $db = new Database(‘site’);
$id = (int) $db->QuoteSmart($id);
require_once("lib/classes/page.php");
$page = new Page($id,$db);
[/PHP]

A(Answer):

Ah. Okay, I see what the problem is.

In Database::SelectDB():


public function SelectDB($dbName)
{
mysql_select_db($dbName);
return true;
}

Note that mysql_* methods will operate on the last-opened connection unless you specify a connection resource.

In order to keep your connections separate, you need to do this:


public function SelectDB($dbName)
{
mysql_select_db($dbName, $this->_LinkID);
return true;
}

And similarly for Database::Query().

A(Answer):

Ok, I have made those changes, but the same error is appearing.

Here is the db.php after the changes:

[PHP] private $_LinkID;
public $Query;
public $Row;
public $NRows;
public $Record;

public function __construct($dbName)
{
$this->LinkID = null;
$this->Query = "";
$this->Row = 0;
$this->NRows = 0;
$this->Record = array();
$this->Connect(‘localhost’,’cindy’,’password’);
$this->SelectDB($dbName);
}

public function Connect($host,$user,$pass)
{
$this->_LinkID = mysql_connect($host,$user,$pass);
return $this->_LinkID;
}

public function SelectDB($dbName)
{
mysql_select_db($dbName, $this->_LinkID);
return true;
}

public function Query($sql)
{
$this->Query = mysql_query($sql, $this->_LinkID);
return $this->Query;
}[/PHP] I was thinking it was exactly the same thing but I guess not.. Pbmods, do you think I would be better off maybe putting the two dbal scripts back the way they were before? I’m truly lost on this one.

A(Answer):

Perhaps $db is getting overwritten? Try renaming $db in one of your files:


require_once("../common/inc/db.php");
$sess_db = new Database('app');
require_once("../common/inc/sessions.php");
$sess = new SessionManager($sess_db); session_start();

A(Answer):

Ok, I changed the "site" calls from $db->… to $db1->… and I am still gettint the error. I do however think you are on the right track with the db class. I just don’t have the knowledge to know exactly what to change.

A(Answer):

Hm.

Try adding this to your Database class:


public function Query($sql)
{
$bt = debug_backtrace();
echo '<pre>', $sql, '</pre> (Called from ', $bt[0]['file'] . ':' . $bt[0]['line'], ')<br />';
$this->Query = mysql_query($sql, $this->_LinkID);
return $this->Query;
}

I think you want $bt[0]. Might be $bt[1]; I can’t remember.

A(Answer):

ok, thanks for the help pbmods. Here is the output:

SELECT session_data FROM sessions WHERE session_id = ’11r84vpddpvvnh31lrn5e8m6t6′ AND expires > 1215405874
(Called from C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\common\inc\sessions.ph p:50)
SELECT t1.title FROM site AS t1 WHERE t1.page_id = 1
(Called from C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\cadetdispatch\lib\clas ses\page.php:49)
REPLACE sessions(session_data,expires,session_id) VALUES(”, ‘1215407315’, ’11r84vpddpvvnh31lrn5e8m6t6′)
(Called from C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\common\inc\sessions.ph p:66)

A(Answer):

That last one is what’s causing the error?

A(Answer):

Yes. It is the last one.

Notice that the select statement above that has a linkid for "site" and the select statement (last one) uses a linkid for the "app". For some reason the db script isn’t switching back from the site to the session script.

A(Answer):

Only thing that comes to mind is the use of mysql_real_escape_string() and possibly $db->QuoteSmart().

mysql_real_escape_string() also takes a connection resource as its second parameter. I can’t imagine that would cause the connections to go out of sync, but I guess it’s worth a shot.

Failing that, I’d need to see the full source of the session handler and index.php to get a better idea of what’s going on.

A(Answer):

Only thing that comes to mind is the use of mysql_real_escape_string() and possibly $db->QuoteSmart().

mysql_real_escape_string() also takes a connection resource as its second parameter. I can’t imagine that would cause the connections to go out of sync, but I guess it’s worth a shot.

Failing that, I’d need to see the full source of the session handler and index.php to get a better idea of what’s going on.

Ok, Pbmods. I will try that and see where it takes me. It really is strange. I will let you know what happens. Thanks so much for the help!

A(Answer):

You might need to create a separate instance of Database specific to the ‘app’ db inside of your session handling functions/class.

This is really weird. I created a totally seperate instance of the db inside the session class then instantiated the session in the index file. I am still getting the exact same error. How can this be??

I have the db instance totally seperated from the index file. Wouldn’t this indicate that the problem is in fact inside the database class?

A(Answer):

I am just curious here but what I am thinking may be the issue has to do with what Atli had mentioned in his post Here. See post #19. Could this be the same type of thing happening in this case?

I’m sorry this thread has been so long. 🙁

A(Answer):

To which part of that post are you referring?

Does the problem occur on every page, or just certain ones?

A(Answer):

To which part of that post are you referring?

Hey Pbmods,

I was referring to post #19 where I asked Atli about using $this->Name when he initialized it in the constructor and again in the method.

Atli said that he needed to initialize it in the method because if he didn’t, the output from that method would echo the prior return value. I was almost certain that I was onto something there. I thought that somehow the database method in the dbal was using the prior returned value of "site".

Does the problem occur on every page, or just certain ones?

The database error occurs on every page.

After 4 days, I finally fixed it. I got it last night actually. Matoma had given me a DBAL a while back to study and I was almost convinced that the issue was in something I messed up in the DBAL class I wrote so I ditched my script and used Matoma’s script for troubleshooting purposes.

The exact same problem was happening. I then went into the sessions class and instantiated a database object for each and every method that needed db access. The problem went away immediately.

I still don’t understand why the way we had it didn’t work. If in my index file, I refer to the database I need to use like:
[PHP] require("database.php");
require("session.php");
$db = new Database(‘application’); <—— Notice "application"
$sess = new Session($db);
session_start();
[/PHP] "application" is the database that is being passed into the constructor of the DBAL. Then the sessions class is passed the value of $db. Shouldn’t it continue to use the "application" db?

Then below that on the index page I have the "site" pages being loaded from the database from the "site" database. Somewhere in the mix of things, when the session values are written the sessions class is no longer using the "application" database but has switched over to using the "site" database. The error that was being generated was that "site.sessions" was not there. the sessions table resides on the "application" database and not the site.

Really strange. I am still very much a nube with OOP and I don’t yet have the experience to know exactly why it isn’t working as expected.

Is there something you can turn me onto that will help me in troubleshooting these types of errors?

I’m cool with leaving the database objects in the session class unless you know what is causing this behavior.