0

I have a code:

class db {
    var $connection;
    function escape($esc) {
        return str_replace(array('%','_'),array('\%','\_'),mysqli_real_escape_string($this->connection,$esc));
    }
[...]
}
$db=new db;



class Session {
    private function read($sid) {
        global $db;
        $r=$db->query('SELECT `data` FROM `sess` WHERE `hash`=\''.$db->escape($sid).'\' LIMIT 1');
        if ($this->debug) echo 'Read: <u>SELECT `data` FROM `sess` WHERE `hash`=\''.$db->escape($sid).'\' LIMIT 1</u><br/>';
        if($db->num_rows($r)==1) {
            $fields=$db->fetch_assoc($r);
            return $fields['data'];
        }
        else return '';
    }

    private function write($sid, $data) {
        global $db;
        if ($this->debug) echo 'Write: <u>REPLACE INTO `sess`(`hash`,`data`) VALUES(\''.$db->escape($sid).'\',\''.$db->escape($data).'\')</u><br/>';
        $db->query('REPLACE INTO `sess`(`hash`,`data`) VALUES(\''.$db->escape($sid).'\',\''.$db->escape($data).'\')');
        return $db->connection->affected_rows;
    }
[...]
function __construct($debug=false) {
    session_set_save_handler(
        array(&$this, 'open'),
        array(&$this, 'close'),
        array(&$this, 'read'),
        array(&$this, 'write'),
        array(&$this, 'destroy'),
        array(&$this, 'clean')
    );
    $this->debug=$debug;
    session_start();
}
}
$sessions=new Session(true);

And I keep getting Fatal error: Call to a member function escape() on a non-object on line 61 (2nd row in function write($sid, $data)). The strange thing is that the debugger shows that the function read has successfully executed. Could anyone please shed some light on why this might be happening?

tereško
  • 58,060
  • 25
  • 98
  • 150
AM-
  • 871
  • 1
  • 10
  • 20

1 Answers1

2

Most likely (though we don't see where you call the method), it's called somewhere before the $db variable is declared.

The correct solution would be to inject the variable to class via the constructor.

public function __construct(db $db, $debug = false) {
    $this->db = $db;
    ....
}

Then use $this->db wherever you need.

The problem with using global variables is that the order of decleration matters, meaning you have to declare $db before you call Session::write(), but it's not obvious that you need to do so!. Now, it's obvious because you need a $db object in order for your constructor to run!

Why is Global State so Evil?

Community
  • 1
  • 1
Madara's Ghost
  • 172,118
  • 50
  • 264
  • 308
  • I know that $db is always declared before the session part as they are both in included in settings.php. My debugger would have printed out illogical things if this was the case, though, as session data is read first, written later, and the read is done without mistakes. Will try though. :) – AM- Jul 15 '12 at 17:36
  • Tried it and, surprisingly, it solved my problem. I'm still unsure why it occurred in the first place as $db was always called BEFORE sessions, but that solved the thing. Thank you. :) – AM- Jul 15 '12 at 17:51
  • @Limoncello: Regardless of why this works and the other solution doesn't (We'll probably never know), this is the **correct** solution, this is what you should **always** do instead of globals! See the edited answer with the link to a question explaining that. – Madara's Ghost Jul 15 '12 at 19:12