Location: PHPKode > projects > crVCL PHP Framework > sessionhandler.lib.php
<?PHP

/*

  The contents of this file are subject to the Mozilla Public License
  Version 1.1 (the "License"); you may not use this file except in compliance
  with the License. You may obtain a copy of the License at
  http://www.mozilla.org/MPL/MPL-1.1.html or see MPL-1.1.txt in directory "license"

  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
  the specific language governing rights and limitations under the License.

  The Initial Developers of the Original Code are:
  Copyright (c) 2003-2012, CR-Solutions (http://www.cr-solutions.net), Ricardo Cescon
  All Rights Reserved.

  Contributor(s): PanterMedia GmbH (http://wwwpanthermedianet), Peter Ammel, Ricardo Cescon

  crVCL PHP Framework Version 2.4
 */




############################################################
if (!defined("SESSIONHANLDER_LIB")) {
   define("SESSIONHANLDER_LIB", 1);
############################################################
   if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER"])) {
      $session_handler_name = "";

//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
      interface iSessionHandler {

         public function open($save_path, $session_name);

         public function read($id);

         public function write($id, $data);

         public function close();

         public function destroy($id);

         public function gc($gc_maxlifetime);
      }
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
      class SessionHandler {
         protected $m_connection = null;
         protected $m_host = "";
         protected $m_user = "root";
         protected $m_pw = "";
         protected $m_db = "";
         protected $m_table = "";
         protected $m_db_engine = "MyISAM";
         protected $m_err_details = false;
         protected $m_err_log = "";
         protected $m_log_critical_section_id = null;
         protected $m_log_critical_section_path = "./";
         protected $m_log_delAfter = "1024K";
         protected $m_throw_trigger = true;
         protected $m_sess_expire = -1;
         protected $m_read = null;
         //-------------------------------------------------------------------------------------------------------------------------------------
            function __construct() {
               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"]))
                  $this->m_host = $GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_USER"]))
                  $this->m_user = $GLOBALS["CRVCL"]["SESSION_HANDLER_USER"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_PW"]))
                  $this->m_pw = $GLOBALS["CRVCL"]["SESSION_HANDLER_PW"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_DB"]))
                  $this->m_db = $GLOBALS["CRVCL"]["SESSION_HANDLER_DB"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_DETAILS"]))
                  $this->m_err_details = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_DETAILS"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_DB_ENGINE"]))
                  $this->m_db_engine = $GLOBALS["CRVCL"]["SESSION_HANDLER_DB_ENGINE"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG"]))
                  $this->m_err_log = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_CRTITICAL_SECTION_ID"]))
                  $this->m_log_critical_section_id = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_CRTITICAL_SECTION_ID"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_CRTITICAL_SECTION_PATH"]))
                  $this->m_log_critical_section_path = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_CRTITICAL_SECTION_PATH"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_CRTITICAL_SECTION_PATH"]))
                  $this->m_log_critical_section_path = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_CRTITICAL_SECTION_PATH"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_DELETE_AFTER"]))
                  $this->m_log_delAfter = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_LOG_DELETE_AFTER"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_THROW_TRIGGER"]))
                  $this->m_throw_trigger = $GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_THROW_TRIGGER"];

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_EXPIRE"]))
                  $this->m_sess_expire = $GLOBALS["CRVCL"]["SESSION_HANDLER_EXPIRE"];
            }
         //-------------------------------------------------------------------------------------------------------------------------------------
         function __destruct() {
            session_write_close();
            mssleep(100);
         }
         //-------------------------------------------------------------------------------------------------------------------------------------
            public function writeLog($s) {
               $log_file = $this->m_err_log;
               $bytes = 0;
               if (!empty($log_file)) {
                  clearstatcache();

                  $crit = null;
                  if ($this->m_log_critical_section_id !== null) {
                     $crit = new CriticalSection($this->m_log_critical_section_path);
                     $crit->enter($this->m_log_critical_section_id, 10, 100);
                  }

                  if (!empty($this->m_log_delAfter)) {
                     $bytes = str2byteInt($this->m_log_delAfter);
                     if (is_file($log_file) && @filesize($log_file) > $bytes) {
                        unlink($log_file);
                     }
                  }

                  $fbuf = @file_get_contents($log_file);

                  $fbuf .= "Timestamp: " . date2MySQLDate() . "\r\n" . $s;
                  $fbuf .= "\r\n\r\n";

                  $bytes = file_put_contents($log_file, $fbuf);

                  if ($this->m_log_critical_section_id !== null) {
                     $crit->leave($this->m_log_critical_section_id);
                  }
               }
               return $bytes;
            }
//-------------------------------------------------------------------------------------------------------------------------------------
      }
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
      if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "memcache") {
         $session_handler_name = "SessionHandlerMemcache";
         
         class SessionHandlerMemcache extends SessionHandler implements iSessionHandler {
//-------------------------------------------------------------------------------------------------------------------------------------
            public function open($save_path, $session_name) {
               // $this->writeLog("open ".time()); // debug only

               if (empty($this->m_host)) {
                  $this->m_host = $save_path;
               }
               $this->m_table = $session_name;

               $this->m_connection = new Memcached();

               $compress = false;
               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BIGDATA_COMPRESSED"])) {
                   $this->m_connection->setOption(Memcached::OPT_COMPRESSION, true);
               }else{
                   $this->m_connection->setOption(Memcached::OPT_COMPRESSION, false);
               }

               // for clustering with minimal cache losses
               $this->m_connection->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
               $this->m_connection->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, TRUE);
               

               $host = $this->m_host;
               $host = str_replace(';', ',', $host);
               $host = explode(',', $host);
               
               $c_host = acount($host);

               for($h = 0; $h < $c_host; $h++){
                     $tmp = explode(':', $host[$h]);
                     $server = $tmp[0];
                     $port = 11211;
                     if(isset($tmp[1])){
                        $port = 11211;
                     }

                     try{

                        if(!$this->m_connection->addserver($server, $port)){
                           if ($this->m_throw_trigger){
                              if($c_host > 1){
                                 trigger_error("SessionHandlerMemcache: can't connect to ".$server.':'.$port, E_USER_NOTICE);
                              }else{
                                 throw new Exception("SessionHandlerMemcache: can't connect to ".$server.':'.$port);
                              }
                           }

                        }

                     }catch(Exception $e){

                           $this->writeLog($e->getMessage());
                           if ($this->m_throw_trigger)
                              trigger_error($e->getMessage(), E_USER_WARNING);
                     }
               }
                             
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function read($id) {
               //$this->writeLog("read ".time()); // debug only
               
               if (!$this->m_connection) {
                  return "";
               }

               $prefix = $this->m_db;
               $sessname = $this->m_table;
               $key = (empty($prefix)?'':$prefix.'.').$sessname.'.'.$id;


               try{
                  $this->m_read = $this->m_connection->get($key);
                  
               }catch(Exception $e){
                     $this->writeLog($e->getMessage());
                     if ($this->m_throw_trigger)
                        trigger_error($e->getMessage(), E_USER_WARNING);
               }

               if($this->m_read === false){
                  $this->m_read = null;
                  return "";
               }

               if (left($this->m_read, 7) == "base64:") {
                  $this->m_read = base64_decode(substr($this->m_read, 7));
               }

               return $this->m_read;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function write($id, $data) {
               // $this->writeLog("write ".time()); // debug only

               if (!$this->m_connection) {
                  return true;
               }
               if (!$data) {
                  return false;
               }

               
               $prefix = $this->m_db;
               $sessname = $this->m_table;
               $key = (empty($prefix)?'':$prefix.'.').$sessname.'.'.$id;
               
               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BASE64"]) && $GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BASE64"] == true) {
                  $data = "base64:" . base64_encode($data);
               }
               
               ###
               $gc_maxlifetime = intval(ini_get('session.gc_maxlifetime'));
               if ($this->m_sess_expire > -1) {
                  $gc_maxlifetime = $this->m_sess_expire;
               }
               if (!is_numeric($gc_maxlifetime) || $gc_maxlifetime < 0) {
                  $gc_maxlifetime = 1440;
               }
               ###


               try{
                  $ret = $this->m_connection->set($key, $data, $gc_maxlifetime);
                  if(!$ret){
                     throw new Exception("SessionHandlerMemcache: write SESSION to " . $this->m_host . " failed (" . $id . ")");
                  }
               }catch(Exception $e){
                     $this->writeLog($e->getMessage());
                     if ($this->m_throw_trigger)
                        trigger_error($e->getMessage(), E_USER_WARNING);

                     $data = NULL;
                     unset($data);
                     gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);

                     return false;
               }
                     
               $data = NULL;
               unset($data);
               gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
               
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function close() {
               // $this->writeLog("close ".time()); // debug only

               if (!$this->m_connection) {
                  return true;
               }

               $gc_maxlifetime = -1; // gc_maxlifetime will be controled direct on memcached, see method "write"
               $this->gc($gc_maxlifetime);

               try{
                  if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "memcache") {
                     # not supported this time for memcached
                     #$this->m_connection->close();
                  }
               } catch (Exception $e){$e=null;}
               
               // $this->writeLog("close2 ".time()); // debug only
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function destroy($id) {
               // $this->writeLog("destroy ".time());  // debug only

               if (!$this->m_connection) {
                  return true;
               }

               $prefix = $this->m_db;
               $sessname = $this->m_table;
               $key = (empty($prefix)?'':$prefix.'.').$sessname.'.'.$id;

               try{
                  $ret = $this->m_connection->delete($key);
                  if (!$ret) {
                     throw new Exception("SessionHandlerMemcache: delete SESSION from " . $this->m_host . " failed (" . $id . ")");
                  }
               }  catch (Exception $e){

                  $this->writeLog($e->getMessage());
                  if ($this->m_throw_trigger)
                     trigger_error($e->getMessage(), E_USER_WARNING);

                  return false;
               }
               
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function gc($gc_maxlifetime) {               
               if (!$this->m_connection) {
                  return true;
               }
               
               // gc_maxlifetime will be controled direct on memcached, see method "write"
               $gc_maxlifetime = -1;

               gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
               
               mssleep(100);
               return true;
            }
//-------------------------------------------------------------------------------------------------------------------------------------
         }
         //-------------------------------------------------------------------------------------------------------------------------------------
         //-------------------------------------------------------------------------------------------------------------------------------------
         $session_handler_check_con = null;
         if(class_exists("Memcached")) {

            $host = $GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"];
            $host = str_replace(';', ',', $host);
            $host = explode(',', $host);

            $c_host = acount($host);

            for($h = 0; $h < $c_host; $h++){
                  $tmp = explode(':', $host[$h]);
                  $server = $tmp[0];
                  $port = 11211;
                  if(isset($tmp[1])){
                     $port = 11211;
                  }

                  $session_handler_check_con = new Memcached();
                  if(!$session_handler_check_con->addserver($server, $port)){
                     $session_handler_check_con = null;
                     $trigger_msg = "SessionHandlerMemcache: can't connect to \"" . $GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"] . "\"";
                     if ($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_THROW_TRIGGER"]){
                        if($c_host > 1){
                           trigger_error($trigger_msg, E_USER_NOTICE);
                        }else{
                           trigger_error($trigger_msg, E_USER_WARNING);
                        }
                     }
                     $session_handler_name = "";
                  }
               }
                  free($session_handler_check_con);
         }else{
              trigger_error("SessionHandlerMemcache: Memcached module for PHP not installed or loaded, please see php.ini", E_USER_ERROR);
         }
            gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
      } // SessionHandlerMemcache
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
      if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "mysql" || strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "pmysql") {
         $session_handler_name = "SessionHandlerMySQL";

         class SessionHandlerMySQL extends SessionHandler implements iSessionHandler {
            
//-------------------------------------------------------------------------------------------------------------------------------------
            private function query($q, &$errno, &$errmsg) {
               $ret = null;
               for ($r = 0; $r < 3; $r++) {
                  $errmsg = "";
                  $errno = 0;
                  $ret = null;
                  if(is_object($this->m_connection) && $this->m_connection instanceof mysqli){
                     $ret = $this->m_connection->query($q);
                     $errno = $this->m_connection->errno;
                     $errmsg = $this->m_connection->error;
                  }else{
                     $ret = mysql_query($q, $this->m_connection);
                     $errno = mysql_errno($this->m_connection);
                     $errmsg = mysql_error($this->m_connection);
                  }
                  // 2006 => MySQL server has gone away
                  // 2011 => %s via TCP/IP
                  // 2013 => Lost connection to MySQL server during query
                  // 1040 => Too many connections
                  // 1053 => Server shutdown in progress
                  // 1152 => Aborted connection %ld to db: '%s' user: '%s' (%s)
                  // 1184 => Aborted connection %ld to db: '%s' user: '%s' host: '%s' (%s)
                  // 1203 => User %s already has more than 'max_user_connections' active connections
                  // 1205 => Lock wait timeout expired.
                  // 1213 => Deadlock found when trying to get lock; try restarting transaction
                  // 2048 => Invalid connection handle
                  // 2055 => Lost connection to MySQL server at '%s', system error: %d
                  // retry only allowed for no or single transaction and in case of 1205/1213
                  if ($errno == 2013 || $errno == 2011 || $errno == 2006 ||
                    $errno == 1040 || $errno == 1053 ||
                    $errno == 1152 || $errno == 1184 || $errno == 1203 || $errno == 1205 || $errno == 1213 ||
                    $errno == 2048 || $errno == 2055) {

                     mssleep(300);
                     if((is_object($this->m_connection) && $this->m_connection instanceof mysqli && $this->m_connection->ping()) || is_resource($this->m_connection) && mysql_ping($this->m_connection)){
                        continue;
                     } else {
                        $errno = -1;
                        $errmsg = "Lost connection to MySQL Server, retry failed.";
                     }
                  }
                  break;
               }
               if ($errno == 0) {
                  $errmsg = "";
               }
               return $ret;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function open($save_path, $session_name) {
               // $this->writeLog("open ".time()); // debug only

               if (empty($this->m_host)) {
                  $this->m_host = $save_path;
               }
               $this->m_table = $session_name;


               $tmp = explode(':', $this->m_host);
               $server = $tmp[0];
               $port = 3306;
               if(isset($tmp[1])){
                  $port = 3306;
               }
               
               $this->m_connection = null;
               if(is_callable("mysqli_init")) {

                  $this->m_connection = mysqli_init();
                  if ($this->m_connection instanceof mysqli){
                      
                     if (!$this->m_connection->real_connect((strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "pmysql" ? 'p:' : '') . $server, $this->m_user, $this->m_pw, null, $port)) {
                        $this->m_connection = null;
                     }
                  }                  
               }else{
                  if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "pmysql") {
                     $this->m_connection = mysql_pconnect($this->m_host, $this->m_user, $this->m_pw);
                  } else {
                     $this->m_connection = mysql_connect($this->m_host, $this->m_user, $this->m_pw, true);
                  }
               }


               if (!isset($GLOBALS["CRVCL"]["SESSION_HANDLER_CHARSET"])) {
                  $GLOBALS["CRVCL"]["SESSION_HANDLER_CHARSET"] = "utf8";
               }
               if(is_object($this->m_connection) && $this->m_connection instanceof mysqli){
                  $this->m_connection->set_charset($GLOBALS["CRVCL"]["SESSION_HANDLER_CHARSET"]);
               }  else {
                  mysql_set_charset($GLOBALS["CRVCL"]["SESSION_HANDLER_CHARSET"], $this->m_connection);
               }
                             

               $query = 'SHOW DATABASES LIKE "' . $this->m_db . '"';
               $errno = 0;
               $errmsg = "";
               $res = $this->query($query, $errno, $errmsg);

               if ($errno != 0) {
                  $trigger_msg = "SessionHandlerMySQL: check database \"" . $this->m_db . "\" failed";
                  if ($this->m_err_details) {
                     $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")";
                  }
                  $this->writeLog($trigger_msg);
                  if ($this->m_throw_trigger)
                     trigger_error($trigger_msg, E_USER_WARNING);

                  return false;
               }else {
                  if((is_object($res) && $res instanceof mysqli_result && $res->num_rows == 0) || (is_resource($res) && mysql_num_rows($res) == 0)){
                     $trigger_msg = "SessionHandlerMySQL: database \"" . $this->m_db . "\" not available";
                     $this->writeLog($trigger_msg);
                     if ($this->m_throw_trigger)
                        trigger_error($trigger_msg, E_USER_WARNING);

                     return false;
                  }
               }

               $query = 'SHOW TABLE STATUS FROM ' . $this->m_db . ' LIKE "' . $session_name . '"';
               $errno = 0;
               $errmsg = "";
               $res = $this->query($query, $errno, $errmsg);
               if ($errno == 0) {
                  if((is_object($res) && $res instanceof mysqli_result && $res->num_rows == 0) || (is_resource($res) && mysql_num_rows($res) == 0)){
                     $version = "0.0.0";
                     $res = $this->query("SELECT VERSION() AS version", $errno, $errmsg);
                     if ($errno == 0) {
                        $version = 0;
                        if(is_object($res) && $res instanceof mysqli_result && $res->num_rows == 0){
                           $version = $res->fetch_assoc();
                        }else{
                           $version = mysql_fetch_assoc($res);
                           mysql_freeresult($res);
                        }
                        free($res);
                        gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
                        $version = $version["version"];
                        $version = strcut($version, "-");
                     }

                     $datafiled = '`data` BLOB default NULL';
                     if (strtoupper($this->m_db_engine) == "MEMORY" && $version >= "5.0.3") {
                        if (!isset($GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"])) {
                           $GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"] = '1K';
                        }
                        $GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"] = str2byteInt($GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"]);
                        if ($GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"] > 32768) {
                           $GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"] = 32768;
                        }
                        $datafiled = '`data` VARCHAR(' . $GLOBALS["CRVCL"]["SESSION_HANDLER_MEMORY_TABLE_MAX_DATA"] . ') default NULL';
                     }
                     $query = '
   			      CREATE TABLE ' . $this->m_db . "." . $session_name . ' (
                              `sessid` VARCHAR(255) NOT NULL,
                              ' . $datafiled . ',
                              `mtime` INTEGER UNSIGNED default NULL,
                              PRIMARY KEY  USING BTREE(`sessid`),
                              KEY `INX_MTIME` USING BTREE (`mtime`)
                              )
                              ENGINE = ' . $this->m_db_engine . ';
   			      ';

                     $errno = 0;
                     $errmsg = "";
                     $this->query($query, $errno, $errmsg);
                     if ($errno != 0) {
                        $trigger_msg = "SessionHandlerMySQL: create SESSION table " . $session_name . " failed";
                        if ($this->m_err_details) {
                           $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")";
                        }
                        $this->writeLog($trigger_msg);
                        if ($this->m_throw_trigger)
                           trigger_error($trigger_msg, E_USER_WARNING);

                        return false;
                     }

                  }else {

                     if(is_object($res) && $res instanceof mysqli_result){
                        $row = $res->fetch_assoc();
                     }else{
                        $row = mysql_fetch_assoc($res);
                        mysql_freeresult($res);
                     }    
                     free($res);
                     gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
                     $this->m_db_engine = $row["Engine"];
                  }
               } else {
                  return false;
               }

               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function read($id) {
               //$this->writeLog("read ".time()); // debug only

               if (!$this->m_connection) {
                  return "";
               }
               $query = 'SELECT SQL_NO_CACHE data FROM ' . $this->m_db . "." . $this->m_table . ' WHERE sessid = "' . $id . '"';

               $res = null;
               $retry = 1; // increase this only for test, if you have problems with sessions because of a slow database server

               $errno = 0;
               $errmsg = "";

               for ($i = 0; $i < $retry; $i++) {
                  $res = $this->query($query, $errno, $errmsg);

                  if ($errno == 0 && ((is_object($res) && $res instanceof mysqli_result && $res->num_rows > 0) || (is_resource($res) && mysql_num_rows($res) > 0))) {
                     break;
                  }
                  mssleep(99);
               }

               if ($errno != 0) {
                  $trigger_msg = "SessionHandlerMySQL: read SESSION from " . $this->m_db . "." . $this->m_table . " failed (" . $id . ")";
                  if ($this->m_err_details) {
                     $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")";
                  }
                  $this->writeLog($trigger_msg);
                  if ($this->m_throw_trigger)
                     trigger_error($trigger_msg, E_USER_WARNING);

                  return "";
               }
               if((is_object($res) && $res instanceof mysqli_result && $res->num_rows == 0) || (is_resource($res) && mysql_num_rows($res) == 0)){
                  $this->m_read = null;
                  return "";
               }

               if(is_object($res) && $res instanceof mysqli_result){
                  $this->m_read = $res->fetch_assoc();
               }else{
                  $this->m_read = mysql_fetch_assoc($res);
                  mysql_freeresult($res);
               }
               $res = NULL;
               unset($res);
               gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);

               if (left($this->m_read["data"], 3) == "gz:") {
                  $this->m_read["data"] = gzuncompress(substr($this->m_read["data"], 3));
               }
               if (left($this->m_read["data"], 7) == "base64:") {
                  $this->m_read["data"] = base64_decode(substr($this->m_read["data"], 7));
               }

               return $this->m_read["data"];
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function write($id, $data) {
               // $this->writeLog("write ".time()); // debug only

               if (!$this->m_connection) {
                  return true;
               }
               if (!$data) {
                  return false;
               }

               $errno = 0;
               $errmsg = "";

               $errno2 = 0;
               $errmsg2 = "";


               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BASE64"]) && $GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BASE64"] == true) {
                  $data = "base64:" . base64_encode($data);
               }

               if (isset($GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BIGDATA_COMPRESSED"])) {
                   if($GLOBALS["CRVCL"]["SESSION_HANDLER_STORE_BIGDATA_COMPRESSED"] === true){
                      $compress = 'auto';
                   }
                   $csize = strlen($data);
                   if ($compress == "auto") {
                        $compress = 4;
                     if ($csize > str2byteInt("18k")) {
                        $compress = 5;
                     } else if ($csize > str2byteInt("25k")) {
                        $compress = 6;
                     }
                   }     
                   $data = "gz:" . gzcompress($data, $compress);
               }


               if(is_object($this->m_connection) && $this->m_connection instanceof mysqli){
                  $data = $this->m_connection->real_escape_string($data);   
               }else{
                  $data = mysql_real_escape_string($data, $this->m_connection);
               }

               $query_type = '';
               $query = '';
               if ($this->m_read !== null) {
                  $query_type = 'UPDATE';
                  $query = 'UPDATE ' . $this->m_db . "." . $this->m_table . ' SET data = "' . $data . '", mtime = UNIX_TIMESTAMP() WHERE sessid = "' . $id . '"';
               } else {
                  $query_type = 'INSERT';
                  $prio = "";
                  if (strtoupper($this->m_db_engine) == "MEMORY" || strtoupper($this->m_db_engine) == "MyISAM") {
                     $prio = 'HIGH_PRIORITY ';
                  }
                  $query = 'INSERT ' . $prio . 'INTO ' . $this->m_db . '.' . $this->m_table . ' SET data = "' . $data . '", mtime = UNIX_TIMESTAMP(), sessid = "' . $id . '" ON DUPLICATE KEY UPDATE sessid = "' . $id . '"';
               }

               if (strtoupper($this->m_db_engine) == "MEMORY" || strtoupper($this->m_db_engine) == "MyISAM") {
                  $this->query('LOCK TABLES ' . $this->m_db . '.' . $this->m_table, $errno2, $errmsg2);
               }

               $data = NULL;
               unset($data);
               gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);

               $this->query($query, $errno, $errmsg);

               if (strtoupper($this->m_db_engine) == "MEMORY" || strtoupper($this->m_db_engine) == "MyISAM") {
                  $this->query('UNLOCK TABLES', $errno2, $errmsg2);
               } else if (strtoupper($this->m_db_engine) == "INNODB") {
                  $this->query('FLUSH TABLES ' . $this->m_db . '.' . $this->m_table, $errno2, $errmsg2);
               }


               if ($errno != 0) {
                  if (strtoupper($this->m_db_engine) == "MEMORY" && $errno == 1114) { // table full, try to drop old sessions in the half time of the GC
                     $gc_maxlifetime = ini_get("session.gc_maxlifetime");
                     $this->gc(intval($gc_maxlifetime / 2));
                     mssleep(500);
                     $this->query($query, $errno, $errmsg);
                     if ($errno == 0) {
                        return true;
                     }
                  }


                  $trigger_msg = "SessionHandlerMySQL: write (" . $query_type . ") SESSION to " . $this->m_db . "." . $this->m_table . " failed (" . $id . ")";
                  if ($this->m_err_details) {
                     $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")" . CRLF . $query;
                  }
                  $this->writeLog($trigger_msg);
                  if ($this->m_throw_trigger)
                     trigger_error($trigger_msg, E_USER_WARNING);
                  return false;
               }

               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function close() {
               // $this->writeLog("close ".time()); // debug only

               if (!$this->m_connection) {
                  return true;
               }
               $gc_maxlifetime = intval(ini_get('session.gc_maxlifetime'));
               $this->gc($gc_maxlifetime);

               if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "mysql") {
                  if(is_object($this->m_connection) && $this->m_connection instanceof mysqli){
                     $this->m_connection->close();
                  }else{
                     mysql_close($this->m_connection);
                  }
               }
               // $this->writeLog("close2 ".time()); // debug only
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function destroy($id) {
               // $this->writeLog("destroy ".time());  // debug only

               if (!$this->m_connection) {
                  return true;
               }
               $query = 'DELETE FROM ' . $this->m_db . "." . $this->m_table . ' WHERE sessid="' . $id . '"';
               $errno = 0;
               $errmsg = "";
               $this->query($query, $errno, $errmsg);
               if ($errno != 0) {
                  $trigger_msg = "SessionHandlerMySQL: delete SESSION from " . $this->m_db . "." . $this->m_table . " failed (" . $id . ")";
                  if ($this->m_err_details) {
                     $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")";
                  }
                  $this->writeLog($trigger_msg);
                  if ($this->m_throw_trigger)
                     trigger_error($trigger_msg, E_USER_WARNING);
                  return false;
               }
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
            public function gc($gc_maxlifetime) {
               if ($this->m_sess_expire > -1) {
                  $gc_maxlifetime = $this->m_sess_expire;
               }
               if (!is_numeric($gc_maxlifetime) || $gc_maxlifetime < 0) {
                  $gc_maxlifetime = 1440;
               }
               if (!$this->m_connection) {
                  return true;
               }

               $query = 'SHOW FULL PROCESSLIST';
               $errno = 0;
               $errmsg = "";
               $res = $this->query($query, $errno, $errmsg);
               if ($errno != 0) {
                  $trigger_msg = "SessionHandlerMySQL: check processlist to delete SESSION's failed";
                  if ($this->m_err_details) {
                     $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")";
                  }
                  $this->writeLog($trigger_msg);
                  if ($this->m_throw_trigger)
                     trigger_error($trigger_msg, E_USER_WARNING);
                  return false;
               }


               if((is_object($res) && $res instanceof mysqli_result)){
                  while ($row = $res->fetch_assoc()) { // don't execute delete at same time
                     if (stripos($row["Info"], 'DELETE FROM ' . $this->m_db . "." . $this->m_table . ' WHERE mtime <') !== false)
                        return true;
                  }                  
               }else{
                  while ($row = mysql_fetch_assoc($res)) { // don't execute delete at same time
                     if (stripos($row["Info"], 'DELETE FROM ' . $this->m_db . "." . $this->m_table . ' WHERE mtime <') !== false)
                        return true;
                  }
                  mysql_freeresult($res);
               }
               free($res);
               gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);

               ###

               $query = 'DELETE FROM ' . $this->m_db . "." . $this->m_table . ' WHERE mtime < UNIX_TIMESTAMP() - ' . $gc_maxlifetime;

               //$this->writeLog("gc ".time()." - ".$query); // debug only

               $errno = 0;
               $errmsg = "";
               $this->query($query, $errno, $errmsg);
               if ($errno != 0) {
                  $trigger_msg = "SessionHandlerMySQL: delete SESSION's from " . $this->m_db . "." . $this->m_table . " older than " . $gc_maxlifetime . " sec. failed";
                  if ($this->m_err_details) {
                     $trigger_msg .= "\r\n" . $errmsg . " (" . $errno . ")";
                  }
                  $this->writeLog($trigger_msg);
                  if ($this->m_throw_trigger)
                     trigger_error($trigger_msg, E_USER_WARNING);
                  return false;
               }
               mssleep(100);
               return true;
            }

//-------------------------------------------------------------------------------------------------------------------------------------
         }
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
         $session_handler_check_con = null;
         if(is_callable("mysqli_init")) {

            $tmp = explode(':', $GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"]);
            $server = $tmp[0];
            $port = 3306;
            if(isset($tmp[1])){
               $port = 3306;
            }

            $session_handler_check_con = mysqli_init();
            if ($session_handler_check_con instanceof mysqli){
               if (!$session_handler_check_con->real_connect((strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "pmysql" ? 'p:' : '') . $server, $GLOBALS["CRVCL"]["SESSION_HANDLER_USER"], $GLOBALS["CRVCL"]["SESSION_HANDLER_PW"], null, $port)) {
                  $session_handler_check_con = null;
               }
            }
         }else{
            if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "pmysql") {
               $session_handler_check_con = mysql_pconnect($GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"], $GLOBALS["CRVCL"]["SESSION_HANDLER_USER"], $GLOBALS["CRVCL"]["SESSION_HANDLER_PW"]);
            } else {
               $session_handler_check_con = mysql_connect($GLOBALS["CRVCL"]["SESSION_HANDLER_HOST"], $GLOBALS["CRVCL"]["SESSION_HANDLER_USER"], $GLOBALS["CRVCL"]["SESSION_HANDLER_PW"], true);
            }
         }
         
         $query = 'SHOW DATABASES LIKE "' . $GLOBALS["CRVCL"]["SESSION_HANDLER_DB"] . '"';
         $res = null;
         $errno = 0;
         if ($session_handler_check_con instanceof mysqli){
            $res = $session_handler_check_con->query($query);
            $errno = $session_handler_check_con->errno;
         }else{
            $res = mysql_query($query, $session_handler_check_con);
            $errno = mysql_errno($session_handler_check_con);
         }

         if ($errno != 0) {
            $trigger_msg = "SessionHandlerMySQL: check database \"" . $GLOBALS["CRVCL"]["SESSION_HANDLER_DB"] . "\" failed";            
            if ($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_THROW_TRIGGER"])
               trigger_error($trigger_msg, E_USER_WARNING);
            $session_handler_name = "";
         }else {
            if((is_object($res) && $res instanceof mysqli_result && $res->num_rows == 0) || (is_resource($res) && mysql_num_rows($res) == 0)){
               $trigger_msg = "SessionHandlerMySQL: database \"" . $GLOBALS["CRVCL"]["SESSION_HANDLER_DB"] . "\" not available";               
               if ($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_THROW_TRIGGER"])
                  trigger_error($trigger_msg, E_USER_WARNING);
               $session_handler_name = "";
            }
         }
         free($res);
         if (strtolower($GLOBALS["CRVCL"]["SESSION_HANDLER"]) == "mysql") {
            if ($session_handler_check_con instanceof mysqli){
               $session_handler_check_con->close();
            }else{
               mysql_close($session_handler_check_con);
            }
         }
         free($session_handler_check_con);
         gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
      } //SessionHandlerMySQL
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
      // init session handler
      if (!empty($session_handler_name)) {
         $session_handler_obj = "{$session_handler_name}";
         $session_handler = new $session_handler_obj;

         $session_set_save_handler = session_set_save_handler(
                 array($session_handler, 'open'), array($session_handler, 'close'), array($session_handler, 'read'), array($session_handler, 'write'), array($session_handler, 'destroy'), array($session_handler, 'gc')
         );
         if ($session_set_save_handler === false) {
            $trigger_msg = "SessionHandler: set session handler to " . $session_handler_name . " failed, maybe you start the session before you initialize the framework";            
            if ($GLOBALS["CRVCL"]["SESSION_HANDLER_ERROR_THROW_TRIGGER"])
               trigger_error($trigger_msg, E_USER_WARNING);
         }
      }
//-------------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------------
   } // iseet($GLOBALS["CRVCL"]["SESSION_HANDLER"])
############################################################
}
############################################################
?>
Return current item: crVCL PHP Framework