Session Class in PHP stopped working
<p>UPDATE:
While migrating to a shared hosting I was trying to figure this issue out. I found the original code for session management here - <a href="http://www.wikihow.com/Create-a-Secure-Session-Management-System-in-PHP-and-MySQL" rel="nofollow noreferrer">http://www.wikihow.com/Create-a-Secure-Session-Management-System-in-PHP-and-MySQL</a></p>
<p>In the end I simply replaced my class with this one, and it started working then, even though, I am pretty sure, I did not change it to begin with. But then I started having a weirder issue. On home server everything worked fine, but on shared hosting every page showed error 503. If I refreshed it a few times I got the same errors as below. Out of curiosity I switched to PHP's native session management and... It worked normally. So my guess is this issue occurs, when the class fails to connect to DB in a timely manner for some reason. After all session class has a few calls to it.</p>
<p>Question is - is there a way to use custom class, while with ability to revert to native one if it fails?</p>
<hr />
<p>When preparing to move my site from local PC to a hosting I encountered some issues with one of the &quot;subsites&quot;. It has an authentication system based on common functions used for storing sessions in DB. At some point (and I am unsure at which) it stopped working properly.</p>
<p>So, when I enter incorrect login or password site acts as expected and I get an error message. Since details are checked against database this proves, that connection to database is established. But when I input correct ones - page refreshes as if login succeeded, but it still shows me login page. Nothing is written in the logs.</p>
<p>After a few tries and\or refreshes I do get a 500 server error and following lines in server log:</p>
<blockquote>
<p>[Sun Apr 03 13:29:42.447631 2016] [:error] [pid 12956:tid 1956] [client 192.168.1.1:60985] PHP Warning:  session_start(): Failed to decode session object. Session has been destroyed in C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\genfunc\functions.php on line 141, referer: cant_post_too_many_links/edocuments/index.php</p>
<p>[Sun Apr 03 13:29:42.447631 2016] [:error] [pid 12956:tid 1956] [client 192.168.1.1:60985] PHP Warning:  session_regenerate_id(): Cannot regenerate session id - session is not active in C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\genfunc\functions.php on line 144, referer: cant_post_too_many_links/edocuments/index.php</p>
<p>[Sun Apr 03 13:29:42.447631 2016] [:error] [pid 12956:tid 1956] [client 192.168.1.1:60985] PHP Warning:  mysqli::prepare(): Couldn't fetch mysqli in C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\genfunc\functions.php on line 192, referer: cant_post_too_many_links/edocuments/index.php</p>
<p>[Sun Apr 03 13:29:42.447631 2016] [:error] [pid 12956:tid 1956] [client 192.168.1.1:60985] PHP Fatal error:  Uncaught Error: Call to a member function bind_param() on null in C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\genfunc\functions.php:195\nStack trace:\n#0 C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\genfunc\globalstart.php(7): session-&gt;gc(1)\n#1 C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\edocuments\index.php(2): include_once('C:\\Users\\simbi\\...')\n#2 {main}\n  thrown in C:\Users\simbi\OneDrive\Documents\!Personal\projects\WS\apache\htdocs\genfunc\functions.php on line 195, referer: cant_post_too_many_links/edocuments/index.php</p>
</blockquote>
<p>I thought, that maybe there is something wrong with include in index.php since it showed such a truncated path, but even changing it does not help.</p>
<p>globalstart.php is actually quite small and I do not see an issue here:</p>
<pre><code>&lt;?php
#include settings
include_once &quot;../genfunc/config.php&quot;;
include_once &quot;../genfunc/functions.php&quot;;
$session = new session();
$session-&gt;start_session('_s', true);
$session-&gt;gc(1);
$loggedin = login_check();
?&gt;
</code></pre>
<p>It does not like gc(1) apparently. While the line in functions.php is $this-&gt;gc_stmt-&gt;bind_param('s', $old); in gc($max) function of session class as below:</p>
<pre><code>class session {
    function __construct() {
        // set our custom session functions.
        session_set_save_handler(array($this, 'open'), array($this, 'close'), array($this, 'read'), array($this, 'write'), array($this, 'destroy'), array($this, 'gc'));
        // This line prevents unexpected effects when using objects as save handlers.
        register_shutdown_function('session_write_close');
    }
    function start_session($session_name, $secure) {
        // Make sure the session cookie is not accessable via javascript.
        $httponly = true;
        // Hash algorithm to use for the sessionid. (use hash_algos() to get a list of available hashes.)
        $session_hash = 'sha512';
        // Check if hash is available
        if (in_array($session_hash, hash_algos())) {
            // Set the has function.
            ini_set('session.hash_function', $session_hash);
        }
        // How many bits per character of the hash.
        // The possible values are '4' (0-9, a-f), '5' (0-9, a-v), and '6' (0-9, a-z, A-Z, &quot;-&quot;, &quot;,&quot;).
        ini_set('session.hash_bits_per_character', 5);
        // Force the session to only use cookies, not URL variables.
        ini_set('session.use_only_cookies', 1);
        // Get session cookie parameters 
        $cookieParams = session_get_cookie_params(); 
        // Set the parameters
        session_set_cookie_params($cookieParams[&quot;lifetime&quot;], $cookieParams[&quot;path&quot;], $cookieParams[&quot;domain&quot;], $secure, $httponly); 
        // Change the session name 
        session_name($session_name);
        // Now we cat start the session
        session_start();
        // This line regenerates the session and delete the old one. 
        // It also generates a new encryption key in the database. 
        session_regenerate_id(true); 
    }
    function open() {
        global $sesuser, $sespass, $mhost, $mdbname, $mport, $msocket;
        $mysqli = new mysqli($mhost, $sesuser, $sespass, $mdbname, $mport, $msocket);
        $this-&gt;db = $mysqli;
        return true;
    }
    function close() {
        $this-&gt;db-&gt;close();
        return true;
    }
    function read($id) {
        if(!isset($this-&gt;read_stmt)) {
            $this-&gt;read_stmt = $this-&gt;db-&gt;prepare(&quot;SELECT data FROM `ed__sessions` WHERE id = ? LIMIT 1&quot;);
        }
        $this-&gt;read_stmt-&gt;bind_param('s', $id);
        $this-&gt;read_stmt-&gt;execute();
        $this-&gt;read_stmt-&gt;store_result();
        $this-&gt;read_stmt-&gt;bind_result($data);
        $this-&gt;read_stmt-&gt;fetch();
        $key = $this-&gt;getkey($id);
        $data = $this-&gt;decrypt($data, $key);
        return $data;
    }
    function write($id, $data) {
        // Get unique key
        $key = $this-&gt;getkey($id);
        // Encrypt the data
        $data = $this-&gt;encrypt($data, $key);
        $time = time();
        if(!isset($this-&gt;w_stmt)) {
            $this-&gt;w_stmt = $this-&gt;db-&gt;prepare(&quot;REPLACE INTO `ed__sessions` (id, set_time, data, session_key) VALUES (?, ?, ?, ?)&quot;);
        }
        $this-&gt;w_stmt-&gt;bind_param('siss', $id, $time, $data, $key);
        $this-&gt;w_stmt-&gt;execute();
        return true;
    }
    function destroy($id) {
        if(!isset($this-&gt;delete_stmt)) {
            $this-&gt;delete_stmt = $this-&gt;db-&gt;prepare(&quot;DELETE FROM `ed__sessions` WHERE id = ?&quot;);
        }
        $this-&gt;delete_stmt-&gt;bind_param('s', $id);
        $this-&gt;delete_stmt-&gt;execute();
        return true;
    }
    function gc($max) {
        if(!isset($this-&gt;gc_stmt)) {
            $this-&gt;gc_stmt = $this-&gt;db-&gt;prepare(&quot;DELETE FROM `ed__sessions` WHERE set_time &lt; ?&quot;);
        }
        $old = time() - $max;
        $this-&gt;gc_stmt-&gt;bind_param('s', $old);
        $this-&gt;gc_stmt-&gt;execute();
        return true;
    }
    private function getkey($id) {
        if(!isset($this-&gt;key_stmt)) {
            $this-&gt;key_stmt = $this-&gt;db-&gt;prepare(&quot;SELECT session_key FROM `ed__sessions` WHERE id = ? LIMIT 1&quot;);
        }
        $this-&gt;key_stmt-&gt;bind_param('s', $id);
        $this-&gt;key_stmt-&gt;execute();
        $this-&gt;key_stmt-&gt;store_result();
        if($this-&gt;key_stmt-&gt;num_rows == 1) { 
            $this-&gt;key_stmt-&gt;bind_result($key);
            $this-&gt;key_stmt-&gt;fetch();
            return $key;
        } else {
            $random_key = hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
            return $random_key;
        }
    }
    private function encrypt($data, $key) {
        $salt = 'u@cewPpn7e9R9pE2HUe-ebH7=EcC!*7Tw!s4espu@hsWDR3DtDuHrGu6hEAGrrHe';
        $key = substr(hash('sha256', $salt.$key.$salt), 0, 32);
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        $encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_ECB, $iv));
        return $encrypted;
    }
    private function decrypt($data, $key) {
        $salt = 'u@cewPpn7e9R9pE2HUe-ebH7=EcC!*7Tw!s4espu@hsWDR3DtDuHrGu6hEAGrrHe';
        $key = substr(hash('sha256', $salt.$key.$salt), 0, 32);
        $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
        $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
        $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, base64_decode($data), MCRYPT_MODE_ECB, $iv);
        return $decrypted;
    }
}
</code></pre>
<p>Honestly, I fail to see what is wrong here, since this is pretty general code, I copied from some other site several years ago and it worked before. But I noticed in the database, that there are 2 sessions created during login for some reason. Maybe that the reason it fails, but I do not understand why the second one gets created.</p>
<p>Can someone advise? If needed I can add some other portions of the code relevant to this.</p>