Создать страницу на php. Разработка многозадачных приложений на PHP V5 (исходники)

Кэмерон Лэйрд

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

Многообразие параллелизма

Сначала отложим в сторону случаи, лежащие вне русла главной темы. У PHP сложные взаимоотношения с многозадачностью или параллелизмом. На верхнем уровне PHP постоянно вовлечен в многозадачность - стандартные установки PHP на сервере (например, модуль Apache) используются многозадачным способом. То есть несколько клиентских приложений (Web-браузеров) могут одновременно запросить одну и ту же PHP-страницу, и Web-сервер возвратит ее всем более или менее одновременно.

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

Параллелизм на клиентской стороне под названием Ajax тоже привлек внимание разработчиков в последние несколько лет. Хотя значение Ajax стало несколько неясным, одним из аспектов этой технологии является то, что браузер может одновременно выполнять вычисления и оставаться чувствительным к таким действиям пользователя, как выбор пунктов меню. Это действительно отчасти многозадачность. Закодированный на PHP Ajax делает это, но без какого-либо специального участия PHP; интегрированные среды Ajax для других языков работают точно также.

Третьим примером параллелизма, который только поверхностно затрагивает PHP, является PHP/TK. PHP/TK - это расширение PHP, предоставляющее переносимые связывания графического интерфейса пользователя (Graphical User Interface - GUI) ядру PHP. PHP/TK позволяет создавать настольные GUI-приложения, написанные на PHP. Его основанные на событиях аспекты моделируют форму параллелизма, которую легко изучить, и она меньше подвержена ошибкам, чем работа с потоками. Опять же, параллелизм "унаследован" от дополнительной технологии, а не является фундаментальной функциональностью PHP.

Было несколько экспериментов по добавлению поддержки поточности в сам PHP. Насколько я знаю, ни один не был удачным. Однако ориентированные на события интегрированные среды Ajax и PHP/TK показывают, что события могут еще лучше выразить параллелизм для PHP, чем это делают потоки. PHP V5 доказывает это.

PHP V5 предлагает stream_select()

В стандартном PHP V4 и более ранних версиях вся работа PHP-приложения должна выполняться последовательно. Если программе нужно извлечь цену товара с двух коммерческих сайтов, например, она запрашивает первую цену, ждет получения ответа, запрашивает вторую цену и ждет опять.

Что, если бы программа могла выполнять несколько задач одновременно? Она завершалась бы лишь за часть того времени, которое необходимо при последовательной работе.

Первый пример

Новая функция stream_select , вместе с несколькими своими друзьями, предоставляет эту возможность. Рассмотрим следующий пример:

0) { $s=stream_socket_client("phaseit.net:80", $errno, $errstr, $timeout, STREAM_CLIENT_ASYNC_CONNECT/STREAM_CLIENT_CONNECT); if ($s) { $sockets[$id++]=$s; $http_message="GET /demonstration/delay?delay=" . $delay . " HTTP/1.0\r\nHost: phaseit.net\r\n\r\n"; fwrite($s, $http_message); } else { echo "Stream " . $id . " failed to open correctly."; } $delay -= 3; } while (count($sockets)) { $read=$sockets; stream_select($read, $w=null, $e=null, $timeout); if (count($read)) { /* stream_select обычно перемешивает $read, поэтому мы должны вычислить, из какого сокета выполняется чтение. */ foreach ($read as $r) { $id=array_search($r, $sockets); $data=fread($r, $convenient_read_block); /* Сокет можно прочитать либо потому что он имеет данные для чтения, ЛИБО потому что он в состоянии EOF. */ if (strlen($data) == 0) { echo "Stream " . $id . " closes at " . date("h:i:s") . ".\n"; fclose($r); unset($sockets[$id]); } else { $result[$id] .= $data; } } } else { /* Таймаут означает, что *все* потоки не дождались получения ответа. */ echo "Time-out!\n"; break; } } ?>

Если выполнить эту программу, отобразится примерно следующая информация:

Program starts at 02:38:50. Stream 4 closes at 02:38:53. Stream 3 closes at 02:38:56. Stream 2 closes at 02:38:59. Stream 1 closes at 02:39:02. Stream 0 closes at 02:39:05.

Важно понимать, что здесь происходит. На высоком уровне первая программа выполняет несколько HTTP-запросов и получает страницы, которые передает ей Web-сервер. Хотя реальное приложение, наверное, запрашивало бы несколько различных Web-серверов (возможно google.com, yahoo.com, ask.com и т.д.), этот пример передает все запросы на наш корпоративный сервер на Phaseit.net просто ради уменьшения сложности.

Запрошенные Web-страницы возвращают результаты после переменной задержки, показанной ниже. Если бы программа выполняла запросы последовательно, для ее завершения понадобилось бы около 15+12+9+6+3 (45) секунд. Как показано в листинге 2, на самом деле она завершается за 15 секунд. Утроение производительности - это отличный результат.

Такое стало возможно благодаря stream_select - новой функции в PHP V5. Запросы инициируются обычным способом - открытием нескольких stream_socket_clients и написанием GET к каждому из них, что соответствует http://phaseit.net/demonstration/delay?delay=$DELAY . При запросе этого URL в браузере вы должны увидеть:


Хотя конкретная реализация в листинге 3 предназначена для UNIX®, почти все сценарии данной статьи с тем же успехом применимы для установок PHP в Windows® (особенно после Windows 98) или UNIX. В частности, с листингом 1 можно работать на любой операционной системе. Linux® и Mac OS X являются вариациями UNIX, и весь приведенный здесь код будет работать в обеих системах.

Запросы к серверу задержки выполняются в следующем порядке:

delay=15 delay=12 delay= 9 delay= 6 delay= 3

Целью stream_select является как можно более быстрое получение результатов. В данном случае порядок задержек противоположен порядку, в котором были сделаны запросы. Через 3 секунды первая страница готова для чтения. Эта часть программы является обычным PHP-кодом - в данном случае с fread . Также как и в другой PHP-программе чтение могло бы осуществляться при помощи fgets .

Обработка продолжается таким же образом. Программа блокируется в stream_select , пока не будут готовы данные. Решающим является то, что она начинает чтение, как только какое-либо соединение будет иметь данные, в любом порядке. Именно так программа реализует многозадачность или параллельную обработку результатов нескольких запросов.

Обратите внимание на то, что при этом нет дополнительной нагрузки на CPU хост-компьютера. Нет ничего необычного в том, что сетевые программы, выполняющие fread таким способом, вскоре начинают использовать 100% мощности CPU. Здесь не этот случай, поскольку stream_select имеет желаемые свойства и отвечает немедленно, как только какое-нибудь чтение становится возможным, но при этом минимальным образом загружает CPU в режиме ожидания между операциями чтения.

Что нужно знать о stream_select()

Подобное основанное на событиях программирование не является элементарной задачей. Хотя листинг 1 и уменьшен до самых основных моментов, любое кодирование, базирующееся на обратных вызовах или координации (что является необходимым в многозадачных приложениях) будет менее привычным по сравнению с простой процедурной последовательностью. В данном случае наибольшая трудность заключена в массиве $read . Обратите внимание на то, что это ссылка; stream_select возвращает важную информацию путем изменения содержимого $read . Так же как указатели имеют репутацию постоянного источника ошибок в C, ссылки, по-видимому, являются той частью PHP, которая представляет наибольшую трудность для программистов.

Такую методику запросов можно использовать из любого числа внешних Web-сайтов, удостоверяясь в том, что программа будет получать каждый результат как можно быстрее, не ожидая других запросов. Фактически, данная методика корректно работает с любым TCP/IP-соединением, а не только с Web (порт 80), то есть в принципе вы можете управлять извлечением LDAP-данных, передачей SMTP, SOAP-запросами и т.д.

Но это не все. PHP V5 управляет различными соединениями как "потоками" (stream), а не простыми сокетами. Библиотека PHP Client URL (CURL) поддерживает HTTPS-сертификаты, исходящую FTP-загрузку, куки и многое другое (CURL позволяет PHP-приложениям использовать различные протоколы для соединения с серверами). Поскольку CURL предоставляет интерфейс stream, с точки зрения программы соединение прозрачно. В следующем разделе рассказывается, как stream_select мультиплексирует даже локальные вычисления.

Для stream_select существует несколько предостережений. Эта функция не документирована, поэтому не рассматривается даже в новых книгах по PHP. Несколько примеров кода, доступные в Web, просто не работают или не понятны. Второй и третий аргументы stream_select , управляющие каналами write и exception , соответствующими каналам read в листинге 1, почти всегда должны быть равны null. За некоторыми исключениями выбор этих каналов является ошибкой. Если вы не имеете достаточного опыта, используйте только хорошо описанные варианты.

Кроме того, в stream_select , по всей видимости, имеются ошибки, по крайней мере, в PHP V5.1.2. Наиболее существенным является то, что значению возврата функции нельзя доверять. Хотя я еще не отладил реализацию, мой опыт показал, что безопасно тестировать count($read) так, как в листинге 1, но это не относится к значению возврата самой stream_select , несмотря на официальную документацию.

Локальный параллелизм PHP

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

Что если замедление происходит поближе? Есть ли способ ускорить получение результатов в PHP при локальной обработке? Есть несколько. Пожалуй, они еще менее известны, чем ориентированный на сокеты подход в листинге 1. Этому есть несколько причин, в том числе:

  • В своем большинстве PHP-страницы достаточно быстры. Лучшая производительность могла бы быть преимуществом, но этого недостаточно для оправдания инвестиций в новый код.
  • Использование PHP в Web-страницах может сделать частичные ускорения кода не важными. Перераспределение вычислений таким образом, чтобы получать промежуточные результаты быстрее, не имеет значения, когда единственным критерием является скорость доставки Web-страницы в целом.
  • Немного локальных узких мест находится под контролем PHP. Пользователи могут выражать недовольство тем, что извлечение информации об учетной записи занимает 8 секунд, но это может быть ограничением обработки базы данных или каких-либо других ресурсов, внешних для PHP. Даже если уменьшить время PHP-обработки до нуля, все равно будет затрачено более 7 секунд просто на поиск.
  • Еще меньшее количество ограничений поддается параллельной обработке. Предположим, что конкретная страница вычисляет рекомендуемую цену для перечисленных обыкновенных акций, а вычисления достаточно сложны и выполняются в течение многих секунд. Вычисление может быть последовательным по природе. Не существует очевидного способа распределить его для "совместной работы".
  • Мало PHP-программистов понимает потенциал PHP для реализации параллельной обработки. Говоря о возможности распараллеливания, большинство из встреченных мной программистов просто цитировали фразу "PHP не работает с потоками" и возвращались к своей сложившейся модели вычислений.

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

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

array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("file", $error_log, "w")); $cmd="sleep " . $delay . "; echo "Finished with delay of " . $delay . ""."; $handles[$id]=proc_open($cmd, $descriptorspec, $pipes); $streams[$id]=$pipes; $all_pipes[$id]=$pipes; $delay -= 2; } while (count($streams)) { $read=$streams; stream_select($read, $w=null, $e=null, $timeout); foreach ($read as $r) { $id=array_search($r, $streams); echo stream_get_contents($all_pipes[$id]); if (feof($r)) { fclose($all_pipes[$id]); fclose($all_pipes[$id]); $return_value=proc_close($handles[$id]); unset($streams[$id]); } } } ?>

Данная программа выведет на экран следующую информацию:

Program starts at 10:28:41. Finished with delay of 1. Finished with delay of 3.

Смысл заключается в том, что PHP запустил два независимых субпроцесса, получил данные от первого, а затем от второго, хотя последний стартовал раньше. Если хост-компьютер является многопроцессорным, а операционная система корректно настроена, она сама заботится о назначении различных субпрограмм разным процессорам. Это один из способов использования преимуществ многопроцессорных машин в PHP.

Резюме

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

Если вы только что столкнулись с PHP, то вам нужно знать некоторые определения.
Прежде всего, PHP – это язык программирования и служит он для написания команд (сценариев), адресованных серверу. Если говорить ещё проще, то при помощи PHP мы можем общаться с сервером.
Команды PHP легко внедряются в HTML страницы. Именно это свойство является важным преимуществом языка PHP перед такими языками, как Perl и C.

Синтаксис PHP

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



Пример

include ("sidebar.htm") ;
?>


Если нам нужно что-то вставить в html код страницы необходимо использовать команду include (вставить). Далее указываем адрес файла, а заканчивается строка, как и в CSS ;

Вставляем HTML код в страницы сайта

Как правило боковые колонки (sidebar ) и подвал (footer ) остаются неизменными на всех страницах сайта. Следовательно коды

.....
и можно вынести в отдельные htm страницы "sidebar.htm" и "footer.htm" и вставлять в страницы сайта при помощи команды include . Если при этом вынести в отдельный файл и главное содержимое -
.....
, то код нашей страницы будет иметь вид:



Пример

include ("sidebar.htm") ;
include ("content.htm") ;
include ("footer.htm") ;
?>


содержимое sidebar.htm


здесь
содержимое
Вашего
сайд-бара

Аналогично с файлами content.htm и footer.htm .

При такой генерации страниц Вам достаточно внести изменения в один файл "sidebar.htm" , что бы изменились все страницы сайта. Что очень удобно если Ваш сайт состоит из сотни или тысячи страниц.

PHP на Вашем компьютере

Чтобы Вы могли работать с PHP-скриптами и просматривать результаты выполнения в браузере, вам нужно установить работающий веб сервер с PHP на Вашем локальном компьютере.
Лучше всего для таких задач подойдёт Денвер . (официальный сайт предоставляет всё необходимое бесплатно ) В комплект установки входит - Apache, php и MySQL. Другими словами на Вашем компьютере будет находиться полнофункциональный сервер для хостинга сайтов .

Для того, что бы PHP код работал в HTML страницах необходимо открыть файл .htaccess в любом текстовом редакторе и прописать следующее:

AddHandler application/x-httpd-php .html

Данная запись разрешает выполнение PHP скриптов в HTML страницах.

Или изменить расширение файла.html на.php

Возможности PHP

PHP способен не только выдавать HTML. Возможности PHP включают формирование изображений, файлов PDF и даже роликов Flash (с использованием libswf и Ming), создаваемых "на лету". PHP также способен выдавать любые текстовые данные, такие, как XHTML и другие XML-файлы. PHP способен осуществлять автоматическую генерацию таких файлов и сохранять их в файловой системе вашего сервера, вместо того, чтобы отдавать клиенту, организуя, таким образом, кеш динамического содержания, расположенный на стороне сервера.

Одним из значительных преимуществ PHP является поддержка широкого круга баз данных. Словом, PHP может предложить вам очень многое! Подробно о преимуществах PHP можно ознакомиться на www.php.su .

February 1, 2015

Всем хорошего дня. Перед вами первая статья из серии PHP для начинающих разработчиков. Это будет необычная серия статей, тут не будет echo "Hello World" , тут будет hardcore из жизни PHP программистов с небольшой примесью “домашней работы” для закрепления материала.

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

Но для начала, чтобы понять зачем нам сессия, обратимся к истокам – к HTTP протоколу.

HTTP Protocol

HTTP протокол – это HyperText Transfer Protocol - «протокол передачи гипертекста» – т.е. по сути – текстовый протокол, и его понять не составит труда.

Изначально подразумевали, что по этому протоколу будет только HTML передаваться, отсель и название, а сейчас чего только не отправляют( _ㅅ_ )=^.^=

Чтобы не ходить вокруг да около, давайте я вам приведу пример общения по HTTP протоколу, вот запрос, каким его отправляет ваш браузер, когда вы запрашиваете страницу http://example.com:

GET / HTTP/1.1 Host: example.com Accept: text/html ...пустая строка...

А вот пример ответа:

HTTP/1.1 200 OK Content-Length: 1983 Content-Type: text/html; charset=utf-8 ... ...

Это очень упрощенные примеры, но и тут можно увидеть из чего состоят HTTP запрос и ответ:

  1. стартовая строка – для запроса содержит метод и путь запрашиваемой страницы, для ответа – версию протокола и код ответа
  2. заголовки – имеют формат ключ-значение разделенные двоеточием, каждый новый заголовок пишется с новой строки
  3. тело сообщения – непосредственно HTML либо данные отделяют от заголовков двумя переносами строки, могут отсутствовать, как в приведенном запросе

Так, вроде с протоколом разобрались – он простой, ведёт свою историю аж с 1992-го года, так что идеальным его не назовешь, но какой есть – отправили запрос – получите ответ, и всё, сервер и клиент никоим образом более не связаны. Но подобный сценарий отнюдь не единственный возможный, у нас же может быть авторизация, сервер должен каким-то образом понимать, что вот этот запрос пришёл от определенного пользователя, т.е. клиент и сервер должны общаться в рамках некой сессии. И да, для этого придумали следующий механизм:

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

Для реализации этого механизма и были созданы (куки, печеньки) – простые текстовые файлы на вашем компьютере, по файлу для каждого домена (хотя некоторые браузеры более продвинутые, и используют для хранения SQLite базу данных), при этом браузер накладывает ограничение на количество записей и размер хранимых данных (для большинства браузеров это 4096 байт, см. RFC 2109 от 1997-го года)

Т.е. если украсть cookie из вашего браузера, то можно будет зайти на вашу страничку в facebook от вашего имени? Не пугайтесь, так сделать нельзя, по крайней мере с facebook, и дальше я вас научу как можно защититься от данного вида атаки на ваших пользователей.

Давайте теперь посмотрим как изменятся наши запрос-ответ, будь там авторизация:

POST /login/ HTTP/1.1 Host: example.com Accept: text/html login=Username&password=Userpass

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

HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Set-Cookie: KEY=VerySecretUniqueKey ... ...

Ответ сервер будет содержать заголовок Set-Cookie: KEY=VerySecretUniqueKey , что заставит браузер сохранить эти данные в файлы cookie, и при следующем обращении к серверу – они будут отправлены и опознаны сервером:

GET / HTTP/1.1 Host: example.com Accept: text/html Cookie: KEY=VerySecretUniqueKey ...пустая строка...

Как можно заметить, заголовки отправляемые браузером (Request Headers) и сервером (Response Headers) отличаются, хотя есть и общие и для запросов и для ответов (General Headers)

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

PHP и сессия

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

Язык PHP создавался под стать протоколу HTTP – т.е. основная его задача – это дать ответ на HTTP запрос и “умереть” освободив память и ресурсы. Следовательно, и механизм сессий работает в PHP не в автоматическом режиме, а в ручном, и нужно знать что вызвать, да в каком порядке.

Перво-наперво необходимо “стартовать” сессию – для этого воспользуемся функцией session_start() , создайте файл session.start.php со следующим содержимым:

Session_start();

И теперь – обновляем страничку, и видим, что браузер отправляет эту куку на сервер, можете попробовать пару раз обновить страницу, результат будет идентичным:

Итого, что мы имеем – теория совпала с практикой, и это просто отлично.

Следующий шаг – сохраним в сессию произвольное значение, для этого в PHP используется супер-глобальная переменная $_SESSION , сохранять будем текущее время – для этого вызовем функцию date() :

Session_start(); $_SESSION["time"] = date("H:i:s"); echo $_SESSION["time"];

Обновляем страничку и видим время сервера, обновляем ещё раз – и время обновилось. Давайте теперь сделаем так, чтобы установленное время не изменялось при каждом обновлении страницы:

Session_start(); if (!isset($_SESSION["time"])) { $_SESSION["time"] = date("H:i:s"); } echo $_SESSION["time"];

Обновляем – время не меняется, то что нужно. Но при этом мы помним, PHP умирает, значит данную сессию он где-то хранит, и мы найдём это место…

Всё тайное становится явным

По умолчанию, PHP хранит сессию в файлах – за это отвечает директива session.save_handler , путь по которому сохраняются файлы ищите в директиве session.save_path , либо воспользуйтесь функцией session_save_path() для получения необходимого пути.

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

Так, идём по данному пути и находим ваш файл сессии (у меня это файл sess_dap83arr6r3b56e0q7t5i0qf91), откроем его в текстовом редакторе:

Time|s:8:"16:19:51";

Как видим – вот оно наше время, вот в каком хитром формате хранится наша сессия, но мы можем внести правки, поменять время, или можем просто вписать любую строку, почему бы и нет:

Time|s:13:"\m/ (@.@) \m/";

Для преобразования этой строки в массив нужно воспользоваться функцией session_decode() , для обратного преобразования – session_encode() – это зовется сериализацией, вот только в PHP для сессий – она своя – особенная, хотя можно использовать и стандартную PHP сериализацию – пропишите в конфигурационной директиве session.serialize_handler значение php_serialize и будет вам счастье, и $_SESSION можно будет использовать без ограничений – в качестве индекса теперь вы сможете использовать цифры и специальные символы | и! в имени (за все 10+ лет работы, ни разу не надо было:)

Задание
Напишите свою функцию, аналогичную по функционалу session_decode() , вот вам тестовый набор данных для сессии (для решения знаний регулярных выражений не требуется), текст для преобразования возьмите из файла вашей текущей сессии:

$_SESSION["integer var"] = 123; $_SESSION["float var"] = 1.23; $_SESSION["octal var"] = 0x123; $_SESSION["string var"] = "Hello world"; $_SESSION["array var"] = ["one", "two", ]; $object = new stdClass(); $object->foo = "bar"; $object->arr = ["hello", "world"]; $_SESSION["object var"] = $object; $_SESSION["integer again"] = 42;

Так, что мы ещё не пробовали? Правильно – украсть “печеньки”, давайте запустим другой браузер и добавим в него теже самые cookie. Я вам для этого простенький javascript написал, скопируйте его в консоль браузера и запустите, только не забудьте идентификатор сессии поменять на свой:

Javascript:(function(){document.cookie="PHPSESSID=dap83arr6r3b56e0q7t5i0qf91;path=/;";window.location.reload();})()

Вот теперь у вас оба браузера смотрят на одну и туже сессию. Я выше упоминал, что расскажу о способах защиты, рассмотрим самый простой способ – привяжем сессию к браузеру, точнее к тому, как браузер представляется серверу – будем запоминать User-Agent и проверять его каждый раз:

Session_start(); if (!isset($_SESSION["time"])) { $_SESSION["ua"] = $_SERVER["HTTP_USER_AGENT"]; $_SESSION["time"] = date("H:i:s"); } if ($_SESSION["ua"] != $_SERVER["HTTP_USER_AGENT"]) { die("Wrong browser"); } echo $_SESSION["time"];

Это подделать сложнее, но всё ещё возможно, добавьте сюда ещё сохранение и проверку $_SERVER["REMOTE_ADDR"] и $_SERVER["HTTP_X_FORWARDED_FOR"] , и это уже более-менее будет похоже на защиту от злоумышленников посягающих на наши “печеньки”.

Ключевое слово в предыдущем абзаце похоже , в реальных проектах cookies уже давно «бегают» по HTTPS протоколу, таким образом никто их не сможет украсть без физического доступа к вашему компьютеру или смартфону

Задание
Добавьте в код проверку на IP пользователя, если проверка не прошла – удалите скомпрометированную сессию.

По шагам

А теперь поясню по шагам алгоритм, как работает сессия в PHP, на примере следующего кода (настройки по умолчанию):

Session_start(); $_SESSION["id"] = 42;

  1. после вызова session_start() PHP ищет в cookie идентификатор сессии по имени прописанном в session.name – это PHPSESSID
  2. если нет идентификатора – то он создаётся (см. session_id()), и создаёт пустой файл сессии по пути session.save_path с именем sess_{session_id()} , в ответ сервера будет добавлены заголовки, для установки cookie {session_name()}={session_id()}
  3. если идентификатор присутствует, то ищем файл сессии в папке session.save_path:
    • не находим – создаём пустой файл с именем sess_{$_COOKIE} (идентификатор может содержать лишь символы из диапазонов a-z , A-Z , 0-9 , запятую и знак минус)
    • находим, читаем файл и распаковываем данные (см. session_decode()) в супер-глобальную переменную $_SESSION
  4. когда скрипт закончил свою работу, то все данные из $_SESSION запаковывают с использованием session_encode() в файл по пути session.save_path с именем sess_{session_id()}

Задание
Задайте в вашем браузере произвольное значение куки с именем PHPSESSID , пусть это будет 1234567890 , обновите страницу, проверьте, что у вас создался новый файл sess_1234567890

А есть ли жизнь без “печенек”?

PHP может работать с сессией даже если cookie в браузере отключены, но тогда все URL на сайте будут содержать параметр с идентификатором вашей сессии, и да – это ещё настроить надо, но оно вам надо? Мне не приходилось это использовать, но если очень хочется – я просто скажу где копать:

А если надо сессию в базе данных хранить?

Для хранения сессии в БД потребуется изменить хранилище сессии и указать PHP как им пользоваться, для этой цели создан интерфейс SessionHandlerInterface и функция session_set_save_handler .

Отдельно замечу, что не надо писать собственные обработчики сессий для redis и memcache – когда вы устанавливаете данные расширения, то вместе с ними идут и соответствующие обработчики, так что RTFM наше всё. Ну и да, обработчик нужно указывать до вызова session_start() ;)

Задание
Реализуйте SessionHandlerInterface для хранения сессии в MySQL, проверьте, работает ли он.
Это задание со звёздочкой, для тех кто уже познакомился с базами данных.

Когда умирает сессия?

Интересный вопрос, можете задать его матёрым разработчикам – когда PHP удаляет файлы просроченных сессий? Ответ есть в официальном руководстве, но не в явном виде – так что запоминайте:

Сборщик мусора (garbage collection) может запускаться при вызове функции session_start() , вероятность запуска зависит от двух директив session.gc_probability и session.gc_divisor , первая выступает в качестве делимого, вторая – делителя, и по умолчанию эти значения 1 и 100, т.е. вероятность того, что сборщик будет запущен и файлы сессий будут удалены – примерно 1%.

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

Самая тривиальная ошибка

Ошибка у которой более полумиллиона результатов в выдаче Google:

Cannot send session cookie – headers already sent by
Cannot send session cache limiter – headers already sent

Для получения таковой, создайте файл session.error.php со следующим содержимым:

Echo str_pad(" ", ini_get("output_buffering")); session_start();

Во второй строке странная “магия” – это фокус с буфером вывода, я ещё расскажу о нём в одной из следующих статей, пока считайте это лишь строкой длинной в 4096 символов, в данном случае – это всё пробелы

Запустите, предварительно удалив cookie, и получите приведенные ошибки, хоть текст ошибок и разный, но суть одна – поезд ушёл – сервер уже отправил браузеру содержимое страницы, и отправлять заголовки уже поздно, это не сработает, и в куках не появилось заветного идентификатора сессии. Если вы стокнулись с данной ошибкой – ищите место, где выводится текст
раньше времени, это может быть пробел до символов в одном из подключаемых файлов, и ладно если это пробел, может быть и какой-нить непечатный символ вроде BOM , так что будьте внимательны, и вас сия зараза не коснется (как-же, … гомерический смех).

Задание
Для проверки полученных знаний, я хочу, чтобы вы реализовали свой собственный механизм сессий и заставили приведенный код работать:

Require_once "include/sess.php"; sess_start(); if (isset($_SESS["id"])) { echo $_SESS["id"]; } else { $_SESS["id"] = 42; }

Для осуществления задуманного вам потребуется функция register_shutdown_function()

В заключение

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

P.S. Если узнали что-то новое из статьи – отблагодарите автора – зашарьте статью в социалках;)

19.01.17 7.7K

С помощью сессии PHP сервер идентифицирует вас и позволяет выполнять необходимые операции: изменение информации на различных веб-страницах, добавление новой информации и т.д. После завершения работы на сайте вы удаляете текущую сессию, нажав на кнопку «Выйти »:

Что такое сессия PHP?

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

Эта информация, хранимая на протяжении сессии, доступна для всех веб-страниц ресурса. На сервере расположение временного файла определяется параметром session.save_path в конфигурационном файле php.ini .

При создании PHP-сессии выполняются следующие три действия:

  • Когда создается сессия, PHP генерирует уникальный идентификатор, который представляет собой случайную строку из 32 шестнадцатеричных чисел. Идентификатор времени жизни сессии PHP выглядит примерно так: 9c8foj87c3jj973actop1re472e8774 ;
  • Сервер отправляет на компьютер пользователя куки, называемые PHPSESSID , для хранения строки уникального идентификатора сессии;
  • Сервер генерирует в указанном временном каталоге файл, который содержит имя уникального идентификатора сессии с префиксом sess _g. sess_9c8foj87c3jj973actop1re472e8774 .

Эти установки помогают скрипту PHP извлекать из файла значения переменных сессии. На стороне клиента PHPSESSID содержит идентификатор сессии. Он подтверждает имя файла, который нужно искать в определенном каталоге на стороне сервера, из него переменные сессии могут быть извлечены и использованы для проверки.

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

Синтаксис сессий в PHP

При PHP авторизации через сессию она создается с помощью функции session_start() и удаляется с помощью функции session_destroy() . Глобальная переменная PHP , известная под именем $_SESSION , используется для установки значений переменных сессии. Сбросить все значения, установленные для переменных сессии, можно с помощью функции session_unset() .

Операции сессии

Мы рассмотрим следующие операции с использованием сессии PHP , а также их примеры.

  • Запуск сессии PHP и установка ее переменных сессии: новая сессия PHP запускается с помощью функции session_start() . После того, как сессия была создана, можно установить значения ее переменных сессии с помощью $_SESSION . Мы установили значения для переменных “userID ” — “php_user ” и “password ” — “tutorials ”:

PHP-сессии - создание Сессия PHP начата и переменные сессии заданы!"; ?>

Результат : в результате запуска приведенного выше PHP-кода на сервере будет выведено следующее сообщение:

  • Получение значений переменных сессии PHP : Можно получить значения переменных, которые мы установили во время последней PHP сессии авторизации. Когда мы открываем PHP-сессию в начале каждой страницы (session_start () ), должен указываться код, приведенный ниже. Мы извлекаем и выводим эти значения с помощью глобальной переменной $_SESSION :

PHP-сессия - получение значений
"; echo "Пароль - " . $_SESSION["password"] . "."; ?>

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

  • Обновление значений переменных сессии PHP : Во время сессии можно обновить значения ее переменных. Сначала нам нужно открыть PHP-сессию в начале каждой страницы (session_start () ). В приведенном ниже коде мы обновляем значения переменных “userID ” — “new_php_user ” и “password ” — “education ”.

Можно вывести массив переменных сессии и их значений с помощью функции print_r($ _SESSION) , как показано ниже:

PHP-сессия - изменение значений
"; print_r($_SESSION); ?>

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


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

О PHP скажу коротко - этот язык программирования используется по всему миру и на нём можно создавать сайты всех уровней сложности, от сайтов-визиток до крупных порталов. Думаю, уже для многих не секрет, что крупнейшие социальные сети facebook.com (с нуля на php) и vk.com (движок на php) были написаны на языке PHP. Так что делаем выводы и начинаем работу!)

Принцип работы кода

Код PHP отрабатывается на стороне сервера . То есть готовой страницы нет. Например, в коде дана команда собрать данные о том, сколько пользователей зарегистрировано на данный момент на сайте. Посетитель сайта нажимает на ссылку все пользователи . Он хочет получить динамические данные , то есть те, которые постоянно изменяются. После того, как подсчёт на сервере будет закончен, с сервера придут данные в виде сгенерированного HTML-кода страницы с количеством пользователей. В результате, после клика-запроса по ссылке, пользователь получает страницу. Если просмотреть код полученной страницы, то можно увидеть только HTML, а PHP-код будет недоступен для просмотра. Грубо говоря, PHP - это указания серверу по тому, как и из каких блоков сделать страницу.

Как выглядит PHP код и куда его вставлять?

PHP-код можно внедрять непосредственно в HTML. PHP-код встраивается в HTML-страницы при помощи угловых скобок и знака вопроса , однако можно ограничиться скобками со знаками вопроса . Нужно будет только изменить расширение файла, например, с .html на .php

Код PHP (файл index.php )



Пример использования PHP


echo "Привет, мир!";
?>



Демонстрация Скачать исходники
Результатом работы кода будет вывод обычного текста Привет, Мир! . Спросите зачем писать код php, чтобы вывести обычный текст? Оператор echo , о котором поговорим чуть позже, нужен не просто для вывода текста. Чаще echo используют для отображения результата работы какой-то функции, которая производила подсчёт или брала данные из БД (Что такое База данных?). То есть для отображения динамических данных .

Оператор echo в PHP

Как Вы уже поняли, оператор echo нужен для вывода данных. Содержимое (в нашем случае пока только текст) берём в кавычки , а в конце ставим точку с запятой ; это обозначает конец работы оператора.

В программирование при создании первой страницы принято употреблять фразу Hello, World! - то есть Привет, мир! Именно её и используем. В примере не будем использовать html, так как это не обязательно.

Код PHP

echo "Привет, Мир!";
?>
Программа выведет Привет, Мир! .
В самом первом примере мы вставляли в html небольшой код php. Теперь, наоборот, внедрим в php-код элементы html.

Код PHP

echo "";
echo "";
echo "Мой первый PHP скрипт";
echo "";
echo "";
echo "

Привет, Мир!

";
echo "";
?>
В результате получим пустую страничку с заголовком Привет, Мир!

Оператор print в PHP

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

print "Привет, Мир!
Вторая строка текста"; // результат будет выведен в две строки
?>
Текст будет выведен также, как и написан.

Оператор вывода - синтаксис heredoc PHP

Как Вы уже успели заметить, выводить страницу путём постоянного использования оператора echo - это некрасиво и нечитаемо. Поэтому для вывода больших частей html кода есть другой оператор вывода, использующего синтаксис heredoc. Он тоже выводит данные в таком же виде, в каком они и были (пробелы и переносы).

echo <<

Пример


Пример вывода большого объема текста с использованием html


Второй абзац такого же объёмного текста.


HERE;
?>

Памятка к уроку

Код PHP может:

1. не содержать ни одного html-элемента. Страница и текст всё равно отобразятся. html нужен для красивой разметки содержимого.

2. быть как включен в html-код, так и содержать его внутри своих операторов вывода (echo, print и тд). Главное не забывать конструкцию

3. страницы с php-кодом должны иметь соответствующее расширение: .php .phtml

Со следующих уроков мы разберём основы создания сайтов на php, в которых увидите все плюсы использования данного языка!

Спасибо за внимание!