Работа с PHP сессиями в БД на примере

от автора

В статье "Горизонтальное масштабирование PHP приложений. Ч.1" упомянута возможность использования балансировщика, а значит требуется централизованное хранение сессий. В качестве одного из вариантов названо хранение в MySQL базе. В этой статье хочу показать пример конкретной реализации — может, не самый оптимальный, но работающий. Для желающих улучшить — допилить напильником.

Пример взят из исходных кодов скрипта интернет-магазина ShopCMS, ошибки исправил сам, лишнее убрал для удобства представления Хабрачитателям.

Сессии хранятся в БД MySQL, таблица session.

CREATE TABLE IF NOT EXISTS `session` (   `id` varchar(32) NOT NULL,   `data` text NOT NULL,   `expire` int(11) NOT NULL DEFAULT '0',   `IP` varchar(15) NOT NULL,   `Referer` varchar(255) NOT NULL,   `user_agent` varchar(255) NOT NULL,   `URL` text NOT NULL,   PRIMARY KEY (`id`),   KEY `expire` (`expire`) ) ENGINE=MyISAM DEFAULT CHARSET=cp1251; 

В самом начале вызывается функция, которая переопределяет стандартные функции работы с сессиями

// CONF_SECURITY_EXPIRE - параметр конфигурации, число дней хранения сессии define( "SECURITY_EXPIRE", 60*60*24 * CONF_SECURITY_EXPIRE );  session_set_save_handler( "sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc" ); session_set_cookie_params( SECURITY_EXPIRE ); session_start( ); 

Описание (отсюда, подробнее можно уточнить тут на английском):

session_set_save_handler() устанавливает пользовательские функции хранения сессии, которые используются для хранения и запрашивания данных, ассоциированных с сессией.Это чаще всего используется, когда метод хранения отличается от методов PHP-сессий. Например, при хранении данных сессии в локальной БД.
Возвращает TRUE при успехе, FALSE при неудаче.

Примечание:
вы обязаны установить в опции конфигурации session.save_handler значение user в вашем файле php.ini, для того чтобы session_set_save_handler() работала.

Примечание:
обработчик «write» не выполняется после закрытия потока вывода.Таким образом, вывод отладочных операторов в обработчике «write» никогда не будет виден в браузере.Если необходим вывод отладки, можно записать его в файл.
Функция read должна всегда возвращать значение, чтобы обработчик сохранения всегда работал, как ожидается. Возвращает пустую строку, если нет данных для чтения. Return-значения из других обработчиков конвертируются в булево выражение. TRUE при успехе, FALSE при неудаче.

Сервисные функции и определения

if (  !defined('SESSION_TABLE')  ) define('SESSION_TABLE', 'session');  

// Позволяет получить реальный адрес IP клинта даже при работе через Nginx proxy function get_real_ip(){ 	if(isset($_SERVER['HTTP_X_REAL_IP']) && $_SERVER['HTTP_X_REAL_IP'] != "" ){ 		return (string)$_SERVER['HTTP_X_REAL_IP']; 	}     elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != ""){         $client_ip = (isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : getenv('REMOTE_ADDR');         $entries = split('[, ]', $_SERVER['HTTP_X_FORWARDED_FOR']);         reset($entries);         while(list(, $entry) = each($entries)){             $entry = trim($entry);             if(preg_match("/^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/", $entry, $ip_list)){                 $private_ip = array(                         '~^0\.~', '~^127\.0\.0\.1~', '~^192\.168\..*~',                         '~^172\.((1[6-9])|(2[0-9])|(3[0-1]))\..*~', '~^10\..*~');                 $found_ip = preg_replace($private_ip, $client_ip, $ip_list[1]);                 if($client_ip != $found_ip){                     $client_ip = $found_ip;                     break;                 }             }         }     }else{         if(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] != "") return (string)$_SERVER['REMOTE_ADDR'];         if(getenv("REMOTE_ADDR")) return (string)getenv('REMOTE_ADDR');         return false;     }     return (string)$client_ip; } 

Функции работы с сессией

function sess_open($save_path, $session_name)   {   return true;   } 

  function sess_close()   {   return true;   } 

  function sess_read($key)   {   $r = mysql_query("select data, IP, user_agent from ".SESSION_TABLE." where id='".mysql_real_escape_string($key)."'");   if (!$r){     return "";   }   else{     $result = mysql_fetch_row($r);     if (!empty($result)){          if (get_real_ip() != $result[1] || strncmp(getenv('HTTP_USER_AGENT'), $result[2], 255)) {              mysql_query("delete from ".SESSION_TABLE." where id='".mysql_real_escape_string($key)."'");             return "";          }        return $result[0];       }     else{       return "";     }    }   } 

  function sess_write($key, $val)   {   mysql_query("replace into ".SESSION_TABLE." values ('".mysql_real_escape_string($key)."', '".mysql_real_escape_string($val)."', UNIX_TIMESTAMP() + ".SECURITY_EXPIRE.", '".mysql_real_escape_string(get_real_ip())."', '".mysql_real_escape_string(getenv('HTTP_REFERER'))."', '".mysql_real_escape_string(getenv('HTTP_USER_AGENT'))."', '".mysql_real_escape_string(getenv('REQUEST_URI'))."')");   } 

  function sess_destroy($key)   {   mysql_query("delete from ".SESSION_TABLE." where id='".mysql_real_escape_string($key)."'");   return true;   } 

  function sess_gc($maxlifetime)   {   db_query("delete from ".SESSION_TABLE." where expire < UNIX_TIMESTAMP()");   } 

Надеюсь, будет полезно в ваших проектах. Удачи!

ссылка на оригинал статьи http://habrahabr.ru/post/210662/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *