php session write close
session_write_close
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
session_write_close — Записывает данные сессии и завершает её
Описание
Завершает текущую сессию и сохраняет её данные.
Список параметров
У этой функции нет параметров.
Возвращаемые значения
Возвращает true в случае успешного выполнения или false в случае возникновения ошибки.
Список изменений
Смотрите также
User Contributed Notes 27 notes
You can have interesting fun debugging anything with sleep() in it if you have a session still active. For example, a page that makes an ajax request, where the ajax request polls a server-side event (and may not return immediately).
If the ajax function doesn’t do session_write_close(), then your outer page will appear to hang, and opening other pages in new tabs will also stall.
Closing the session and then manipulating session variables is not something many would do by intent. However, if your sessions suddenly start misbehaving, failing to record changes etc it is well worth checking that the cause is not this one!
Workaround if session_write_close() still doesn’t write sessions fast enough:
i have been using a mysql database custom session handler (not using cookies) and was having problems getting the session data to be saved consistantly on my database driven website
clients also have to navigate across domains for website management at times so i have some special code to make all this happen relatively seamlessly and to ensure that each person’s data is secure.
i added session_write_close() at the very end of the last script that set session data and solved the problem.
i am not sure why, but it seems the calls to write and close were not always being made (i was not smart enough to figure it out)
hope this helps someone.
Why this function is highly important? I explain.
In a nutshell, how session works:
Client side: php is sending back to the client, a cookie with the id of the session. So, the session ends when the server ends to process the script and not when session_write_close() is executed. So, using session_write_close() for fast saving the session in the client side is useless unless you are ob_flush() and flush() to the customer.
Server side: It could be changed but the normal behavior is to save the session information in a file. For example:
**This file is usually locked**, so if two sessions are trying open at the same time, then one is freezed until the file is unlocked. session_write_close() ends the lock.
Its caused because the first process lock the session file.
For PHP 7.0 and higher, we could use session_start(true); for auto close after the first read.
This operation is highly important for AJAX when we used to do many operations in parallel by using the the same session
Beware, if you overwrite the default PHP Session handling and use debugging code inside the write() function, the debugging code is not executed until you run session_write_close().
Maybe intuitive to many, it took days to realize. hope it helps!
i had trouble with 2 concurrent scripts, a login form with a php generated captcha. the latter truncated the session (session file length 0) so the login always failed due token or captcha error. i thought that was a session blocking problem, but was not
// captcha.php pseudo code
but seems pipeline produced serialization errors on session file, so changing to another inocuous character (underscode in my case) solved the problem. try to avoid reserved serialization characters |:<>«;
I had a problem with realizing the restore password form. First a user entered his login or e-mail in the system.
Then the script searched the database, got the session data, and sended link with SID to registered e-mail. The link was configured so, that it restored session data and logged user in the secure interface to the change password form.
Then was displayed a page with the message about sended message.
The problem was that ID was not unique in three pages, the SID sended to e-mail anyone could see in cookie.
I tryed to start new session before generating and after sending link with the code:
.
session_start ();
/*Getting user login and e-mail from database*/
$user_login = «. » ;
$user_id = «. «
/*CLOSE PREVIOUS SESSION*/
session_unlink ();
session_destroy ();
/*CLOSE THE SESSION WITH USER DATA*/
session_write_close ();
/*AND STARTING A NEW SESSION*/
session_start ();
/*THEN LOAD THE ‘MESSAGE SENDED’ PAGE*/
header ( «Location: /restore/message_sended/» );
?>
The trouble was that SID was the same even after session_unlink() and session_write_close(). The session_start() function just restored the previous session data. So the script was not safe.
Then I added session_regenerate_id() call after each session_start().
.
session_start ();
/*Getting user login and e-mail from database*/
$user_login = «. » ;
$user_id = «. «
/*CLOSE PREVIOUS SESSION*/
session_unlink ();
session_destroy ();
/*NOW GENERATING LINK FOR SESSION DATA */
session_start ();
session_regenerate_id (); //Regenerating SID for sending
/*CLOSE THE SESSION WITH USER DATA*/
session_write_close ();
/*AND STARTING ANOTHER NEW SESSION*/
session_start ();
session_regenerate_id (); //Regenerating SID
/*THEN LOAD THE ‘MESSAGE SENDED’ PAGE*/
header ( «Location: /restore/message_sended/» );
?>
And now it works as needed! The SID sending to user we cannot see in cookies nor before neither after generated link, but the data is saved in session with this id. So only the owner of account can get it!
As we all know, if an object is serialised, then the class definition must be included _before_ it is unserialised.
My framework has an enormous number of class files, and including them all at the beginning of the script was really taking it’s toll on my system (memory and execution time) so I switched to including required classes at the top of each class file that used them using require_once.
This caused problems because I start my session at the very beginning of my script’s execution, but all my class files aren’t there at the beginning!!
So no in my special ‘require’ function, I do the following:
if(!class_exists($to_require))
<
session_write_close();
require_once(‘path/to/classes/’.$to_require.’.php’);
session_start();
>
This is a considerably smaller performance hit that including every class that the application uses at the very beginning of the application.
You can easily make a cool chatbox without using frames and subdomains in combination with SSE (server side events), using for example a ‘while(true)
Using session_write_close() prevents the session being locked (because the request ‘never’ ends (maybe after a minute or two.. but otherwise the page would hang).
So you can make a chatbox without shell access on shared hosting, you just need to make a ‘output all clients the new messages’ function for the SSE stream and code a few lines of javascript. Read up on SSE.
Obviously need a good caching or fast database with a lot of clients, because everyone will spawn a new stream connection. (in contrast to push mechanisms which will require at least a cron job on shared hosting).
I was having the same problem as many here regarding setting session data just before a header location redirect and having the session data just not be there. I tried everything people here said, and none of their combinations worked. What did finally work for me was to fire off a session_regenerate_id(true) call just prior to the header() and die() calls.
session_regenerate_id(true);
header(‘location: blah blah’);
die();
Without the regenerate id call, the write close did not seem to do anything. session_write_close() doesn’t seem to matter at all. It certainly didn’t fix anything on its own for me.
This is a rather annoying issue with php sessions that I’ve never run into before. I store my sessions to /dev/shm (which is RAM) so file IO blocking can’t be the problem. Now I’m nervous that some other session data might not be getting updated prior to a header() location change, which is extremely important and common in any web app.
session_write_close
(PHP 4 >= 4.0.4, PHP 5, PHP 7)
session_write_close — Write session data and end session
Описание
End the current session and store session data.
Возвращаемые значения
Эта функция не возвращает значения после выполнения.
Смотрите также
Коментарии
if you are trying to work with a larger code base meant for a specific application. and it implements some custom session save handlers, it appears there is no way to reset those save handlers back to the default php state if they are getting in your way. my workaround:
session_write_close(); // close the session at the top of the page 🙂
session_write_close() worked as a lifesaver for me when automatically uploading files to a user (forcing a download instead of a link). If files are large, and since session_start() does not allow another page using session_start() to proceed until it’s done, i was not able to upload more than one file at a time. By using session_write_close() before beginning the file upload, my users can now download as many big files as they like, at the same time. Example:
As we all know, if an object is serialised, then the class definition must be included _before_ it is unserialised.
My framework has an enormous number of class files, and including them all at the beginning of the script was really taking it’s toll on my system (memory and execution time) so I switched to including required classes at the top of each class file that used them using require_once.
This caused problems because I start my session at the very beginning of my script’s execution, but all my class files aren’t there at the beginning!!
So no in my special ‘require’ function, I do the following:
if(!class_exists($to_require))
<
session_write_close();
require_once(‘path/to/classes/’.$to_require.’.php’);
session_start();
>
This is a considerably smaller performance hit that including every class that the application uses at the very beginning of the application.
Workaround if session_write_close() still doesn’t write sessions fast enough:
Along the same lines as what cenaculo at netcabo dot pt, bkatz at usefulengineering dot com, and editorial at literati dot ca said about making sure you session_write_close(), don’t forget to ob_end_flush() if you’re using output buffering.
I’ve been having some weird hanging issues when I tried to navigate away from a page with content streaming in an Iframe or a separate window.
This function is useful for forcing serialization of session data but it can introduce difficult-to-track bugs if it’s called more than once per session_start() call. Since it doesn’t have a return value or raise an exception there won’t be any indication that the serialization failed and the code will continue normally. Only when a user visits a page that depends on unsaved session data will there be any indication of the failure.
As far as I can tell this affects both the default and custom session handling functions.
();
$_SESSION [ ‘foo’ ] = ‘box’ ;
session_write_close ();
For the session problem when using header(«Location. «), I found session_write_close() not to help me on the my IIS server using PHP in CGI mode. The problem was the PHPSESSID cookie was never being set, so I did it the manual way:
Worked for me this way!
I had a similar problem with session data.
I lost the session data randomly, without any pattern. I didn’t even use the header(location. ) function.
Tried:
set «session.use_cookies» to 1 in php.ini
session-write-close()
ob_start() / ob_end_flush()
No matter what I did, it didn’t worked.
Finally, when I set the «session.use_only_cookies» to 1, problem is solved. I am no longer losing any sessions.
My sessions were screwing up because I had 2 different session IDs going at once:
— 1 PHPSESSID cookie for domain.com
— 1 PHPSESSID cookie for www.domain.com
//At the beginning of each page.
session_set_cookie_params (1800,»/»,»domain.com»);
session_start();
When trying to use exec on Windows 2003 Server together with WAMP you probably will experience that the server stops to answer your requests.
Hope this will help someone!
I had a problem with realizing the restore password form. First a user entered his login or e-mail in the system.
Then the script searched the database, got the session data, and sended link with SID to registered e-mail. The link was configured so, that it restored session data and logged user in the secure interface to the change password form.
Then was displayed a page with the message about sended message.
The problem was that ID was not unique in three pages, the SID sended to e-mail anyone could see in cookie.
I tryed to start new session before generating and after sending link with the code:
.
session_start ();
/*Getting user login and e-mail from database*/
$user_login = «. » ;
$user_id = «. «
/*CLOSE PREVIOUS SESSION*/
session_unlink ();
session_destroy ();
/*CLOSE THE SESSION WITH USER DATA*/
session_write_close ();
/*AND STARTING A NEW SESSION*/
session_start ();
/*THEN LOAD THE ‘MESSAGE SENDED’ PAGE*/
header ( «Location: /restore/message_sended/» );
?>
The trouble was that SID was the same even after session_unlink() and session_write_close(). The session_start() function just restored the previous session data. So the script was not safe.
Then I added session_regenerate_id() call after each session_start().
.
session_start ();
/*Getting user login and e-mail from database*/
$user_login = «. » ;
$user_id = «. «
/*CLOSE PREVIOUS SESSION*/
session_unlink ();
session_destroy ();
/*NOW GENERATING LINK FOR SESSION DATA */
session_start ();
session_regenerate_id (); //Regenerating SID for sending
/*CLOSE THE SESSION WITH USER DATA*/
session_write_close ();
/*AND STARTING ANOTHER NEW SESSION*/
session_start ();
session_regenerate_id (); //Regenerating SID
/*THEN LOAD THE ‘MESSAGE SENDED’ PAGE*/
header ( «Location: /restore/message_sended/» );
?>
And now it works as needed! The SID sending to user we cannot see in cookies nor before neither after generated link, but the data is saved in session with this id. So only the owner of account can get it!
I was having the same problem as many here regarding setting session data just before a header location redirect and having the session data just not be there. I tried everything people here said, and none of their combinations worked. What did finally work for me was to fire off a session_regenerate_id(true) call just prior to the header() and die() calls.
session_regenerate_id(true);
header(‘location: blah blah’);
die();
Without the regenerate id call, the write close did not seem to do anything. session_write_close() doesn’t seem to matter at all. It certainly didn’t fix anything on its own for me.
This is a rather annoying issue with php sessions that I’ve never run into before. I store my sessions to /dev/shm (which is RAM) so file IO blocking can’t be the problem. Now I’m nervous that some other session data might not be getting updated prior to a header() location change, which is extremely important and common in any web app.
Using a SQL Server database and Windows Server 2003, if you use session_write_close() prior to a header() call, and it still appears to freeze up your session, check to make sure that you are not in the middle of a transaction.
I noticed that by starting a transaction, executing some queries, attempting to write close the session and then redirecting without rolling back or committing that transaction, your session may freeze up. Just like what you may experience if you attempt to redirect with header() without first calling session_write_close().
PHP для начинающих. Сессия
Начну с сессий — это один из самых важных компонентов, с которыми вам придется работать. Не понимая принципов его работы — наворотите делов. Так что во избежание проблем я постараюсь рассказать о всех возможных нюансах.
Но для начала, чтобы понять зачем нам сессия, обратимся к истокам — к HTTP протоколу.
HTTP Protocol
Изначально подразумевали, что по этому протоколу будет только HTML передаваться, отсель и название, а сейчас чего только не отправляют и =^.^= и(•_ㅅ_•)
Чтобы не ходить вокруг да около, давайте я вам приведу пример общения по HTTP протоколу.
Вот пример запроса, каким его отправляет ваш браузер, когда вы запрашиваете страницу http://example.com :
А вот пример ответа:
Это очень упрощенные примеры, но и тут можно увидеть из чего состоят HTTP запрос и ответ:
Т.е. если украсть cookie из вашего браузера, то можно будет зайти на вашу страничку в facebook от вашего имени? Не пугайтесь, так сделать нельзя, по крайней мере с facebook, и дальше я вам покажу один из возможных способов защиты от данного вида атаки на ваших пользователей.
Давайте теперь посмотрим как изменятся наши запрос-ответ, будь там авторизация:
Метод у нас изменился на POST, и в теле запроса у нас передаются логин и пароль. Если использовать метод GET, то строка запроса будет содержать логин и пароль, что не очень правильно с идеологической точки зрения, и имеет ряд побочных явлений в виде логирования (например, в том же access.log ) и кеширования паролей в открытом виде.
Как можно заметить, заголовки отправляемые браузером (Request Headers) и сервером (Response Headers) отличаются, хотя есть и общие и для запросов и для ответов (General Headers)
Сервер узнал нашего пользователя по присланным cookie, и дальше предоставит ему доступ к личной информации. Так, ну вроде с сессиями и HTTP разобрались, теперь можно вернутся к PHP и его особенностям.
PHP и сессия
Я надеюсь, у вас уже установлен PHP на компьютере, т.к. дальше я буду приводить примеры, и их надо будет запускать
Вот вам статейка на тему PHP is meant to die, или вот она же на русском языке, но лучше отложите её в закладки «на потом».
Перво-наперво необходимо «стартовать» сессию — для этого воспользуемся функцией session_start(), создайте файл session.start.php со следующим содержимым:
Запустите встроенный в PHP web-server в папке с вашим скриптом:
Запустите браузер, и откройте в нём Developer Tools (или что там у вас), далее перейдите на страницу http://127.0.0.1:8080/session.start.php — вы должны увидеть лишь пустую страницу, но не спешите закрывать — посмотрите на заголовки которые нам прислал сервер:
Там будет много чего, интересует нас только вот эта строчка в ответе сервера (почистите куки, если нет такой строчки, и обновите страницу):
Увидев сие, браузер сохранит у себя куку с именем `PHPSESSID`:
PHPSESSID — имя сессии по умолчанию, регулируется из конфига php.ini директивой session.name, при необходимости имя можно изменить в самом конфигурационном файле или с помощью функции session_name()
И теперь — обновляем страничку, и видим, что браузер отправляет эту куку на сервер, можете попробовать пару раз обновить страницу, результат будет идентичным:
Итого, что мы имеем — теория совпала с практикой, и это просто отлично.
Обновляем страничку и видим время сервера, обновляем ещё раз — и время обновилось. Давайте теперь сделаем так, чтобы установленное время не изменялось при каждом обновлении страницы:
Обновляем — время не меняется, то что нужно. Но при этом мы помним, PHP умирает, значит данную сессию он где-то хранит, и мы найдём это место…
Всё тайное становится явным
В вашей конфигурации путь к файлам может быть не указан, тогда файлы сессии будут хранится во временных файлах вашей системы — вызовите функцию sys_get_temp_dir() и узнайте где это потаённое место.
Так, идём по данному пути и находим ваш файл сессии (у меня это файл sess_dap83arr6r3b56e0q7t5i0qf91 ), откроем его в текстовом редакторе:
Как видим — вот оно наше время, вот в каком хитром формате хранится наша сессия, но мы можем внести правки, поменять время, или можем просто вписать любую строку, почему бы и нет:
Так, что мы ещё не пробовали? Правильно — украсть «печеньки», давайте запустим другой браузер и добавим в него теже самые cookie. Я вам для этого простенький javascript написал, скопируйте его в консоль браузера и запустите, только не забудьте идентификатор сессии поменять на свой:
Вот теперь у вас оба браузера смотрят на одну и туже сессию. Я выше упоминал, что расскажу о способах защиты, рассмотрим самый простой способ — привяжем сессию к браузеру, точнее к тому, как браузер представляется серверу — будем запоминать User-Agent и проверять его каждый раз:
Ключевое слово в предыдущем абзаце похоже, в реальных проектах cookies уже давно «бегают» по HTTPS протоколу, таким образом никто их не сможет украсть без физического доступа к вашему компьютеру или смартфону
Стоит упомянуть директиву session.cookie-httponly, благодаря ей сессионная кука будет недоступна из JavaScript’a. Кроме этого — если заглянуть в мануал функции setcookie(), то можно заметить, что последний параметр так же отвечает за HttpOnly. Помните об этом — эта настройка позволяет достаточно эффективно бороться с XSS атаками в практически всех браузерах.
По шагам
А теперь поясню по шагам алгоритм, как работает сессия в PHP, на примере следующего кода (настройки по умолчанию):
А есть ли жизнь без «печенек»?
PHP может работать с сессией даже если cookie в браузере отключены, но тогда все URL на сайте будут содержать параметр с идентификатором вашей сессии, и да — это ещё настроить надо, но оно вам надо? Мне не приходилось это использовать, но если очень хочется — я просто скажу где копать:
А если надо сессию в базе данных хранить?
Отдельно замечу, что не надо писать собственные обработчики сессий для redis и memcache — когда вы устанавливаете данные расширения, то вместе с ними идут и соответствующие обработчики, так что RTFM наше всё. Ну и да, обработчик нужно указывать до вызова session_start() 😉
Когда умирает сессия?
За время жизни сессии отвечает директива session.gc_maxlifetime. По умолчанию, данная директива равна 1440 секундам (24 минуты), понимать её следует так, что если к сессии не было обращении в течении заданного времени, то сессия будет считаться «протухшей» и будет ждать своей очереди на удаление.
Интересен другой вопрос, можете задать его матёрым разработчикам — когда PHP удаляет файлы просроченных сессий? Ответ есть в официальном руководстве, но не в явном виде — так что запоминайте:
Самая тривиальная ошибка
Ошибка у которой более полумиллиона результатов в выдаче Google:
Cannot send session cookie — headers already sent by
Cannot send session cache limiter — headers already sent
Для получения таковой, создайте файл session.error.php со следующим содержимым:
Во второй строке странная «магия» — это фокус с буфером вывода, я ещё расскажу о нём в одной из следующих статей, пока считайте это лишь строкой длинной в 4096 символов, в данном случае — это всё пробелы
Для проверки полученных знаний, я хочу, чтобы вы реализовали свой собственный механизм сессий и заставили приведенный код работать:
Блокировка
Ещё одна распространённая ошибка у новичков — это попытка прочитать файл сессии пока он заблокирован другим скриптом. Собственно, это не совсем ошибка, это недопонимание принципа блокировки 🙂
Но давайте ещё раз по шагам:
«Воткнутся» в данную ошибку очень легко, создайте два файла:
Есть пару вариантов, как избежать подобного явления — «топорный» и «продуманный».
«Топорный»
Использовать самописный обработчик сессий, в котором «забыть» реализовать блокировку 🙂
Чуть лучше вариант, это взять готовый и отключить блокировку (например у memcached есть такая опция — memcached.sess_locking) O_o
Потратить часы на дебаг кода в поисках редко всплывающей ошибки…
«Продуманный»
Куда как лучше — самому следить за блокировкой сессии, и снимать её, когда она не требуется:
— Если вы уверенны, что вам не потребуется вносить изменения в сессионные данные используйте опцию read_and_close при старте сессии:
Таким образом, блокировка будет снята сразу по прочтению данных сессии.
— Если вам таки нужно вносить изменения в сессию, то после внесения оных закрывайте сессию от записи:
В заключение
В этой статье вам дано семь заданий, при этом они касаются не только работы с сессиями, но так же познакомят вас с MySQL и с функциями работы со строками. Для усвоения этого материала — отдельной статьи не нужно, хватит и мануала по приведенным ссылкам — никто за вас его читать не будет. Дерзайте!