2012-04-06 2 views
16

Я одну страницу, где я сделать длинный опрос я использовать на начало этой страницы этоPHP Сохранить сеанс при использовании session_write_close();

session_start(); 
session_write_close(); 

Потому что:

для предотвращения одновременных операций записи только один сценарий может работать на сессия в любое время

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

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

Каким образом это сделать?

Это будет очень хорошо, это будет способ сделать что-то вроде

session_write_open(); 
//do stuff 
session_write_close(); 

Но session_write_open() не существует!

Благодаря

+0

запозданием, для будущих читателей, я предлагаю вам использовать 'session_set_save_handler()' как более передовой практики, поскольку она не предполагает каких-либо обходных, но модифицирует сессии в качестве авторов PHP казалось, предназначались. Я опубликовал пример того, как это сделать ниже. –

ответ

13

Перед вы внести некоторые изменения в сессии, вызовите session_start снова. Внесите изменения, и если вы все еще не хотите снова выходить из вызова session_write_close. Вы можете делать это столько раз, сколько хотите.

+0

Почему он работает для меня без «session_start»? Я просто делаю session_write_close в самом начале моего скрипта, содержащего много логики, включая обновления сеанса, и все работает нормально, корректно обновляемый сеанс –

+0

@VictorBredihin, не глядя на фактический код, я понятия не имею, что может произойти.Возможно, вы могли бы задать новый вопрос. – Jon

10

Предыдущее решение будет создавать идентификаторы сессии и куки ... Я бы не использовать его как:

Сессия создается при каждом вызове 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, кажется хорошим !?

+0

Хорошо, я не знал о куки-файлах, да звонил session_start(); не имеет большого смысла, но у меня нет другого решения для решения проблемы, о которой я знаю;) Спасибо за информацию об ошибке! –

+0

Это может быть просто функция, которая отсутствует ... –

+1

Обнаружено обходное решение! отредактированный ответ! –

3

После проверки работы Armel Larcier.Вот мое предложенное нами решение этой проблемы:

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() внутри функции.

4

Другие ответы здесь представляют довольно хорошие решения. Как упоминалось в @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. 
} 

связанные SO Оригинальный вопрос/ответ: https://stackoverflow.com/a/12315542/114558

+1

Это хорошая находка. Лучшим решением было бы просто никогда не выводить содержимое в куски (всегда работал для меня), и в этом случае вы получаете несколько заголовков, но они безвредны. Однако это может быть невозможно, если, например, вы унаследовали некоторый код, поэтому этот метод обхода имеет допустимый прецедент. – Jon

+0

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

3

Все ответы здесь, кажется, говорят использовать методы сеанса таким образом, чтобы они явно не предназначались для использования ... а именно для вызова 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. See docs.

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; 
     } 
    } 
Смежные вопросы