У меня есть одна страница, где я делаю длинный опрос, который я должен использовать в начале этой страницы.
session_start(); session_write_close();
Потому как :
для предотвращения одновременной записи только один скрипт может работать на сеансе в любое время
Поэтому, если я этого не сделаю, и длительный опрос работает, пользователь не сможет загрузить другую страницу.
Таким образом, доступ к моим данным в сеансе с этой страницы опроса возможен, но в какой-то момент моего сценария я должен сохранить сеанс на сервере, потому что я внес некоторые изменения в него.
Каким образом это сделать?
Это будет очень хорошо, это будет способ сделать что-то вроде
session_write_open(); //do stuff session_write_close();
Но session_write_open () не существует!
благодаря
Прежде чем внести изменения в сеанс, вызовите session_start
раз. Внесите изменения, и если вы еще не хотите снова выходить из вызова session_write_close
. Вы можете делать это столько раз, сколько хотите.
Предыдущее решение создаст идентификаторы сеанса и файлы cookie … Я бы не использовал его как есть:
Сессия создается каждый раз, когда вы вызываете session_start (). Если вы хотите избежать нескольких файлов cookie, напишите лучший код. Несколько session_start (), особенно для тех же имен в одном скрипте, кажется очень плохой идеей.
см. здесь: https://bugs.php.net/bug.php?id=38104
Я ищу решение прямо сейчас, и я не могу его найти. Я согласен с теми, кто говорит, что это «ошибка». Вы должны иметь возможность повторно открыть сеанс php, но, как вы сказали, session_write_open()
не существует …
Я нашел обходной путь в вышеупомянутой теме. Он включает отправку заголовка, указывающего вручную файл cookie идентификатора сеанса после обработки запроса. К счастью, я работаю с домашним контроллером Front Controller, который работает так, что никакой субконтроллер никогда не будет отправлять данные самостоятельно. В двух словах, он отлично работает в моем случае. Чтобы использовать это, вам просто нужно использовать ob_start()
и ob_get_clean()
. Вот волшебная линия:
if (SID) header('Set-Cookie: '.SID.'; path=/', true);
EDIT: см. Ниже ответ CMCDragonkai, кажется хорошим !?
Другие ответы здесь представляют довольно хорошие решения. Как упоминалось в @Jon, трюк состоит в том, чтобы снова вызвать session_start (), прежде чем вы захотите внести изменения. Затем, когда вы закончите внесение изменений, снова вызовите session_write_close ().
Как уже упоминал @Armel Larcier, проблема заключается в том, что PHP пытается генерировать новые заголовки и, скорее всего, генерирует предупреждения (например, если вы уже написали данные без заголовка клиенту). Конечно, вы можете просто префикс session_start () с помощью «@» (@session_start ()), но есть лучший подход.
Еще один вопрос переполнения стека, предоставленный @VolkerK, показывает лучший ответ:
session_start(); // first session_start ... session_write_close(); ... ini_set('session.use_only_cookies', false); ini_set('session.use_cookies', false); //ini_set('session.use_trans_sid', false); //May be necessary in some situations ini_set('session.cache_limiter', null); session_start(); // second session_start
Это предотвращает попытку PHP снова отправлять заголовки. Вы могли бы даже написать вспомогательную функцию, чтобы обернуть функции ini_set (), чтобы сделать это немного более удобным:
function session_reopen() { ini_set('session.use_only_cookies', false); ini_set('session.use_cookies', false); //ini_set('session.use_trans_sid', false); //May be necessary in some situations ini_set('session.cache_limiter', null); session_start(); //Reopen the (previously closed) session for writing. }
Оригинальный вопрос и ответ: https://stackoverflow.com/a/12315542/114558
После проверки работы Армеля Ларчье. Вот мое предлагаемое решение этой проблемы:
ob_start(); session_start(); session_write_close(); session_start(); session_write_close(); session_start(); session_write_close(); session_start(); session_write_close(); if(SID){ $headers = array_unique(headers_list()); $cookie_strings = array(); foreach($headers as $header){ if(preg_match('/^Set-Cookie: (.+)/', $header, $matches)){ $cookie_strings[] = $matches[1]; } } header_remove('Set-Cookie'); foreach($cookie_strings as $cookie){ header('Set-Cookie: ' . $cookie, false); } } ob_flush();
,ob_start(); session_start(); session_write_close(); session_start(); session_write_close(); session_start(); session_write_close(); session_start(); session_write_close(); if(SID){ $headers = array_unique(headers_list()); $cookie_strings = array(); foreach($headers as $header){ if(preg_match('/^Set-Cookie: (.+)/', $header, $matches)){ $cookie_strings[] = $matches[1]; } } header_remove('Set-Cookie'); foreach($cookie_strings as $cookie){ header('Set-Cookie: ' . $cookie, false); } } ob_flush();
Это сохранит все файлы cookie, которые были созданы до работы с сеансами.
BTW, вы можете захотеть зарегистрировать вышеуказанный код как функцию для register_shutdown_function. Обязательно запустите ob_start () перед функцией и ob_flush () внутри функции.
Все ответы здесь, кажется, говорят, чтобы использовать методы сеанса таким образом, чтобы они явно не предназначались для использования … а именно для вызова session_start()
более одного раза.
Веб-сайт PHP предлагает пример реализации SessionHandlerInterface, который будет работать так же, как и существующие сеансы, но без блокировки файла. Просто реализуя свой примерный интерфейс, я исправил проблему с блокировкой, чтобы разрешить одновременные соединения на одном сеансе, не ограничивая возможность добавления vars в сеанс. Чтобы предотвратить некоторые условия гонки, поскольку сеанс приложения не является полностью безгосударственным, мне пришлось сделать способ сохранить средний запрос сеанса, не закрывая его, чтобы важные изменения могли сэкономить сразу после изменения, а менее важные сеансовые вары могли просто сохранить в конце запроса. См. Приведенный ниже пример использования:
Session::start(); echo("<pre>Vars Stored in Session Were:\n");print_r($_SESSION);echo("</pre>"); $_SESSION['one'] = 'one'; $_SESSION['two'] = 'two'; //save won't close session and subsequent request will show 'three' Session::save(); $_SESSION['three'] = 'three';
Если вы замените Session::start()
на session_start()
и Session::save()
с session_write_close()
, вы заметите, что последующие запросы никогда не распечатывают третью переменную … она будет потеряна. Однако, используя SessionHandler (ниже), данные не теряются.
Для реализации OOP требуется PHP 5.4+. Тем не менее, вы можете предоставить отдельные методы обратного вызова в старых версиях PHP. См. Документы .
namespace { class Session implements SessionHandlerInterface { /** @var Session */ private static $_instance; private $savePath; public static function start() { if( empty(self::$_instance) ) { self::$_instance = new self(); session_set_save_handler(self::$_instance,true); session_start(); } } public static function save() { if( empty(self::$_instance) ) { throw new \Exception("You cannot save a session before starting the session"); } self::$_instance->write(session_id(),session_encode()); } public function open($savePath, $sessionName) { $this->savePath = $savePath; if (!is_dir($this->savePath)) { mkdir($this->savePath, 0777); } return true; } public function close() { return true; } public function read($id) { return (string)@file_get_contents("$this->savePath/sess_$id"); } public function write($id, $data) { return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true; } public function destroy($id) { $file = "$this->savePath/sess_$id"; if (file_exists($file)) { unlink($file); } return true; } public function gc($maxlifetime) { foreach (glob("$this->savePath/sess_*") as $file) { if (filemtime($file) + $maxlifetime < time() && file_exists($file)) { unlink($file); } } return true; } }