2017-02-17 9 views
6

Мы блокируем инвентарь для клиентов, таблица, в которой хранится инвентарь, содержит идентификатор сеанса, который заблокировал его вместе с другой информацией о клиенте. Когда сессия истекает, мы хотим разблокировать этот инвентарь, чтобы другие люди могли его купить. Поскольку мы регистрируем session_id() в таблице, зная это, есть ли способ проверить, активен ли сеанс в PHP?проверить, активен ли сеанс PHP, известный сеансовым

Если мы используем БД для хранения сеанса, мы можем, вероятно, проверить, существует ли строка до того момента, когда была последняя активность, в memcached мы, вероятно, можем определить ключ сеанса и проверить его так, для сеанса файла мы можем, вероятно, сделать то же самое, выяснить имя файла для сеанса и проверить, существует ли файл.

Есть ли что-то, что работает везде, где бы вы ни занимались?

+1

Почему вы не можете использовать базу данных? – sumit

+0

Я не один, чтобы спросить о чем-то подобном, но я бы начал с этого и пошел оттуда. 'if (session_status() === PHP_SESSION_ACTIVE) {} '. Я имею в виду, если это не сработает, мне придется копать глубже. :-) Это может помочь с первой частью вопроса. Существует также 'if (session_status() === PHP_SESSION_NONE) {}' –

ответ

1

Вы можете поместить код в свою функцию, которая выведет пользователя, который должен обрабатывать разблокировку инвентаря, связанного с этим сеансом.

+1

Ваше предложение не будет закрывать окно браузера. Мне все равно нужно разблокировать его потом. –

+0

См. Это для обработки этой ситуации: http: // stackoverflow.com/questions/13443503/run-javascript-code-on-window-close-or-page-refresh –

+0

Кроме того, таймаут в сеансе должен обрабатывать ведение журнала использования. –

1

Закрытие окна браузера вызывает событие beforeunload, что может обрабатываться только Javascript. Вы можете поместить в обработчик событий Javascript для этого события, которое делает вызов AJAX обратно на сайт, например, так (JQuery пример, приведенный):

$(window).bind("beforeunload", function() { 
    $.ajax({ 
     url: "http://example.com/destroySession.php" 
    }); 
}); 

Это перезвонит на сайте может быть против страницы, которая разблокирует элементы инвентаря в отношении идентификатора sessionID пользователя, после чего вызывается команда PHP session_destroy(). Например:

<?php 

// Load your config bootstrap/etc. 

session_start(); 

// Unlock target inventory items 

session_destroy(); 

?> 
+0

Итак, если пользователь сохраняет свой браузер открытым, элемент никогда не будет разблокирован? –

+0

Не обязательно, сессия может иметь таймаут. OP не предоставил код, который обрабатывает ввод/проверку сеанса в таблице базы данных. Без этой информации я могу только постулировать. –

2

Вы не можете использовать session_id, session_start и т.д.

Эти функции обрабатывают сеанс пользователя, который запрашивает страницу. Вы не можете использовать их для легкого управления списком всех сеансов пользователей. Вы должны беспокоиться о тех же правах для созданных пользователем сеансов (www-data) и скрипта cli, который будет выполнен для поиска, который должен быть разблокирован - файлы сессий предназначены только для владельцев.

В результате доступа сеанса доступа время изменено, поэтому каждый чек на сеанс, который вы меняете, влияет на поведение сеанса и влияет на его поведение.

Как выполнить блокировку легче

Обычно блокировка товаров производится только в течение фиксированного периода времени, скажем, 20мин. Но если вы хотите провести инвентаризацию, выбранную во время сеанса, вы можете использовать небольшую модификацию существующего кода.

  1. Добавить новое поле last_activity рядом с session_id полем (один простой миграции)
  2. обновления Каждый запрос его NOW (просто позвоните $row->touch() на модели - docs).
  3. Задайте задачу crontab, чтобы удалить каждую строку с last_activity < NOW - delta, где delta - это тайм-аут сеанса.

Используя этот подход, вы можете избежать использования каждого драйвера сеанса.

P.S. Вероятно, вы захотите убедиться, что сеанс мертв через определенное время, read this question. Если эта работа выполняется сборщиком мусора, срок ее службы может меняться.

+0

Я знаю о сборщике мусора и не должен на него рассчитывать. Я пытался убедить бизнес, что он должен быть заблокирован в течение определенного периода времени, а не во время входа пользователя в систему, однако это было обещано клиенту до того, как я пришел на борт, поэтому нам просто нужно это сделать. Способ предложить сеанс именно так, как я хотел его реализовать, но мне сказали, что у нас есть обязательства. –

+0

@MihaiP. Что делать, если вы продлеваете время блокировки во время входа в систему и активно? Это будет соответствовать требованиям бизнеса. –

+0

Тем не менее вам необходимо настроить сеансы. Вы не можете разрешать пользователям блокировать объекты навсегда, если они не открывают какую-либо страницу. Что означает «во время входа пользователя»? Может ли он заблокировать предметы навсегда, если он проверит «помни меня»? –

2

Итак, вы спрашиваете, существует ли общий подход в PHP для доступа к любому сеансу. Хотя вариант использования в вопросе правдоподобен, влияние того, что вы можете делать то, что вы предлагаете, представляет собой серьезный риск для безопасности.

Это одна из причин, почему встроенные функции сеанса в PHP делают то, что вам сложно.

В теории вы можете использовать для указания конкретных идентификаторов сеанса и поиска их. Я только что сделал несколько простых тестов и не имел большого успеха. Существует только одна встроенная функция для загрузки сеансов 'session_start', которые нужно будет вызывать повторно. В руководстве конкретно сказано, что это не сработает:

Начиная с PHP 4.3.3, вызов session_start() после предыдущего сеанса приведет к ошибке уровня E_NOTICE. Кроме того, запуск второго сеанса будет просто проигнорирован.

Возможно, все еще возможно обойти это, возможно, с помощью разветвления или других умных скриптов. Но ваш код будет работать неясным способом, который может порвать с будущими обновлениями PHP или, возможно, вмешиваться в существующие сеансы Live.

Лучшим решением было бы написать инструмент, специфичный для используемого обработчика сеанса, который позволяет читать только доступ к сеансу. Сценарий в вопросе даже не нуждается в доступе к данным сеанса, просто информация о временной отметке, которая может быть использована для вычисления времени истечения.

В случае обработки сеансов «файлы». Путь к хранилищу файлов сеанса можно открыть с помощью ini_get('session.save_path');. Сценарий проверки может потребоваться выполнить с теми же правами, что и веб-сервер, чтобы получить доступ к этому местоположению.

Скрипт должен часто запускаться по расписанию, чтобы проверять истекшие сеансы и удалять блокировки из инвентаря.

+0

Я согласен, что я пытаюсь сделать, не чувствует себя хорошо :). После этого я буду чувствовать себя грязным. То, что мы сделали прямо сейчас, это использовать memcached как для сеанса, так и для кэширования. Поскольку Laravel использует идентификатор сеанса в качестве ключа memcached, я считаю, что мы можем просто проверить, существует ли ключ и выяснить его, если сеанс все еще активен или нет. –

+0

Это должно сработать. Ключ сеанса memcached будет (должен быть для предотвращения коллизий) больше, чем только идентификатор сеанса. [Сбросьте ключи] (http://stackoverflow.com/questions/19560150/get-all-keys-set-in-memcached), чтобы выработать шаблон, который вы ищете. –

+0

Из моих тестов это тот же самый ключ сеанса, что и ключ memcached. Он должен работать легко. –

1

session_status доступен для PHP v5.4 и более поздних версий, возможно, именно поэтому.

Вы можете попробовать с session_id:

session_id() возвращает идентификатор сессии для текущей сессии или пустой строки («»), если нет текущей сессии (нет текущого идентификатора сессии не существует) ,

+0

session_status только проверяет состояние сеанса для текущего пользователя. Мне нужно проверить статус сеанса другого сеанса, который я знаю только из идентификатора сеанса. –

1

Что вы можете сделать - это сохранить флаг (например, last_activity) в каждом сеансе пользователя, чтобы проверить активность вашего сеанса и выяснить, следует ли разблокировать элемент или заблокировать его. Вот что работает для меня:

$sessionId = get_session_id_that_locked_the_item_from_db(); 

if(session_status() !== PHP_SESSION_ACTIVE) { 
    session_start(); 
} 
// get current session id. 
$current_id = session_id(); 
// renew activity. 
echo 'Renewed my activity.'; 
$_SESSION['active'] = 1; 
// Close the current session 
session_write_close(); 

if($current_id === $sessionId) { 
    // the current user has locked the item. 
} 
// Check if the var active of this session exists. 
elseif(!checkSessionlastActivity($sessionId, $current_id)) { 
    // Show the inventory item. 
    showInventoryItem(); 
    // Lock inventory when the item is selected(eg added to basket). 
    if('I want to lock the item') { 
     lockInventoryItem($current_id); 
    } 
} 
else { 
    echo 'Item is locked.'; 
} 

function checkSessionlastActivity($id, $current_id) { 
    // switch session to the one that locked the item. 
    session_id($id); 
    session_start(); 
    // get session is active. 
    $active = (isset($_SESSION['active'])? $_SESSION['active']: null); 
    // Close the session. 
    session_abort(); 
    // restore current session. 
    session_id($current_id); 
    session_start(); 

    if($active !== null) { 
     return true; 
    } 
    return false; 
} 

function lockInventoryItem($current_id) { 
    put_my_session_id_to_db($current_id); 
} 

function showInventoryItem() { 
    echo 'You can select item'; 
} 

Примечание: Я не уверен, если это будет работать для различных систем. Это зависит от настроек сеанса php.

Мнение: Сессия используется для определенной функциональности. Я думаю, что переключение между сеансами, принадлежащими разным пользователям, не означает, для каких сеансов были разработаны.Во всяком случае, я бы посоветовал вам использовать этот обходной путь, пока вы не реализуете решение для блокировки для вашего инвентаря.

В случае, если вы будете полагайтесь ли сеанс истек или нет, проверьте настройки сессии (session.gc_maxlifetime, session.gc_probability & session.gc_divisor), а также это может helpful.

+0

Если я запустил сеанс, чтобы проверить его, если он активен, это не будет приостанавливать сеанс неопределенно долго? –

+0

Да, на самом деле это обновит временную метку сеанса, но в моем решении вы не полагаетесь на обновленную метку сессии, но «last_activity». Если вам это интересно, используйте session_abort() вместо session_write_close() для сеанса db. Я уточню свой ответ, есть еще несколько вещей, которые вам нужно рассмотреть. –

3

Для этой цели можно использовать session_id и session_start.

$ids = [ 
      '135b29ef958a23418f2a804474787305', // active session 
      '135b29ef958a23418f2a804474787306', // inactive session 
      '135b29ef958a23418f2a804474787305', // active session 
     ]; 

foreach($ids as $id) 
{ 
    session_id($id); 
    session_start(); 

    $status = isset($_SESSION['logged_in']); 

    print(($status ? 1 : 0) . PHP_EOL); 
    session_abort(); 
} 

Проверьте, существует ли всегда установленная переменная сеанса. Чтобы убедиться, что это не новый сеанс.

Вам нужно будет проверить, не сбрасывает ли счетчик времени жизни на сеанс. На моей системе это не влияет на срок службы пока что-то изменится в сессии

Edit: обновляемые с session_abort Переберите и проверить несколько сеансов Идентификаторы

+0

Спасибо, я не знал о session_abort(). Вы уверены, что когда вы откроете сеанс, он еще не расширен? Я бы не хотел, чтобы сессия продолжилась навсегда. –

+0

@Sander Backus. Мне любопытно, ты это проверил? Это не сработало, когда я попробовал, мне было бы интересно узнать, есть ли способ заставить его работать. –

+0

Я проверил это. Как сказал @ shukshin.ivan, вы должны убедиться, что у процесса есть разрешения на чтение сеанса (например, запустите его через ваш веб-сервер, а не в cli). В моей системе временные метки файлов сеанса не изменяются. И с помощью специальных обработчиков сеансов метка времени также не должна меняться.Я не уверен, как это обрабатывается, например, memcached. Возможно, вы могли выполнить ini_set session.gc_probability, session.gc_divisor перед запуском этого, чтобы убедиться, что сбор мусора запущен, прежде чем вы проверяете, существуют ли сеансы? Затем проверьте каждую сессию сразу после таймаута, чтобы убедиться, что они не будут расширяться. –

3

Проверка одного сеанса существуют в папке сессии:

echo ini_get('session.save_path'); 

, например, в моей папке файл идентификатор сессии:

sess_l75lm4ktlhvlu566n8i6do8k71 

Набор SES Sion parametrs (например):

ini_set ('session.cookie_lifetime', 100)

После session_destroy(); Файл был удален из папки сеанса:

 if (file_exists($sessionfile)) { 
     echo "exists"; 
    }else{ 
     echo "not exist" 
    } 

Или Вы можете отправить запрос Js, когда близко браузер и разблокировать продукт (светлячок тестировался):

<script type="text/javascript"> 
    // alert(document.cookie); 

    window.onbeforeunload = function() { 
    return "Do you really want to close?"; 
    }; 
    window.onunload = function() { 
    return "Do you really want to close?"; 
    }; 

    $(window).on('beforeunload', function(){ alert ('Send post to unlock product')}); 
    $(window).unload(function() { alert("'Send post to unlock product'"); }); 
</script> 
+0

Это решение да, однако это учитывает только сеанс файла. Сессия может быть где угодно (DB, file, memcached). Я надеялся, что есть решение, которое было бы общим. Я понимаю, что я могу сделать что-то подобное, если сессия находится в БД или memcached. –

+0

PHP записывает информацию о сеансе на все серверы, указанные в session.save_path; аналогично RAID-1. Вам нужны файлы cookie сеанса. –

+0

Или добавьте id (заблокированные элементы) в сессионный cookie из PHP, а затем все пользователи знают –

1
// correct way 
$id = 'abc'; 
session_id($id); 
session_start(); 
$isActive = (session_status() === PHP_SESSION_ACTIVE); 
session_write_close(); 
var_dump($isActive); 

// tricky way (root required) 
$id = 'abc'; 
$isActive = false; 
$sessionTimeout = (int)ini_get('session.gc_maxlifetime'); 
$rdi = new RecursiveDirectoryIterator(session_save_path(), FilesystemIterator::SKIP_DOTS); 
$rii = new RecursiveIteratorIterator($rdi); 
$ri = new RegexIterator($rii, preg_quote("/sess_$id/"), RegexIterator::MATCH); 
/** 
* @var $fileInfo SplFileInfo 
*/ 
foreach ($ri as $fileInfo) { 
    $expiredAt = $fileInfo->getMTime() + $sessionTimeout; 
    $isActive = $expiredAt > time(); 
    if ($isActive) { 
     break; 
    } 
} 
var_dump($isActive); 

Как правило, для хранения идентификатора сессии в БД и не держать сессию там - это плохая идея.

Посмотрите на http://php.net/manual/en/function.session-set-save-handler.php

Основываясь на своем профиле, вы знаете Yii. Существует хороший пример реализации:

https://github.com/yiisoft/yii2/blob/master/framework/web/DbSession.php

https://github.com/yiisoft/yii2/blob/master/framework/web/Session.php#L97

https://github.com/yiisoft/yii2/blob/master/framework/web/Session.php#L148