tags:

views:

381

answers:

7

I am using ADODB for connecting to database and I am using the variable $db inside my functions to refer to the database connection.

For some reason, the actual $db is not getting imported in the 'write' function:

<?php
// load ADODB class
include(DIR_WS_CLASSES . "adodb5/adodb.inc.php");

$db = NewADOConnection(DB_TYPE);
$db->Connect(DB_SERVER, DB_SERVER_USERNAME, DB_SERVER_PASSWORD, DB_DATABASE);


class SessionManager {
    var $life_time;

    function SessionManager(){
        global $db;
        // Read the maxlifetime setting from PHP
        $this->life_time = get_cfg_var("session.gc_maxlifetime");

        // Register this object as the session handler
        session_set_save_handler(array(&$this, "open"), 
                                array(&$this, "close"), 
                                array(&$this, "read"), 
                                array(&$this, "write"), 
                                array(&$this, "destroy"), 
                                array(&$this, "gc"));
    }

    function open($save_path, $session_name){
        global $sess_save_path;
        global $db;

        $sess_save_path = $save_path;

        return true;
    }

    function close(){
        global $db;
        return true;
    }

    function read($id){
        global $db;
        // Set empty result
        $data = '';

        // Fetch session data from the selected database
        $time = time();
        $newid = $db->qstr($id, get_magic_quotes_gpc());

        $sql = "SELECT session_data 
                FROM sessions 
                WHERE session_id = $newid 
                AND expires > $time";

        $rs = $db->Execute($sql) or die($db->ErrorMsg());
        $a = $rs->RecordCount();

        if($a > 0){
            $data = $rs->fields['session_data'];
        }

        return $data;
    }

    function write($id, $data){
        global $db;
        // Build query                
        $time = time() + $this->life_time;
        $newid = $db->qstr($id, get_magic_quotes_gpc());
        $newdata = $db->qstr($data, get_magic_quotes_gpc());
        $sql = "REPLACE sessions
                (session_id, session_data, expires) 
                VALUES($newid, $newdata, $time)";

        $rs = $db->Execute($sql) or die($db->ErrorMsg());

        return TRUE;
    }

    function destroy($id){
        global $db;
        // Build query
        $newid = $db->qstr($id, get_magic_quotes_gpc());
        $sql = "DELETE FROM sessions 
                WHERE session_id = $newid";

        $db->Execute($sql) or die($db->ErrorMsg());

        return TRUE;
    }

    function gc(){
    // Garbage Collection
        global $db;
        // Build DELETE query.  Delete all records that passed expiration time
        $sql = "DELETE FROM sessions 
                WHERE expires < UNIX_TIMESTAMP()";

        $db->Execute($sql) or die($db->ErrorMsg());

        // Always return TRUE
        return true;
    }
}


// initialize session
$sess = new SessionManager();
session_start();
?>

The function is intended to import the $db's actual value. Instead, it's assigning an empty value to $db. Any idea what is wrong with the above code? Thank you.

A: 

Either you don't assign $db as global or you assign it an empty value.

Michael Krelin - hacker
I didn't want to assign empty value to $db.The global $db is assigning empty value instead of importing the original value.
Nirmal
`global $db` isn't assigning anything. It basically imports `$db` into local namespace. So the question is whether you did assign anything to `$db` in global namespace (either outside of all functions or inside the function with `global $db` declaration.
Michael Krelin - hacker
Yes, a class is assigned to $db outside all functions and it occurs near the start of the page.
Nirmal
This code is part of the session handler function I am writing for saving into the database. Now I have a doubt whether the writing of session occurs inside or outside the scripting thread.
Nirmal
Have you checked the value of `$db` after assignment? The thing is, every newbie suspects that it's his runtime environment that is at fault (we all do that), but almost always it is not, so check your flow and syntax.
Michael Krelin - hacker
It may be executed before you get to assign your `$db`.
Michael Krelin - hacker
Consider initialilzinig `$db` in the `open` part of the handler?
Michael Krelin - hacker
I had the same idea and tried doing that. But no luck.
Nirmal
Try tracing your assignment and use and see what happens first.
Michael Krelin - hacker
and make sure you have `global $db` in your `open` handler if you assign it there.
Michael Krelin - hacker
Now I solved the issue by redeclaring the class to $db:$db = NewADOConnection(DB_TYPE);$db->Connect(DB_SERVER, DB_SERVER_USERNAME, DB_SERVER_PASSWORD, DB_DATABASE);But why can't I import it to the local namespace? Definitely the session is written within the scripting scope because the defined variable are properly being applied.
Nirmal
I think it's the order of execution that bites you.
Michael Krelin - hacker
I have posted the whole class. Maybe you can help in finding out if any problem exists. Thanks!
Nirmal
The important bit is whether you initialize `$db` before or after use. The code posted has no `$db` initialization so it's hard to tell. I'm pretty sure if you put it into your `open()` memeber it will work.
Michael Krelin - hacker
$db is initialized before calling this function.
Nirmal
before calling *which* function? ;-)
Michael Krelin - hacker
Sorry. Before using the class. :-)
Nirmal
I'm out of ideas. Try looking up the `$GLOBALS['db']` at various points of execution...
Michael Krelin - hacker
Anyway, thank you for your try.
Nirmal
A: 

I'm not a PHP expert but maybe you're overwriting it. Try an echo of $db before and after

global $db;

And: are you sure you assigned anything to $db (before looking to its value inside the function)?

I tried that and found no overwriting occuring at any place.A class is assigned to $db at the top of the page. So, I am very sure there is a preset value for the variable. Moreover, global $db is working in other functions!
Nirmal
A: 

Try var_dump on $db outside and inside the function.

Alex JL
When I do var_dump outside the function, it's showing the intended values.If inside before using global, it gives an undefined variable error.If after the global, it shows empty value.
Nirmal
+5  A: 

EDITED FOR THOSE WHO DIDN'T FOLLOW THE COMMENTS ;)

"As of PHP 5.0.5 the write and close handlers are called after object destruction and therefore cannot use objects or throw exceptions. The object destructors can however use sessions. It is possible to call session_write_close() from the destructor to solve this chicken and egg problem"

So the idea is to have a destructor like this:

function __destruct() {
    session_write_close();
}

So that objects can be used in the write & close handlers.

Then, for safety, to re-instantiate $db in write & close because the global $db destructor may very well be called before the SessionHandler one.

Julian Aubourg
Hmm... no luck! :(
Nirmal
Maybe you should put a little more of you code so we can have some context.
Julian Aubourg
OK. I shall edit my post now and give more of the code.
Nirmal
You forgot "global $db;" in the write method.
Julian Aubourg
But I have hard time understanding your logic... why isn't $db a member of your class?
Julian Aubourg
No. I wrongly posted my latest trial. Now I have corrected the code sample. Please have a look and see if you can help me. Thanks.
Nirmal
Oh, I think I understand. It's very possible the global $db variable is garbage collected before the session is closed/saved. You should read the doc regarding the session custom handlers carefully. You may need to recreate the db connection at a certain point.
Julian Aubourg
Oh my God, somehow I missed it! Now I have assigned $db as a member of class and everything works fine. Thank you. That was a nice catch!
Nirmal
No.. assigning as member doesn't work. The page was cached it seems.
Nirmal
@Juilan: I am not really sure about why you did.global $db;$db=If you are creating a new object, then why to import it by using global keyword.
Krishna Kant Sharma
@Krishna: the global $db I marked here was to be put before the global declaration. See, if you include a php file and it happens to end up in a function scope, the $db will never be global, hence the use of global $db before the assignment. Believe me, I had the issue and I thought it may have been Nirmal's one... but I was mistaken ;)
Julian Aubourg
@Nirmal: yes, I'm almost certain globals have been cleansed when the write handler is called. A good solution would be to have $db be a member of the class.
Julian Aubourg
@Nirmal: I had skipped your comments... put the code with $db as an approach somewhere... because it should work. I'd like to review it.
Julian Aubourg
I have updated the code now.
Nirmal
@Nirmal: with $db as a member... jeez, my writing is spacey Oo
Julian Aubourg
@Nirmal: look in my post now and try it.
Julian Aubourg
I have updated the code. Please have a look.
Nirmal
@Nirmal: public static $db; does nothing unless you use it as SessionManager::$db ;) Have you tried the code I put into my post?
Julian Aubourg
Oh, OK. I tried yours but the $db variable in all the functions are failing. I am getting an undefined error.
Nirmal
Wait... you do instance the SessionManager at one point?Anyway, are you sure you can give methods as session handlers?
Julian Aubourg
AHA! The PHP doc has something interesting: "As of PHP 5.0.5 the write and close handlers are called after object destruction and therefore cannot use objects or throw exceptions. The object destructors can however use sessions.It is possible to call session_write_close() from the destructor to solve this chicken and egg problem."
Julian Aubourg
So, what should I do now? What should I add and where should I add? Please..
Nirmal
@Nirmal: so basically, revert to your original code but add a destructor to SessionManager that calls session_write_close(). That way, you'll be able to use objects in your handlers before object destruction. Hopefully, the SessionManager will be destroyed before the $db object but it would be safer to create a new ADOConnection in write and close just to be safe.
Julian Aubourg
Since I am new to PHP classes, it would be nice if you can show me how to add a destructor. Thank you.
Nirmal
"function __destruct() { session_write_close(); }" but remember, chances are the $db object would have been destroyed first, so make a new one in write )
Julian Aubourg
OK. I tried destructor but still the problem persists. I am reverting to the original code with $db intialized again. Whoever you are, thank you for your patience and passion to help. Have a great day!
Nirmal
I wish you good luck. Custom session handling in php is a hell of a sport ;)
Julian Aubourg
@Nirmal: oh, and you have *2* underscores in front of destruct (just in case, I made the error often in the beginning).
Julian Aubourg
Point noted. :)
Nirmal
A: 

You dont seem initialise $db anywhere in the above code.

I would expect to see a $db = new ADO("myserver,mydb,user,password") call somewhere in the code.

James Anderson
I have updated the code now.
Nirmal
A: 

Try these 2 things:

1) Assign

$GLOBALS['db']=...;

instead of

$db=...;

in your global script.

Then reference it in your function as $db=$GLOBALS['db'];

Reason: Its a possibility that you are assigning $db in some place which is not global.

2) Make it as private property of your class. Provide a getter, but not the setter. Instead set it from your constructor.

Krishna Kant Sharma
A: 

I am pretty sure what is the problem. See your code will work fine when this file was included in global scope. But consider this:

$a=1; function displaya() { global $a; var_dump($a); }

When you run this file you will see int(1) as the output.

But consider another file:

function abc() { include("above-stated-file.php"); displaya(); }

abc();

Now it will display NULL.

The reason is that in second file. Your first file was included in a local context, not the global one. Thats why 'global $a' doesnt work.

Change it to :

$GLOBALS['a']=....;

and when you use it : $a=$GLOBALS['a'];

Krishna Kant Sharma
I tried it, but still Iget an error Notice: Undefined index: db in E:\applications\myfurni\webroot\includes\classes\sessions\sessions.php on line 59
Nirmal
Where did you assigned $GLOBALS['db']=NewD.......;
Krishna Kant Sharma
I assigned it like this: $GLOBALS['db'] = NewADOConnection(DB_TYPE);
Nirmal