root.elima.ru
Мертвечина
Статьи и книгиИнтернет-технологии и сетевое программирование

СЕССИИ – обучение и /правильное/ использование

Дмитрий Бородин
dima@php.spb.ru

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

Будем считать, что ваш сервер настроен по умолчанию (файл php.ini) и в файлах httpd.conf или .htaccess не внесали никаких для утановки локальных php-настроек (локальных – для каталога и его подкаталогов).

Когда нам нужны сессии?

Ответить на этот вопрос проще ответом на другой вопрос – что мы хотим для удобной работы в PHP? Сейчас мы много чего придумаем (для облегчения программирования), потом захотим, чтобы оно само работало, а еще дальше окажется, что так и работает PHP ;-) Как только вы поймете, чего вам так не хватало, вы поймете идею сессий и технические подробности.

Чего мы хотим для удобной работы в PHP?

Представьте программу тестирования людей. Есть куча вопросов и куча людей. Надо постараться, чтобы люди: В такой задаче надо перед началом проходжения теста взять все номера вопросов, перемешать между собой и сохранить в область данных посетителя массив номеров. Затем поместить в туже область флаг (flag=тестирование), чтобы человек не мог перейти на начало теста (начать его заного), не прорешав тест до конца. Это сделать очень просто. Предположим, что index.php – это главная страница для старта, vopros.php – страница, где выдадут очередной вопрос. Если человек с флагом "тестирование" попытается перейти на index.php, то ему надо написать – "Вы не закончили тестирование". Если человек обиделся, вырубил комп и ушел спать, через некоторый тайм аут (к примеру, через 3 дня), надо записать в историю прохождения тестов: "брошенный тест". Это похоже на игру через Интернет – если человек проигрывает, но не хочет признать это, он может как бы потерять связь с игровым сервером (якобы модем повесил трубку).

Как это все реализовать? Очень просто. Предположим, что у каждого посетителя есть некая область памяти (у каждого – своя), в которую наша программа может что-то записывать и читать. Эта область памяти должна соответствовать всем требованиям, которые мы выдвинули выше. Наша программа не должна заботиться о ней. И все это можно сделать. Эта абстрактная область памяти имеет идеальный интерфейс. Вернее, не имеет его. К примеру, если бы данная область хранилась в файлах, нам надо было бы файлы читать и анализировать. Для записи – надо было бы записывать туда данные в каком-то формате. При хранении в базе данных – нам пришлось в начале программы выполнять SQL-запрос, чтобы извлечь все данные и в самом конце выполнения программы записать их обратно. Все это (затраты на чтение файла или базы данных) – называется интерфейс. Как я сказал, наше общение с базой данных вообще не имеет интерфейса. Это значит, что любые переменные, которые мы пожелаем, будут с некоторого момента считаться областью памяти посетителя. При каждом запуске программы, от разных посетителей, она будет иметь доступ через какие-то переменные только к персональным настройкам. Чтобы сохранить (закрепить) у пользователя какую-либо информацию в переменной, надо просто записать в эту переменную что-нибудь. К примеру, в программе авторизации пользователей, говорим "хочу, чтобы $auth стала частью областью памяти". Если человек вводит свой логин и пароль (их мы проверяем как-то, например по базе данных), то мы записываем в переменную $auth единичку, затем в аналогичную переменную $login его логин. Пароль записывать не надо, т.к. наличие единицы в переменной $auth уже будет означать, что человек прошел авторизацию. На всякий случай будем хранить еще и логин. Пароль хранить совершенно не нужно, т.к. нет случая его применить... кроме как вывести на экран (но это уже будет глупая затея, а не реальная программа). Таким образом, мы один раз устанавливаем $auth, а дальше в любом месте может проверить авторизированность пользователя if ($auth) или if ($auth==1) . Так как переменная $auth будет частью области памяти, то можно не бояться, что какой-нибудь злой хакер запустит нашу программу как programm.php?auth=1 и окажется авторизированным.

Подведем итог нашей мечте о комфортной работе

Все, о чем тут было сказано, как не трудно догадаться по названию, называется сессия. Принцим действия следующий (забудьте пока о том, как это все работает): Еще раз скажем "итак" и подведем итог. Мы хотим, чтобы PHP-процессор умел распознавать пользователей и предоставлял в свободное распоряжение программе область памяти (в виде переменной $auth, $login или $любое_название), которую невозможно никак подделать. Всех новых пользователей PHP-процессор тоже должен распознавать и присваивать им новую область памяти. Единственное, что сможет злой пользователь – прикинуться новым пользователем или вообще бросить наш сайт.


Как устроены сессии: идентификатор посетителя

Выше был описан принцим сессий. Их придумали для того, что PHP лишен такой нормального свойства программы – хранить что-либо о пользователе. И виноват тут не PHP. Если взять что-то другое – средневековый язык Perl или язык ASP (для тех, кто решил тормознуть P4 1700Mhz dual), они будут работать по тем же самым принципам. И будут лишены того же – способностью что-то созранить о пользователе. Это происходит из-за убогости Web'a. Но задача программиста не придумывать что-то, а писать программы, работающие в любой среде.

Вернемся к устройству сессий. С помощью чего PHP-процессор различает пользователей? С помощью идентификатора. Он является 128 битным числом. Если человек пришел на сайт впервые и PHP-процессор это видет, то данному посетителю присваивается какое-то случайное число, не присвоенное еще никому. В дальнейшем, при повторном заходе, посетитель будет ассоциирован с его личным числом (идентификатором), из-за чего программе будет предоставлена персональная область памяти.

Запомните главное – если суметь передать идентификатор от одной страницы (PHP-скрипта) до другой (до следующего вызова с нашего сайта), то мы сможем различать всех посетителей. Больше, от не удобного для программиста Web'а, ничего не требуется. Так как мы выбрали очень больше число, то не существует ни малейшего опасения, что злой хакер подделает число, тем самым воспользуется чужой областью данных. Запомните еще одну главную мысль – подделать сессию или идентификатор нельзя. Число из 128 бит (это 2 в 128 степени) в десятичном представлениим имеет 38 нулей. Пердположим, на вашем сайте будет 10 000 посетителей. Тогда у хакера шансы несколько возрастают до 2 в степени 114. Но это по прежнему нереальная вероятность, что либо подделать. Одна из очевидных причин следующая. Чтобы подобрать номер сессии, нужно сделать запрос к вашему веб-серверу. Пусть каждый запрос занимает 1024 байта. Через миллиард обращений к вашему сайту, что составит 1000 ГБ трафика, явно что-то случиться: Хоть сессию подделать нельзя, но существуют следующие непредвиденные случаи: Как видите, все основано на том, что кто-то что-то у кого-то стащит. Но это уже не проблема программиста. Представьте, вы написали идеальную программу, в которой нет ошибок и дыр для взлома, а через день злой хакер взломал сервер и скопировал / изменил / удалил все, что посчитал нужным, даже не запуская программу. Еще раз заметим, хоть вариант стащить сессию есть, только это уже проблемы другого рода.

Выглядит этот несчастный идентификатор обычно так: "7f4cbf53fbcd4717792447f32da7dba8 ", или так: "ac4f4a45bdc893434c95dcaffb1c1811 ". Это текстовая запись 128-битного числа (занимает 32 байта).

Как устроены сессии: где хранится информация

Выше мы установили, что для нашей мечты нужно постараться передавать идентификатор от вызова к вызову (как именно передают – через главу). Имея идентификтор (число) легко загрузить каждому пользователю его персональные данные. Есть два популярных способа хранинения – база данных и файлы. Еще можно хранить сессии в памяти сервера (ОЗУ), но при выключеннии сервера будут потеряны сессии. Обычно сессии хранят в файлах, чтобы снизить нагрузку на базу данных. В огромном числе задач сессии позволяют полностью отказаться от базы данных. Или использовать ее миниматьльно: в базе данных только список логин/паролей, в сессиях (на файлах) вся рабочая информация.

Как устроены сессии: сериализация

Если вы посмотрите на файл сессии (где он находиться – узнаете позже ), то увиде нечто такое:
myarray|a:4524:{s:7324:"startup";i:98;s:4:"type";i:1;s:2:"ip";s:13:"1572342";s
:2:"tt";i:2;s:3:"b";i:12312;s:621:"max";i:2380;s:6:"min";i:-8334;s:10:"realmax
";i:1213126;s:10:"realmin";i:0;s:1:"kol";i:1;s:1:"n";i:15;s:9:"timestart";i:98
8387008;s:7:"timeend";i:9883218;s:4:"list";a:16:{i:0;s:7:"1002001";i:1;s:7:"10
02007";i:2;s:7:"1002010"; ............ и т.д.
Представьте, у нас есть огромный массив сложной структуры, к примеру, $myarray. Вам будет интересно узнать, что любую перменную (сложный массив) с любыми символами (произвольные бинарные данные) очень легко запаковать в строку из букв и цифр. Для чего переводить массив в строку? Строку (просто переменная), да еще и текстовую, легко куда-нибудь записать. Например, на листочек бумаги. Или в базу данных... Или в файл сессии. Позже, можно распаковать такую строку в оригинальную переменную и получить то, что было до упаковки. Данный метод (называется – сериализация) упаковки/распокавки переменных или объектов применяется не только в сессиях. Просто PHP, чтобы сохранить все данные пользователя, запаковывает их и кладет как строку в файл сессии.

$myStr=serialize($myVar) – запаковать огромный массив или любую произвольную переменную $myVar в текстовую переменную $myStr.

$myVar=serialize($myStr) – распаковать (восстановить) данные из ранее запакованной переменной, хранящейся в $myStr, и записать результат распаковки (оригинальную переменную) в $myVar

Как устроены сессии: передача идентификатора

Двумя главами выше вы установили, что для нашей мечты нужно передавать идентификатор от отного вызова программы к другому. Давайте подумаем, какими способами можно что либо передать. В нашем случае мы установили, что надо передавать идентификатор сессии – строку из букв и цифр длиной 32 символа. Отвлекитись от всего и подумайте, как можно передавать какую-то строку от скрипта к скрипту.


Способ 1: Cookies

Это самый популярныи и работающий по умолчанию способ передачи идентификатора. Можно один раз поместить в куку переменную и все остальные скрипты будут ее получать. Если у пользователя куки включены, PHP сам поместит туда идентификатор и потом сам его от туда достанет. О том, как PHP узнает о включенности кук – в следующей главе. В этом способе в куках создается переменная с изветным именем (по-умолчанию: PHPSESSID) и значением идентификатора (например: ac4f4a45bdc893434c95dcaffb1c1811 ).

Хорошо : куки (cookies) – это самый простой способ закрепить за посетителем какие-либо данные. Трудно стереть куку или изменить.
Плохо : куки могут быть выключены. Зайдите на Спайлог да узнайте, каков % посетителей не смогут работать с вашим сайтом.


Способ 2: параметры http://php.spb.ru/test.php?PHPSESSID=ac4f4a45bdc893434c95dcaffb1c1811

Если вы будете дописывать все ваши ссылки подобным образом, то все ваши последующие скрипты получат идентификатор. PHP использует и данный метод по умолчанию (совмещая с куками, если те выключены). Т.е. PHP _сам_ применит либо куки, либо способ 2. PHP сам после работы вашей программы проанализирует ее страницу для браузера и позаменяет все ссылки так, чтобы дописать переменную PHPSESSID.

Хорошо : 100% надежный способ. Легко передавать во всех ссылках номер сессии (это нагрузка, но она очень простая, поэтому – хорошо).
Плохо : любой чайник может переделать номер сессии или удалить ее. Т.к. в принципе нет способа надежной привязки посетителя к номеру – беда небольшая. Злой сосед по компьютеру может украсть номер из URL (32 символа). Но если сделать URL подлиннее (test.php?_________....______&PHPSESSID=12345678), то сосед ничего не увидет. Строка "Адрес" в браузере не бесконечная :-))


Способ 3: PathInfo http://php.spb.ru/test.php/ac4f4a45bdc893434c95dcaffb1c1811

Это основано на понимании Апачем того, где из этого URL путь к скрипту, а где параметры. Т.е. Апач распознает данный вызов, как обращение к файлу test.php и передаст любую произвольную строку в переменной $pathinfo=getenv("PATH_INFO") – так ее можно взять и получить от туда идентификатор запроса. Соответственно, чтобы передать управление в другой скрипт, надо подобным образом составить URL (GET-запрос) или передать форму GET или POST запросом.

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


Способ 4: rewrite, виртуальный каталог http://php.spb.ru/ac4f4a45bdc893434c95dcaffb1c1811/test.php

С помощью стандартного модуля rewrite можно огранизовать подобного рода виртуальный каталог. Если написать произвольный путь – Апач возьмет скрипт test.php из фиксированного каталога. В скрипте придеться проанализировать переменную $url=getenv("REQUEST_URI"); и взять из нее идентификатор. Далее по нему PHP восстановит нужную сессию.

Хорошо : все хорошо. Легко работать, в отличии от способа 3. Не нужно передать идентификатор – сам собой передастся.
Плохо : ничего плохого. Немного надо написать и немного настроить rewrite. Чтобы позволить кешировать картинки, путь к ним надо писать обсолютный.


Способ 5: DNS, виртуальный сервер http://ac4f4a45bdc893434c95dcaffb1c1811.php.spb.ru/test.php

Можно настроить DNS так, чтобы по любому подобному пути произошло бы обращение к вашему веб-серверу. В самом сервере с помощью алиаса "*.php.spb.ru" задать, что эти адреса принадлежат главному серверу php.spb.ru. Далее по описанной выше схеме из переменной $host=getenv("HTTP_HOST"); достаем идентификатор и передаем в PHP. После чего тот достает нужную сессию.

Хорошо : все хорошо. Совсем ничего не надо делать, по сравнению с виртуальным каталогом.
Плохо : злой сосед может срисовать номер сессии. И ее уже не запрятать за правый край строки "Адрес". Но это не страшно, т.к. ситуация со злым соседом весьма редкая. Чтобы позволить кешировать картинки, путь к ним надо писать обсолютный (ссылаясь на главное имя сервера).


Способ 6: фиктивный логин http://ac4f4a45bdc893434c95dcaffb1c1811@php.spb.ru/test.php

Зеркально похож на способ виртуального каталога. В прессе ранее не не упоминался :-) Придумался сам собой, пока писал эту стать. Хоть и не оригинальная идея, но реальная для использования. Для тех, кто не в курсе, URL состоят из следующего вида: Получается – http:// логин: пароль@ ИмяСервера: порт/ путь# якорь . Если пароль не указывают, получается http://логин@сервер, где слово логин может быть любым. К примеру, на этом основаны регулярно появляющиеся шутки типа http://www.microsoft.com@195.63.2.95/doc/windows_must_die.txt , где пишут разные приколы, подделав дизайн под нужный сайт. Есть и полезное свойтво – если чайник от незнания напишет в браузере адрес dima@mail.ru, то он попадет на главную страницу почтовой системы.

Еще можно простараться впихать идентификтаор в якорь, только это не имеет смылса, т.к. способ ничем не отличается от "способа 2".

Промежуточный итог, характеристика сессий

В самом начале мы описали персональную область данных, которой так нехватает веб-программисту, чтобы хранить что либо. Далее, мы установили, что PHP надо только сообщить названия переменных. После этого ПХП присвоит всем посетителям идентификаторы (число 128 бит), которое невозможно подделать, откроет на диске файл, названный аналогично номеру сессии для хранения информации. Данные там запаковыны с помощью функции сериализации, т.е. наипростейшим способом. Чтобы наш сайт успешно работал, мы должны позаботиться о передаче идентификатора от запроса к запросу. Делать это можно разными путями. БОльшую часть работы ПХП делает сам. И так, после вводной части (наша мечта -> реальность с четкими задачами) посмотрим, что проиход по умолчанию.

Что проиходит по умолчанию

Читайте данную главу особенно внимательно, здесь описано поведение ПХП по умолчанию и что требуется от программера в особых случаях (если куки у посетителя отключены, но юзать сессии хочеться). Поведение ПХП при нестандарных параметрах описано ниже.

Представьте, посетитель обратился к одной из страниц вашего сайта. ПХП прочел файлы конфигурации. Интерпретатор ПХП начинает выполнять ПХП-файл. Если он там не встретит команду session_register() или session_start() , то никакие сессии работать не будут. Если бы ПХП обнаружил параметр настройки session.auto_start равный 1 , то сессии стартовали бы без нашей команды (по умолчанию данный параметр содержит 0 – выключено).

В нашей программе (мы работаем с сессиями) эти строчки будут одними из первых, что обработаются ПХП-процессором. Поэтому можно считать, что поддержка сессии запуститься при старте скрипта.

Что входит в понятие запуск сессий. ПХП пытается выяснить, новый это посетитель или нет. Если новый – надо выдать ему новое случаное число и создать файл под хранение сесии, если старый – взять его идентификатор и извлеть из соответствуюего файла переменные сессии. Возможно, посетитель будет иметь номер несуществующей сессии. Тогда он приравнивается к новому посетителю.

Алгоритм извлечения идентификатора. Из настроек сервера известно, что имя переменной, хранящей идентификатор – PHPSESSID (можно задать произвольное).
  1. Если идентификатор будет найден в куках, то пользователь считается идентифицированным и использующи куки: повторно кука не устанавливается, URL не подвергаются автозамене (о автозамене чуть ниже).
  2. Если идентификатор найден в URL (GET-запрос) или в POST-запросе и не найден в куках, то пользователь считается идентифицированным и не использующем куки: кука выставлятся (на всякий случай), URL проходят автозамену, чтобы вставить в них идентификатор.
  3. Если идентификатор не найден ни в куках, ни в URL, то пользователь считается новым, используется ли он куки – не известно, происходит выделение нового случайного идетификатора и установка в куки, автозамена всех URL.
Таким образом, можно определить поведение ПХП: Алгоритм установки кук. После завершения первого алгоритма получается, что все пользователи становятся идентифицированными с каким-то номером – старым или новым. Если в предыдущей проверке выполнились 2 или 3 варианты, по ПХП устанавливает куку. Если выполнился вариант 1 – кука повторно не отсылается. Вы скорее всего когда либо работали с куками и знаете, что устанавить их можно только до вывода на экран какого-либо текста. Поэтому и надо писать команду session_start() в самом начале программы. Хотя, можно применить буферизацию вывода установив output_buffering в on . Тогда вы сможете стартовать сессии в любом месте программы, ставить куки и писать где угодно информацию в заголовок страницы (с помощью функции Header() ).

Алгоритм автозамены всех ссылок и форм. После того, как ПХП-программа закончит работу и до вывода результата браузеру, происходит автозамена ссылок. Вернее, ПХП решает, нужно ли ее производить. Если из вышеприведенных 3-х вариантов срабатывают 2-й или 3-й (либо посетитель новый, либо у него намерянно отключены куки), то ПХП производит автозамену. Автозамена всех ссылок такого типа:


БЫЛО: <a href=test.php?a=1&name=Dima>click here</a>

СТАЛО: <a href=test.php?a=1&name=Dima&PHPSESSID=ac4f4a45bdc893434c95dcaffb1c1811>click here</a>



Подобным образом ПХП пытается вставить идетификаторы во все формы GET или POST запроса:


БЫЛО: <form action=test.php> Имя: <input type=text name=nick value='$nick'> <input type=submit> </form> <br> СТАЛО: <form action=test.php?PHPSESSID=ac4f4a45bdc893434c95dcaffb1c1811> <input type=hidden name=PHPSESSID value='ac4f4a45bdc893434c95dcaffb1c1811'> Имя: <input type=text name=nick value='$nick'> <input type=submit> </form>

Данная автозамена происходит без вашего участия, ПХП сам решает когда нужно заменять и сам пытается встроить идетификатор посетителя, чтобы не потерять его. Таким образом, если вы будете делать нормальные страницы, содержащие ссылки и формы, ПХП постарается все позаменять. Однако, если вы постараесь ему помешать, например, вставляя HTML-теги с помощью функции javascript:document.write(), то ПХП явно не станет анализировать эти скрипты. Короче говоря, ПХП не гарантирует вам 100% замены всех ссылок и форм, но работает он очень хорошо.

Чтобы автоматичиская замена URL работала, убедитесь, что ПХП скомпилирован с опцией --enable-trans-id . Если на вашем сайте не поисходит автозамены – перекомпилируйте свой ПХП. Обычно ПХП компилируют с минимальным количеством параметров и указанного паратра там нет. Если ваш сервер очень нагружен, по подумайте, стоит ли вообще включать автозамену URL (скорее всего вы можете себе это позволить, т.к. на подовляющем большинстве серверов процессор в среднем загружен только на 5-10%).

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

Если вы хотите поддерживать работу сайта с отключенными куками но не хотите, чтобы ПХП автоматически заменял ссылки и формы, либо если ПХП на какой-то конкретной ссылке/форме глючит и не вставляет идентификатор, проделывайте это вручную. Это очень просто. Для начала отпределим новую константу SIDFORM (после session_start() ):


define ("SIDFORM","<input type=hidden name=".session_name()." value=".session_id().">");

Представим, что у нас есть ссылки и формы (еще бывают фреймы и теги типа AREA, но там делают аналогично). С помощью двух констант SID – определена в ПХП и SIDFORM – придумали сами себе, переработаем текст:


------------------БЫЛО (ПХП-файл)------------------ <html><style>div.main {margin-left: 20pt; margin-right: 20pt}</style> <body leftmargin=0 rightmargin=0 topmargin=0 bottommargin=0 marginheight=0 marginwidth=0> <link REL=stylesheet HREF=http://emanual.ru/style.css TYPE=text/css> <table border=0 width=100% cellspacing=0 cellpadding=0><tr><td bgcolor=gold><a href=http://emanual.ru/> <img src=http://emanual.ru/logo.gif width=150 height=60 vspace=4 hspace=4 border=0></a></td> <td align=center bgcolor=gold valign=center width=100%> <iframe src="/texts/1/http://rb1.design.ru/cgi-bin/iframe/emanual?996401829&" width=470 height=60 marginwidth=0 marginheight=0 scrolling=no frameborder=0> <a href="http://rb1.design.ru/cgi-bin/href/emanual?996401829" target=_top> <img src="/texts/1/http://rb1.design.ru/cgi-bin/banner/emanual?996401829&" alt="RB1 Network" width=470 height=60 border=0 ismap></a></iframe> <img src=http://emanual.ru/blank.gif width=4 height=1 vspace=0 hspace=0 border=0> <iframe src="/texts/1/http://120.goodoo.ru/cgi-bin/iframe/emanual?996401830&" width=120 height=60 marginwidth=0 marginheight=0 scrolling=no frameborder=0> <a href="http://120.goodoo.ru/cgi-bin/href/emanual?996401830" target=_top> <img src="/texts/1/http://120.goodoo.ru/cgi-bin/banner/emanual?996401830&" alt="GooDoo 120" width=120 height=60 border=0 ismap></a></iframe></td></tr> <tr><td bgcolor=black colspan=2 background=http://emanual.ru/line_bg.gif align=center class=fine height=20><font color=white> <a href=http://softarea.ru class=top>Программы</a> &#149;<a href=http://hardarea.ru class=top>Железо</a> &#149; <a href=http://softarea.ru/drivers class=top>Драйверы</a> &#149; <a href=http://majordomo.ru/hosting class=top>Хостинг</a> &#149; <a href=http://reklamist.com class=top>Энциклопедия рекламы</a></font></td></tr></table><div class=main> Ссылка: <a href=test.php?a=1>click here</a> ... Форма: <form action=test.php> .... </form> ------------------МЫ ЗАМЕНЯЕМ НА (новая версия ПХП-файла)------------------ <html><style>div.main {margin-left: 20pt; margin-right: 20pt}</style> <body leftmargin=0 rightmargin=0 topmargin=0 bottommargin=0 marginheight=0 marginwidth=0> <link REL=stylesheet HREF=http://emanual.ru/style.css TYPE=text/css> <table border=0 width=100% cellspacing=0 cellpadding=0> <tr><td bgcolor=gold><a href=http://emanual.ru/> <img src=http://emanual.ru/logo.gif width=150 height=60 vspace=4 hspace=4 border=0></a></td> <td align=center bgcolor=gold valign=center width=100%> <iframe src="/texts/1/http://rb1.design.ru/cgi-bin/iframe/emanual?996401829&" width=470 height=60 marginwidth=0 marginheight=0 scrolling=no frameborder=0> <a href="http://rb1.design.ru/cgi-bin/href/emanual?996401829" target=_top> <img src="/texts/1/http://rb1.design.ru/cgi-bin/banner/emanual?996401829&" alt="RB1 Network" width=470 height=60 border=0 ismap></a></iframe> <img src=http://emanual.ru/blank.gif width=4 height=1 vspace=0 hspace=0 border=0> <iframe src="/texts/1/http://120.goodoo.ru/cgi-bin/iframe/emanual?996401830&" width=120 height=60 marginwidth=0 marginheight=0 scrolling=no frameborder=0> <a href="http://120.goodoo.ru/cgi-bin/href/emanual?996401830" target=_top> <img src="/texts/1/http://120.goodoo.ru/cgi-bin/banner/emanual?996401830&" alt="GooDoo 120" width=120 height=60 border=0 ismap></a></iframe></td></tr> <tr><td bgcolor=black colspan=2 background=http://emanual.ru/line_bg.gif align=center class=fine height=20><font color=white> <a href=http://softarea.ru class=top>Программы</a> &#149; <a href=http://hardarea.ru class=top>Железо</a> &#149; <a href=http://softarea.ru/drivers class=top>Драйверы</a> &#149; <a href=http://majordomo.ru/hosting class=top>Хостинг</a> &#149; <a href=http://reklamist.com class=top>Энциклопедия рекламы</a> </font></td></tr></table><div class=main> Ссылка: <a href="test.php?a=1&<?=SID?>"?>>click here</a> .... Форма: <form action=test.php> <?=SIDFORM?> .... </form> ------------------РЕЗУЛЬТАТ ВЫПОЛНЕНИЯ (это получит браузер)------------------ <style>div.main {margin-left: 20pt; margin-right: 20pt}</style> <body leftmargin=0 rightmargin=0 topmargin=0 bottommargin=0 marginheight=0 marginwidth=0> <link REL=stylesheet HREF=http://emanual.ru/style.css TYPE=text/css> <table border=0 width=100% cellspacing=0 cellpadding=0><tr><td bgcolor=gold> <a href=http://emanual.ru/><img src=http://emanual.ru/logo.gif width=150 height=60 vspace=4 hspace=4 border=0></a></td> <td align=center bgcolor=gold valign=center width=100%> <iframe src="/texts/1/http://rb1.design.ru/cgi-bin/iframe/emanual?996401829&" width=470 height=60 marginwidth=0 marginheight=0 scrolling=no frameborder=0> <a href="http://rb1.design.ru/cgi-bin/href/emanual?996401829" target=_top> <img src="/texts/1/http://rb1.design.ru/cgi-bin/banner/emanual?996401829&" alt="RB1 Network" width=470 height=60 border=0 ismap> </a></iframe><img src=http://emanual.ru/blank.gif width=4 height=1 vspace=0 hspace=0 border=0> <iframe src="/texts/1/http://120.goodoo.ru/cgi-bin/iframe/emanual?996401830&" width=120 height=60 marginwidth=0 marginheight=0 scrolling=no frameborder=0> <a href="http://120.goodoo.ru/cgi-bin/href/emanual?996401830" target=_top> <img src="/texts/1/http://120.goodoo.ru/cgi-bin/banner/emanual?996401830&" alt="GooDoo 120" width=120 height=60 border=0 ismap></a> </iframe></td></tr><tr> <td bgcolor=black colspan=2 background=http://emanual.ru/line_bg.gif align=center class=fine height=20><font color=white> <a href=http://softarea.ru class=top>Программы</a> &#149; <a href=http://hardarea.ru class=top>Железо</a> &#149; <a href=http://softarea.ru/drivers class=top>Драйверы</a> &#149; <a href=http://majordomo.ru/hosting class=top>Хостинг</a> &#149; <a href=http://reklamist.com class=top>Энциклопедия рекламы</a> </font></td></tr></table><div class=main>Ссылка: <a href=test.php?a=1&PHPSESSID=ac4f4a45bdc893434c95dcaffb1c1811>click here</a> .... Форма: <form action=test.php> <input type="text" name="PHPSESSID" value="ac4f4a45bdc893434c95dcaffb1c1811"> .... </form>

Тоже самое будет и в ПХП-варианте: echo "<a href=test.php?e=2>click here</a>"; надо заменить на echo "<a href=test.php?e=2&".SID.">click here</a>";
Обратите внимание, что SID – это константа. И если вы хотите иметь переменную $SID , то просто напишите в начале программы $SID=SID; (после старта сессий).

Итак, подведем итог автозамене. Если вы не хотите вставлять идентификаторы – полагайтесь на ПХП, который постарается все ссылки заменить. Если вы хотите 100% надежность того, что все смогут использовать ваш сайт и ни один пользователь ни на одной старнице случайно не потеряет сессию, то прибавляйте к каждой форме и ссылке по небольшой константе (их можно названить очень коротко, типа "X" и "Y").

Параметры и функции для сессий

В данной главе приведено описаний сессий – это ФУНКЦИИ и одноименные НАСТРОЙКИ в php.ini или .htaccess. Функции от настроек отличаются тем, что у первых есть скобочки – функция() . Для тех, кто не в курсе. Вы можете настраивать сессии, задавая разные параметры в php.ini, httpd.conf или .htaccess файлах. В php.ini все переменные уже написаны и содержат какие-либо значения (их называют глобальными настройками), с помощью httpd.conf или .htaccess можно указать для какого-либо виртуального сервера или отдельного каталога локальные настройки ПХП. Для этого используют строку php_value Имя Значение . Обратите внимание, что использовать настройки можно только если ваш ПХП является модулем Апача. В противном случае (ПХП – не модуль, а отдельная CGI-программа) такая строка в .htaccess вызовет ошибку, как и любая неправильная строка.

session_start()

Запустить поддержку сессий в скрипте. Данная функция подробно была описана выше. Функцию можно не использовать, т.к. первая же session_register("НазваниеПеременнной") запустит сессии.

session_destroy()

Уничтожить: Функция отменяет действие session_start(). Вызывать нужно после вызова session_start(). Можно применять, чтобы уничтожать сессиию пользователя, а потом сразу вызывать в программе второй раз session_start(), получиться совершенно новый посетитель с новым идентификатором и чистой сессией.

session_name() , session.name , PHPSESSID

ПХП для хранения идентификатора использует какую-то переменную В куку записывают переменную: значение переменной – идентификатор, название переменной – PHPSESSID. PHPSESSID – это название, которое используют по-умолчанию. Я рекомендую переименовать ее во что-либо короткое, например SID. А для этого надо заменить параметр session.name на значение SID: После такого переименования получится, что константа SID содержит строку "SID=номер_сессии" (echo SID; ), а переменная $SID только сам номер (echo $SID; ). Для тех, кто не в курсе, константы в ПХП используют без символа доллара.

Чтобы получить название переменной, которую используют для хранения идентификатора, что в свою очередь происходит из-за настроек, надо воспользоваться функцией без параметра $name=session_name()

Чтобы установить такую переменную в произвольное название, воспользуйтесь функцией с параметром session_name("МоеНазвание") . Разумеется, если вам почему-то понадобилось изменить имя переменной для кук с помощью этой функции, то ее надо вызывать перед session_start() или session_register(), иначе, как не трудно догадаться, будет поздно что либо менять. К тому же, вы должны будете использовать эту функцию во всех своих скриптах, где требуется заменить "PHPSESSID" на "МоеНазвание".

session_module_name(), session.save_handler

Получить или установить текущий модуль сессии. ПХП может хранить сессии разном виде. По умолчанию – в файлах. См. так же session_set_save_handler().

session_save_path(), session.save_path

Получить или установить каталог, в котором будут храниться файлы сессии. $path=session_save_path() – получить. session_save_path("/mydir/temp"); – установить. Параметр session.save_path можно задавать в php.ini или .htaccess .

session_id()

Получить или установить идентификатор посетителя (128-битное число, представленное в виде строки в 32 байта). В нормальных условиях вам не нужно устанавливать номер сесии. Но если вы хотите для всех своих посетителей использовать одну сессиию, то перед session_start() придумайте любое имя (произвольная строка) – session_id("dima"); или получите настоящий идентификатор другим образом. Вызов функции без параметров вернет вам текущий номер сессии (в таком случае – вызвать после session_start() ).

session_register()

Зарегистрировать одну или несколько переменных. Передавать надо имена переменных, а не сами переменные: session_register("переменная1", "переменная2", ...) . Сама функция подробна описана выше.

session_unregister()

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

session_unset()

Очистить все переменные сессии. В отличии от session_destroy() сессия и идентификатор остается.

session_is_registered()

Проверить, зарегистрирована какая либо переменная в текущей сессии или нет: if (session_is_registered("МояПеременная")) ...

session_get_cookie_params() и session_set_cookie_params()

Получить информацию о куке, хранящей переемнную с идентификатором сессии. echo "<pre>SESSION INFO:\n"; print_r(session_get_cookie_params()); Так можно узнать о времени жизни переменной, домене и пути куки. session_set_cookie_params() – соотвественно переустановить параметры (хотя все эти параметры надо задавать в php.ini – см. ниже).

session_decode() и session_encode()

Раскодировать закодированную строку в сессию и получить закодированную строку сессии.

session_set_save_handler()

Возможно, вас не усраивают варианты хранения сессий, предлагающихся в ПХП. Может быть вы хотите хранить сессии в простых текстовых файлах, чтобы их можно было легко редактировать. Тогда вам нужно создать несколько функций, отвечающих за функционирование сессий и передать названия этих функций в session_set_save_handler() (чтобы пропустить какой либо параметр, передавайте пустую строку – "" ). Если вы пишите что-не не стандарное, вам будет полезна фукнция. Описывать ее не буду, т.к. не использовал, да и в мануале есть полное описание и пример.

session.auto_start

По умочанию равно 0, сессии автоматически не запускаются. Если равно 1, то session_start() писать в начале каждого скрипта не нужно.

session.cookie_lifetime

Если равно 0 (по-умолчанию), то кука, хранящяя сессию, будет уничтожена на компьютера посетителя как только он закроет все окошки браузера. Если не нуль, то это задает число секунд для хранения куки.

session.gc_maxlifetime

Количество секунд, через которое ПХП уничтожит у себя на диске устаревший файл сессии. По умолчанию – 1440. Это произойдет НЕ РАНЕЕ кол-ва секунд, указанного в переменной. Данное время определяет нижнюю гланицу (НЕ РАНЕЕ), когда файл становится кандидатом на удаление. Верхняя граница определяется следующей переменной. Время отсчитывается от последнеого обращения посетителя т.е. это время доступа к файлу, а не создания. У любого файла есть время создания. модификации (запись) и доступа (чтение).

session.gc_probability

Число от 1 до 100, измеряемое в процентах (по умолчанию – 1, т.е. 1%), которое задает вероятность запуска механизма удаления старых файлов сессии. Другими словами, при каждом обращении посетителей к любой программе вашего сайта, использущей сессии, с вероятностью 1% произойдет поиск и удаление устаревших файлов сессии. Таким образом, по-умолчанию, вы на должны опасаться, что на сервере скопится огромное число мусора, и вам придется писать программу для его сбора. Только укажите желаемую частоту сбора. Не рекомендуется устанавливать 100%, т.к. ПХП будет при каждом обращении пользователя искать старые файла, что создаст лишнюю нагрузку. Разумный максимум для данной переменной – 5%, хотя лучше оставить как есть – 1%.

session.use_cookies

Использовать ли куки, для хранения идентификатора сессий или нет. По умолчанию – 1, можно использовать. Если равно 0, то ПХП ничего в куки записывать не будет, а будет пытаться все время автозаменять URL и формы.

session.cookie_path

Если на вашем сайте используется несколько разных программ (у каждой свои сессии) и вы не хотите, чтобы сессии случайно пересекались, то установить нужный путь для кук. Если установить session.cookie_path /programm1 , то все ваши программы, расположенные в каталоге pogramm1 или его подкатлогах, будут иметь одно общее пространство для кук, что равносильно одной и тожей сессии. Если посетитель после каталога /programm1 зайдет в /programm2 (для которого тоже заведены отдельные куки), то он будт идентифицирован новой программой по новому. Если у вас на сайте всего одна программа или много программ, но на разных названиях серверов, то куки/сессии никак не пересекутся.

session.cookie_domain

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

Приступим к работе с сессиями

Для начала решим, как правильно должны начинаться наши скрипты и что мы собираемся хранить в сессиях. Предложение N1 – несколько строк, которые будут отвечать за правильный запуск сессий и обработки новых пользователей, которым надо записать какие-то переменные для начала пработы, хорошо бы поместить в отдельный файл. Итак, файл index.php будет главным на сайте, а описанные команды мы поместим в default.php.

Предложение N2 – наша программа будет содержать много функций, в которых иногда будет происходить доступ к переменным сессий, следовательно последние надо будет делать глобальными в функциях. Если у нас будет десяток переменных, то нам придется писать большую строку для объявления переменных глобальными. Если же мы зарегистрируем в сессиях только одну переменную-массив с коротким названием, то нам будет легко ее прописывать глобальной и легко обращаться. Итак, предложение 2 – используем переменную $s, все необходимые параметры пишем в $s как в массив: $s[название]=значение .

Что записывать всем пользователям по умолчанию? Давайте, при входе нового посетителя, будет объявлять переменную $s[start] , в которой запишим время входа пользователя. Чтобы получить текущее время (число) используют time() . Больше ничего делать с новыми посетителями не надо. На главной страние мы будем выводить фразу "Добро пожаловать!", если человек впервые тут и фразу "Вы пришли к нам ?? секунд назад". Подсчитать кол-во секунд весьма просто: time() – текущее время, $s[start] – уже прошедшее (когда мы произвели запись $s[start] ). Таким образом, надо от большего отнять меньшее. Пишем эту программу.

Файл default.php :

<? // до первой строки с "<?" вы не должны ставить пустые строки или пробелы // данный файл должен начинаться с "<?" // в тексте программ возможно любое кол-во пустых строк unset($s); // удаление переменной $s, если она вдруг была объявлена // чтобы никто не мог ничего через URL ничего записать // например, так: index.php?s[start]=... // сессии еще не запустились, поэтому мы удаляем не ту переменную $s, // что появиться после следующей строки session_start(); // включить поддержку сессий. Если человек новый, ему // выдадут случайный идентификатор. Но мы про это пока не // думает, а сразу приступаем к использованию сессии session_register("s"); // зарегистрировать переменную $s как переменную сессии // обратите внимание – пишем "s" в функции, а не $s // с этого момента вся информация в будущем массиве $s будет сохраняться ?>

Файл index.php (или любой файл вашего будущего сайта)

<? // до первой строки с "<?" вы не должны ставить пустые строки или пробелы include("default.php"); // подключаем файл default.php из текущего каталога (из того же, где и сам // index.php лежит) if (!isset($s['start'])) { // если выполнится это условие, т.е. переменной $s[start] еще несуществует, // то мы может произвести какие-то действия, пусть даже самые простые. // Используем переменную $s[start] как флаг и отсчет времени $s['start']=time(); // в переменную запишеться ненулевое положительное число // больше данный IF не выполниться, т.к. теперь и далее переменная // $s[start] существует (и дополнительно хранит полезное число) echo "Добро пожаловать на наш сайт!"; // выводим сообщение в знак того, что данный посетитель пришел // пришел впервые } else { // если выполниться это условие, то это старый посетитель // выводим для него соответствующую фразу и не производим // установку переемнных по умолчанию echo "Вы впервые пришли на наш сайт ".(time()-$s['start'])." секунд назад"; } ?>

Данный пример поможно посмотреть тут: /php/s/example1/

Обратите внимание, что программа при выключенных куках работать не будет. Для его работоспособности надо при первом обращении пользователя сделать header("Location: $PHP_SELF?".SID); (разумеется, при этом человек не увидет приветствие).

На этом пока придеться закончить, остальное допишу через несколько дней. Пока вы можете смотреть уже готовые примеры этого сайта, использующие сессии: