Разное

Сервер php – PHP: $_SERVER — Manual

26.04.2020

Используем встроенный в PHP сервер

Вы здесь: Главная — PHP — PHP Основы — Используем встроенный в PHP сервер

При разработке сайтов на локальной машине мы обычно используем либо Denwer, либо OpenServer либо еще какой-то пакет разработчика сайтов. Но начиная с версии 5.4 в PHP есть встроенный веб-сервер. Он может быть запущен через командную строку в Windows, Mac или Linux. Для этого необходимо выполнить команду cd для перехода в директорию, в которой находятся файлы проекта и запустить в этой директории встроенный PHP сервер:

C:\> php –S localhost:8080

Эта команда запустит в консоли веб-сервер, который будет доступен в браузере по соответствующему адресу на порту 8080. Сервер может быть остановлен нажатием клавиш Ctrl-C. Корневой директорией в данном случае будет текущая папка:

PHP 7.1.1 Development Server started at Mon Dec 17 11:56:05 2018
Listening on localhost:8080
Document root is C:\Sites\myrusakov.lc

Press Ctrl-C to quit

Открыть сайт в браузере теперь можно по адресу http://localhost:8080. В случае, если вы явным образом не обращаетесь к какому-либо конкретному файлу, то сервер вернет по умолчанию либо index.php либо index.html из коневой директории проекта. Все запросы к адресам логируются в консоли, в которую иногда полезно заглядывать в случае возникновения ошибок.

Если же вам необходимо, чтобы локальный сайт был доступен в локальной сети

, то вместо адреса localhost, необходимо сделать следующее:

C:\> php –S 0.0.0.0:8080

Далее, нужно узнать ip адрес локального компьютера, например набрав в консоли следующую команду:

C:\> ipconfig

В категории адаптера в графе IPv4 и будет искомый адрес. Теперь к сайту или веб-приложению на вашей машине можно подключиться с другого компьютера или даже смартфона (по Wi-Fi) по тому адресу, который вы нашли выше, примерно так (не забудьте обязательно указать номер порта ):

http://X.X.X.X:8080 , где X.X.X.X – это адрес IPv4

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

  • Создано 27.12.2018 13:36:30
  • Михаил Русаков
Предыдущая статья Следующая статья

Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

Если Вы не хотите пропустить новые материалы на сайте,

то Вы можете подписаться на обновления: Подписаться на обновления

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

Порекомендуйте эту статью друзьям:

Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

  1. Кнопка:
    <a href=»https://myrusakov.ru» target=»_blank»><img src=»https://myrusakov.ru//images/button.gif» alt=»Как создать свой сайт» /></a>

    Она выглядит вот так:

  2. Текстовая ссылка:
    <a href=»https://myrusakov.ru» target=»_blank»>Как создать свой сайт</a>

    Она выглядит вот так: Как создать свой сайт

  3. BB-код ссылки для форумов (например, можете поставить её в подписи):
    [URL=»https://myrusakov.ru»]Как создать свой сайт[/URL]

myrusakov.ru

$_SERVER — Информация о сервере и среде исполнения | Руководство по PHP

(PHP 4 >= 4.1.0, PHP 5, PHP 7)

$_SERVER — $HTTP_SERVER_VARS [удалено] — Информация о сервере и среде исполнения

Описание

Переменная $_SERVER — это массив, содержащий информацию, такую как заголовки, пути и местоположения скриптов. Записи в этом массиве создаются веб-сервером. Нет гарантии, что каждый веб-сервер предоставит любую из них; сервер может опустить некоторые из них или предоставить другие, не указанные здесь. Тем не менее, многие эти переменные присутствуют в » спецификации CGI/1.1, так что вы можете их ожидать их реализации и в конкретном веб-сервере.

Переменная $HTTP_SERVER_VARS содержит ту же начальную информацию, но она не суперглобальная. (Заметьте, что $HTTP_SERVER_VARS и $_SERVER являются разными переменными, так что PHP обрабатывает их соответственно). Также учтите, что «длинные массивы» были удалены в версии PHP 5.4.0, поэтому

$HTTP_SERVER_VARS больше не существует.

Индексы

Вы можете найти (а можете и не найти) любой из следующих элементов в массиве $_SERVER. Заметьте, что немногие элементы, если вообще такие найдутся, будут доступны (или действительно будут иметь значение), если PHP запущен в командной строке.

PHP_SELF
Имя файла скрипта, который сейчас выполняется, относительно корня документов. Например,$_SERVER[‘PHP_SELF’] в скрипте по адресу http://example.com/foo/bar.php будет /foo/bar.php. Константа __FILE__ содержит полный путь и имя файла текущего (то есть подключенного) файла. Если PHP запущен в командной строке, эта переменная содержит имя скрипта, начиная с PHP 4.3.0. Раньше она была недоступна.
‘argv’
Массив аргументов, переданных скрипту. Когда скрипт запущен в командой строке, это дает C-подобный доступ к параметрам командной строки. Когда вызывается через метод GET, этот массив будет содержать строку запроса.
‘argc’
Содержит количество параметров, переданных скрипту (если запуск произведен в командной строке).
GATEWAY_INTERFACE
Содержит используемую сервером версию спецификации CGI; к примеру’CGI/1.1‘.
SERVER_ADDR
IP адрес сервера, на котором выполняется текущий скрипт.
SERVER_NAME
Имя хоста, на котором выполняется текущий скрипт. Если скрипт выполняется на виртуальном хосте, здесь будет содержатся имя, определенное для этого виртуального хоста.
SERVER_SOFTWARE
Строка идентификации сервера, указанная в заголовках, когда происходит ответ на запрос.
SERVER_PROTOCOL
Имя и версия информационного протокола, через который была запрошена страница; к примеру ‘HTTP/1.0‘;
REQUEST_METHOD
Какой метод был использован для запроса страницы; к примеру ‘GET‘, ‘HEAD‘, ‘POST‘, ‘PUT‘.

Замечание

:

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

REQUEST_TIME
Временная метка начала запроса. Доступна, начиная с PHP 5.1.0.
REQUEST_TIME_FLOAT
Временная метка начала запроса с точностью до микросекунд. Доступна, начиная с PHP 5.4.0.
QUERY_STRING
Строка запросов, если есть, с помощью которой была получена страница.
DOCUMENT_ROOT
Директория корня документов, в которой выполняется текущий скрипт, в точности та, которая указана в конфигурационном файле сервера.
HTTP_ACCEPT
Содержимое заголовка Accept: из текущего запроса, если он есть.
HTTP_ACCEPT_CHARSET
Содержимое заголовка Accept-Charset: из текущего запроса, если он есть. Например: ‘
iso-8859-1,*,utf-8
‘.
HTTP_ACCEPT_ENCODING
Содержимое заголовка Accept-Encoding: из текущего запроса, если он есть. Например: ‘gzip‘.
HTTP_ACCEPT_LANGUAGE
Содержимое заголовка Accept-Language: из текущего запроса, если он есть. Например: ‘en‘.
HTTP_CONNECTION
Содержимое заголовка Connection: из текущего запроса, если он есть. Например: ‘Keep-Alive‘.
HTTP_HOST
Содержимое заголовка Host: из текущего запроса, если он есть.
HTTP_REFERER
Адрес страницы (если есть), которая привела браузер пользователя на эту страницу. Этот заголовок устанавливается веб-браузером пользователя. Не все браузеры устанавливают его и некоторые в качестве дополнительной возможности позволяют изменять содержимое заголовка HTTP_REFERER. Одним словом, в самом деле ему нельзя доверять.
HTTP_USER_AGENT
Содержимое заголовка User-Agent: из текущего запроса, если он есть. Эта строка содержит обозначение браузера, которым пользователь запросил данную страницу. Типичным примером является строка: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586). Среди прочего, вы можете использовать это значение с функцией get_browser() чтобы адаптировать вывод вашей страницы к возможностям браузера пользователя
HTTPS
Принимает непустое значение, если запрос был произведен через протокол HTTPS.

Замечание: Обратите внимание, что при использовании ISAPI с IIS значение будет off, если запрос не был произведен через протокол HTTPS.

REMOTE_ADDR
IP-адрес, с которого пользователь просматривает текущую страницу.
REMOTE_HOST
Удаленный хост, с которого пользователь просматривает текущую страницу. Обратный просмотр DNS базируется на значении переменной REMOTE_ADDR.

Замечание: Ваш веб-сервер должен быть настроен, чтобы создавать эту переменную. Для примера, в Apache вам необходимо присутствие директивы HostnameLookups On в файле httpd.conf, чтобы эта переменная создавалась. См. также gethostbyaddr().

REMOTE_PORT
Порт на удаленной машине, который используется для связи с веб-сервером.
REMOTE_USER
Аутентифицированный пользователь.
REDIRECT_REMOTE_USER
Аутентифицированный пользователь, если запрос был перенаправлен изнутри.
SCRIPT_FILENAME

Абсолютный путь к скрипту, который в данный момент исполняется.

Замечание:

Если скрипт запускается в командной строке (CLI), используя относительный путь, такой как file.php или ../file.php, переменная $_SERVER[‘SCRIPT_FILENAME’] будет содержать относительный путь, указанный пользователем.

SERVER_ADMIN
Эта переменная получает свое значение (для Apache) из директивы конфигурационного файла сервера. Если скрипт запущен на виртуальном хосте, это будет значение, определенное для данного виртуального хоста.
SERVER_PORT
Порт на компьютере сервера, используемый веб-сервером для соединения. Для установок по умолчанию, значение будет ‘80‘; используя SLL, например, это значение будет таким, какое сконфигурировано для соединений безопасного HTTP.

Замечание: Чтобы получить физический (реальный) порт в Apache 2, необходимо установить UseCanonicalName = On и UseCanonicalPhysicalPort = On, иначе это значение может быть подменено и не вернуть реальной значение физического порта. Полагаться на это значение небезопасно в контексте приложений, требующих усиленной безопасности.

SERVER_SIGNATURE
Строка, содержащая версию сервера и имя виртуального хоста, которые добавляются к генерируемым сервером страницам, если включено.
PATH_TRANSLATED
Filesystem- (not document root-) based path to the current script, after the server has done any virtual-to-real mapping.

Замечание: Начиная с PHP 4.3.2, переменная PATH_TRANSLATED больше не устанавливается неявно в Apache 2 SAPI, по сравнению с Apache версии 1, где она устанавливается в то же самое значение, что и переменная SCRIPT_FILENAME, когда она не используется Apache. Это изменение было сделано для соответствия спецификации CGI, где переменная PATH_TRANSLATED должна существовать только тогда, когда PATH_INFO определена. Пользователи Apache 2 могут использовать директиву AcceptPathInfo = On в конфигурационном файле httpd.conf для задания переменной PATH_INFO.

SCRIPT_NAME
Содержит путь, к текущему исполняемому скрипту. Это полезно для страниц, которые должны указывать на самих себя. Константа __FILE__ содержит полный путь и имя текущего (т.е. включаемого) файла.
REQUEST_URI
URI, который был передан для того, чтобы получить доступ к этой странице. Например, ‘/index.html‘.
PHP_AUTH_DIGEST
При выполнении HTTP Digest аутентификации, этой переменной присваивается заголовок ‘Authorization’, который присылается клиентом (его необходимо потом использовать для соответствующей валидации).
PHP_AUTH_USER
Когда выполняется HTTP-аутентификация, этой переменной присваивается имя пользователя, предоставленное пользователем.
PHP_AUTH_PW
Когда выполняется HTTP-аутентификация, этой переменной присваивается пароль, предоставленный пользователем.
AUTH_TYPE
Когда выполняется HTTP-аутентификация, этой переменной присваивается тип аутентификации, который используется.
PATH_INFO
Содержит любой предоставленный пользователем путь, содержащийся после имени скрипта, но до строки запроса, если доступно. Например, если текущий скрипт запрошен по URL http://www.example.com/php/path_info.php/some/stuff?foo=bar, то переменная $_SERVER[‘PATH_INFO’] будет содержать /some/stuff.
ORIG_PATH_INFO
Исходное значение переменной ‘PATH_INFO‘ до начала обработки PHP.

Список изменений

Версия Описание
5.4.0 Массив $HTTP_SERVER_VARS больше не доступен в связи с удалением «длинных массивов».
5.3.0 Директива register_long_arrays, которая приводила к заполнению $HTTP_SERVER_VARS помечена как устаревшая.
4.1.0 Введена переменная $_SERVER вместо старой $HTTP_SERVER_VARS.

Примеры

Пример #1 Пример использования $_SERVER

<?php
echo $_SERVER['SERVER_NAME'];
?>

Результатом выполнения данного примера будет что-то подобное:

Примечания

Замечание:

Это ‘суперглобальная’ или автоматическая глобальная переменная. Это просто означает что она доступна во всех контекстах скрипта. Нет необходимости выполнять global $variable; для доступа к ней внутри метода или функции.

Смотрите также

Вернуться к: Предопределённые переменные

php.ru

PHP массив $_SERVER

Описание значений глобального массива $_SERVER с примерами.

1

Параметры сервера

$_SERVER['SERVER_NAME']

Имя хоста, обычно совпадает с доменом.

example.com

$_SERVER['GATEWAY_INTERFACE']

Версия CGI на сервере.

CGI/1.1

$_SERVER['SERVER_SOFTWARE']

Название и версия сервера.

Apache/2.4.7

$_SERVER['SERVER_SIGNATURE']

Версия сервера и имя виртуального хоста, обычно пуста.

$_SERVER['SERVER_PROTOCOL']

Имя и версия используемого HTTP протокола.

HTTP/1.0
HTTP/1.1

$_SERVER['SERVER_ADDR']

IP-адрес сервера.

127.0.0.1

$_SERVER['SERVER_PORT']

Порт сервера.

80

$_SERVER['SERVER_ADMIN']

Значение из директивы конфигурационного файла Apache.
На хостингах указывают контактный e-mail.

2

Параметры соединения

$_SERVER['HTTP_HOST']

Имя сервера, как правило, совпадает с доменом.

example.com

$_SERVER['HTTP_PORT']

Порт подключения.

80
443

$_SERVER['REMOTE_ADDR']

IP-адрес, с которого пользователь просматривает текущую страницу.

64.246.37.238
fe80:0:0:0:200:f8ff:fe21:67cf

$_SERVER['REMOTE_HOST']

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

$_SERVER['REMOTE_PORT']

Порт на удаленной машине, который используется для связи с веб-сервером.

$_SERVER['REQUEST_SCHEME']

Схема запроса.

http

$_SERVER['REQUEST_METHOD']

Метод запроса к странице.

GET
POST
PUT

$_SERVER['REQUEST_TIME']

Время запроса к серверу в Unix timestamp.

1537962596

$_SERVER['REQUEST_TIME_FLOAT']

​Время запроса к серверу с точностью до микросекунд.

1537962596.892

3

Пути на сервере

$_SERVER['DOCUMENT_ROOT']

Директория корня сайта, в которой выполняется текущий скрипт.

/home/example.com/public_html

$_SERVER['CONTEXT_DOCUMENT_ROOT']

Появился в Apache2, то же самое что и DOCUMENT_ROOT.

$_SERVER['PATH_INFO']

Содержит путь, содержащийся после имени скрипта.
Например для адреса http://site.ru/index.php/123 значение будет следующим:

/123

$_SERVER['ORIG_PATH_INFO']

Исходное значение переменной PATH_INFO перед обработкой PHP.

$_SERVER['SCRIPT_NAME']

Путь и имя выполняемого скрипта.

/index.php

$_SERVER['PHP_SELF']

​Путь к исполняемому скрипту относительно корня сайта, обычно равен SCRIPT_NAME.

/index.php

$_SERVER['SCRIPT_FILENAME']

​Абсолютный путь к исполняемому скрипту.

/home/example.com/public_html/index.php

$_SERVER['PATH']

/usr/local/bin:/usr/bin:/bin

4

Авторизация на .htpasswd

$_SERVER['AUTH_TYPE']

Метод HTTP аутентификации.

Basic

$_SERVER['REMOTE_USER'] и $_SERVER['PHP_AUTH_USER']

Логин пользователя.

$_SERVER['PHP_AUTH_PW']

Пароль.

5

HTTPS

Данные переменные появляются при HTTPS соединении:

$_SERVER['HTTPS'], $_SERVER['HTTP_X_HTTPS'], $_SERVER['REDIRECT_HTTPS']

1
On

$_SERVER['HTTP_X_FORWARDED_PROTO']

https

6

URL

Значения в примерах приведены для адреса http://site.ru/index.php?page=1&sort=2

$_SERVER['REQUEST_URI']

URI страницы с GET-параметрами, без домена.

/index.php?page=1&sort=2

$_SERVER['QUERY_STRING']

Строка GET-запроса.

page=1&sort=2

$_SERVER['argv']

Массив GET-параметров.

Array
(
    [0] => page=1&sort=2
)

$_SERVER['argc']

Количество элементов массива $_SERVER['argv'].

$_SERVER['REDIRECT_URL']

​Содержит URL страницы без GET-параметров и домена.

/index.php

7

Заголовки браузера

$_SERVER['HTTP_USER_AGENT']

Строка, обозначающая браузер и операционную систему, который открыл данную страницу.

Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36

$_SERVER['HTTP_COOKIE']

Куки браузера в виде строки: ключ=значение; ключ=значение;…
Данные доступны в переменной $_COOKIE.

_ym_uid=xxx; _ym_d=xxx; PHPSESSID=xxx;

$_SERVER['HTTP_REFERER']

Адрес страницы, с которой браузер пользователя перешёл на текущую страницу.

http://example.com/index.php

$_SERVER['HTTP_ACCEPT']

Содержимое заголовка Accept из текущего запроса.

text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

$_SERVER['HTTP_ACCEPT_ENCODING']

HTTP заголовок переданный клиентом, говорящий о том какие алгоритмы сжатия он может понять.

gzip, deflate

$_SERVER['HTTP_ACCEPT_LANGUAGE']

​Содержимое заголовка Accept-Language.

ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7

$_SERVER['HTTP_ACCEPT_CHARSET']

Предпочтения клиента относительно кодировки.

utf-8, iso-8859-1

$_SERVER['HTTP_CONNECTION']

Значение заголовка Connection.

sclose
keep-alive

$_SERVER['HTTP_CACHE_CONTROL']

max-age=0

$_SERVER['HTTP_UPGRADE_INSECURE_REQUESTS']

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

snipp.ru

PHP: встроенный web-сервер — CMScafe

Введение

Мало кто знает, но начиная с версии PHP 5.4 этот язык программирования имеет встроенный web-сервер. В этой статье мы рассмотрим его запуск и использование в ОС Windows.

Если у вас не настроен обработчик PHP в системе:

  1. Скачиваем с официального сайта нужную нам версию: http://windows.php.net/download/
  2. Распаковываем в удобную директорию (у меня это D:\PHP)
  3. Кликаем на кнопке «мой компьютер» правой кнопкой мыши – выбираем «свойства» — «дополнительные параметры системы» — «дополнительно» — «переменные среды» — переменная «path»
  4. В значениях этой переменной перечислены пути разделенные ; — добавляем путь к папке с нашим PHP.
  5. Поздравляю! Теперь в вашей командной строке windows доступна команда php.

Запуск и настройка сервера

Теперь пришло время запустить сервер. Для этого откроем терминал и перейдем в папку с сайтом. После чего вызовем команду

php -S localhost:80

либо можно использовать такую команду:

php -S localhost:80 -t D:\Path\To\Site

В данном примере флаг -t используется для указания пути к папке с исполняемыми скриптами PHP.

Вместо пути к папке – можно указать путь к исполняемому php файлу. Такой файл будет являться маршрутизатором приложения. Причем если скрипт возвращает false – то запрашиваем url будет обрабатываться сервером или вернет 404 ошибку, иначе, если скрипт возвращает что-то еще, то он передается клиенту.

флаг -S указывает на то, что нужно запустить php сервер по адресу localhost на 80 порте.

Вместо localhost можно также указать любой IP адрес по которому будет доступен сайт. Это особенно удобно, если вы используете локальную сеть.

Если вместо IP указать 0.0.0.0 – то сервер будет доступен на любом интерфейсе машине.

Иногда приложению необходимо указать специфичные настройки php.ini. Для этого предусмотрен флаг -c, позволяющий явно указать путь к такому файлу настроек.

php -S localhost:80 -c path/to/php.ini

Хорошим примером, в таких случаях, является хранить php.ini в корневой директории вашего приложения. Это может облегчить доступ к настройкам для других разработчиков, если вы работаете в команде и используете общий (сетевой) доступ к приложению. Также это позволяет выгружать файл настроек в систему контроля версий.

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

php -S localhost:80 router.php

Сценарий маршрутизации будет запускаться перед каждым HTTP-запросом.

Стоит подметить, что после запуска сервера каждый HTTP запрос отображается в терминале. Благодаря этому можно, например, заметить когда сайт вернет 400 или 500 ошибку. Вообще терминал при включенном web-сервере выполняют функцию онлайн-мониторинга состояний приложения.

Михаил Мозгов

Привет! Меня зовут Михаил. В 2007 году познакомился с Joomla! на уровне использования готовых решений + HTML и CSS. С 2012 года начал изучать PHP + SQL и JS. В 2013-2014 годах открыл для себя сайт CMSCafe. Здесь я почерпнул много интересного и познавательного материала, за что отдельное спасибо Евгению и Дмитрию. В настоящее время работаю начальником IT отдела в компании Tutmee Int.

cmscafe.ru

Суперглобальный массив $_SERVER в PHP ($HTTP_SERVER_VARS)

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

В своей статье я предлагаю таблицу с основными переменными суперглобального массива $_SERVER. Таблица подобно той, что была сделана для функций PHP для работы со строками.

Чтобы посмотреть все элементы массива $_SERVER, нужно: а) либо вызвать функцию print_r(), которая распечатает массив; б) либо вызвать функцию phpinfo(), которая выведет информацию о PHP-интерпретаторе.

Кратко о $HTTP_SERVER_VARS

Суперглобальный массив $_SERVER пришёл на смену массиву $HTTP_SERVER_VARS в PHP версии 4.1.0. $HTTP_SERVER_VARS сейчас не используется, но знать о нём стоит. Прежде всего, старая версия не являлась автоглобальной. Второе отличие заключается в том, что некоторые элементы массива $_SERVER не существуют в $HTTP_SERVER_VARS, хотя в большинстве случаев их переменные совпадают.

Элемент

Краткое описание

Пример

$_SERVER[‘DOCUMENT_ROOT’]

Содержит путь к корневому каталогу сервера.

C:\folder

/www/folder

$_SERVER[‘HTTP_HOST’]

$_SERVER[‘SERVER_NAME’]

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

www.mass-on.ru

shublog.ru

$_SERVER[‘SCRIPT_FILENAME’]

Содержит имя скрипта, начиная от корневого каталога виртуального хоста.

C:\folder\index.php

/www/folder/index.php

$_SERVER[‘PHP_SELF’]

$_SERVER[‘SCRIPT_NAME’]

Содержит имя скрипта.

/index.php

$_SERVER[‘REQUEST_URI’]

Содержит имя скрипта, начиная от корневого каталога фиртуального хоста, а также переданные ему методом GET параметры.

/www/folder/index.php?page=2&num=5

$_SERVER[‘QUERY_STRING’]

Содержит параметры, переданные скрипту методом GET.

Для адреса

http://site.com/index.php?page=2&num=5

будет выведено

page=2&num=5

$_SERVER[‘REQUEST_METHOD’]

Содержит метод запроса, который применяется для вызова скрипта.

GET или POST

$_SERVER[‘HTTP_REFERER’]

Содержит адрес страницы, с которой пришёл посетитель.

http://yandex.ru/yandsearch

$_SERVER[‘HTTP_USER_AGENT’]

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

Nokia – NOKIA; Sony Ericsson – ERICSSON или SONYERICSSON; Samsung – SAMSUNG или SEC-; Motorola – MOT; LG – LG или LG-; Alcatel – ALCATEL; Panasonic – PANASONIC; Sagem – SAGEM; Pantech – PANTECH; Siemens – SIE; BenQ – BENQ; NEC – NEC; Sharp – SHARP.

Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) (IE 6 и Windows XP)

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.0) Opera 9.50 [ru] (Opera 9.5 и Windows 2000)

$_SERVER[‘REMOTE_ADDR’]

Содержит IP-адрес клиента.

98.240.10.1

$_SERVER[‘SERVER_ADDR’]

Содержит IP-адрес сервера.

127.0.0.1

$_SERVER[‘HTTP_ACCEPT’]

Описывает предпочтения клиента относительно типа документа. Содержимое этого элемента извлекается из HTTP-заголовка Accept, который передаётся серверу клиентом.

Формат вывода: MIME-тип [[; q], другой MIME-тип [; q] … ]

Предпочитаемых MIME-типов может быть несколько, тогда они перечисляются через запятую. * используется для задания шаблона, группировки. q – коэффициент предпочтения, по умолчанию 1, изменяется от 0 до 1.

image/jpeg, image/x-xbitmap, application/x-shockwave-flash

image/*; q=0.5, image/jpeg (предпочитает жпег всем остальным форматам)

$_SERVER[‘HTTP_ACCEPT_LANGUAGE’]

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

ru, en; q=0.9 (предпочтение русского, но если его нет — и английский сойдёт)

$_SERVER[‘HTTP_ACCEPT_CHARSET’]

Подобен предыдущим. Содержит заголовок Accept-Charset

utf-8

$_SERVER[‘HTTP_ACCEPT_ENCODING’]

Подобен предыдущим. Содержит заголовок Accept-Encoding

gzip, *;q=0

$_SERVER[‘SERVER_PORT’]

Содержит прослушиваемый порт сервера.

80

$_SERVER[‘SERVER_SOFTWARE’]

Содержит информацию о веб-сервере.

Apache/2.2.4 (Win32)

$_SERVER[‘SERVER_PROTOCOL’]

Содержит версию HTTP-протокола.

HTTP/1.1

$_SERVER[‘GATEWAY_INTERFACE’]

Содержит версию CGI, используемую веб-сервером.

CGI/1.1

$_SERVER[‘REQUEST_TIME’]

Время начала запроса веб-страницы в UNIX-формате. Доступна, начиная с PHP 5.1.0

1277291674

Полный адрес страницы с параметрами:

<?php
echo ‘http://’.SERVER_NAME.$_SERVER[‘REQUEST_URI’];
?>

Таблицу со всеми функциями можно скачать по ссылке в формате *.doc.

 

 

 

ВНИМАНИЕ, СОВЕТ!

Будьте особо внимательны к переменной $_SERVER[‘REQUEST_URI’] и не забывайте её проверять! Дело в том, что её использование может быть не особо безопасным. К примеру, на Вашем сайте некоторые урлы формируются с использованием этого параметра. Тогда можно прописать в строке браузера ссылку http://site.com/index.php?”><script>alert(document.cookie)</script>. В результате будет выведено окно, где будет показано содержимое файла кукисов. Данный пример безобиден, но это является дырой, с помощью которой хакер может воспользоваться брешью, к примеру, чтобы украсть данные другого пользователя. Посему — проверяйте переменную на недопустимые символы, а особенно > и <.

На сегодня, пожалуй, всё. До новых встреч и удачных выходных!

PHP, функции

Понравилась статья или журнал? Подписывайся на продолжение!

Похожие записи:

shublog.ru

Web-разработка • PHP и MySQL

Протокол WebSocket предназначен для решения разных задач и снятия ограничений обмена данными между браузером и сервером. Он позволяет пересылать любые данные, на любой домен, безопасно и почти без лишнего сетевого трафика. Для установления соединения WebSocket клиент и сервер используют протокол, похожий на HTTP. Клиент формирует особый HTTP-запрос, на который сервер отвечает определенным образом.

Простой сокет-сервер

В первую очередь надо в файле php.ini расскомментировать строку, позволяющую работать с сокетами и перезапустить сервер:

extension = php_sockets.dll

Вот как выглядит простейший сокет-сервер:

<?php
function SocketServer($limit = 0) {
    $starttime = time();
    echo 'SERVER START' . PHP_EOL;

    echo 'Socket create...' . PHP_EOL;
    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

    if (false === $socket) {
        die('Error: ' . socket_strerror(socket_last_error()) . PHP_EOL);
    }

    echo 'Socket bind...' . PHP_EOL;
    $bind = socket_bind($socket, '127.0.0.1', 7777); // привязываем к ip и порту
    if (false === $bind) {
        die('Error: ' . socket_strerror(socket_last_error()) . PHP_EOL);
    }

    echo 'Set options...' . PHP_EOL;
    // разрешаем использовать один порт для нескольких соединений
    $option = socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
    if (false === $option) {
        die('Error: ' . socket_strerror(socket_last_error()) . PHP_EOL);
    }

    echo 'Listening socket...' . PHP_EOL;
    $listen = socket_listen($socket); // слушаем сокет
    if (false === $listen) {
        die('Error: ' . socket_strerror(socket_last_error()) . PHP_EOL);
    }

    while (true) { // бесконечный цикл ожидания подключений
        echo 'Waiting for connections...' . PHP_EOL;
        $connect = socket_accept($socket); // зависаем, пока не получим ответа
        if ($connect !== false) {
            echo 'Client connected...' . PHP_EOL;
            echo 'Send message to client...' . PHP_EOL;
            socket_write($connect, 'Hello, Client!');
        } else {
            echo 'Error: ' . socket_strerror(socket_last_error()) . PHP_EOL;
            usleep(1000);
        }

        // останавливаем сервер после $limit секунд
        if ($limit && (time() - $starttime > $limit)) {
            echo 'Closing connection...' . PHP_EOL;
            socket_close($socket);
            echo 'SERVER STOP' . PHP_EOL;
            return;
        }
    }
}

error_reporting(E_ALL); // выводим все ошибки и предупреждения
set_time_limit(0);      // бесконечное время работы скрипта
ob_implicit_flush();    // включаем вывод без буферизации

// Запускаем сервер в работу, завершение работы через 60 секунд
SocketServer(60);

Запустим его в работу:

> php.exe -f simple.php
SERVER START
Socket create...
Socket bind...
Set option...
Listening socket...
Waiting for connections...

Попробуем пообщаться с сервером с помощью telnet:

> telnet

Получив приглашение telnet, даем команду:

> open 127.0.0.1 7777

И видим сообщение от сервера:

Наш сервер в другом окне тоже встрепенулся:

WebSocket сервер

Протокол WebSocket работает над TCP. Это означает, что при соединении браузер отправляет по HTTP специальные заголовки, спрашивая: «Поддерживает ли сервер WebSocket?». Если сервер в ответных заголовках отвечает «Да, поддерживаю», то дальше HTTP прекращается и общение идёт на специальном протоколе WebSocket, который уже не имеет с HTTP ничего общего.

GET /chat HTTP/1.1
Host: websocket.server.com
Upgrade: websocket
Connection: Upgrade
Origin: http://www.example.com
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==
Sec-WebSocket-Version: 13

Здесь GET и Host — стандартные HTTP-заголовки, а Upgrade и Connection указывают, что браузер хочет перейти на WebSocket.

Сервер может проанализировать эти заголовки и решить, разрешает ли он WebSocket с данного домена Origin. Ответ сервера, если он понимает и разрешает WebSocket-подключение:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=

Для тестирования работы сервера нам нужен клиент:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>Простой WebSocket клиент</title>
    <link rel="stylesheet" href="style.css" type="text/css" />
    <script src="socket.js" type="text/javascript"></script>
</head>
<body>
    <div>
        <span>Сервер</span>
        <input type="text" value="" />
    </div>
    <div>
        <input type="button" value="Установить соединение" />
        <input type="button" value="Разорвать соединение" />
    </div>
    <div>
        <span>Сообщение</span>
        <input type="text" value="" />
        <input type="button" value="Отправить сообщение" />
    </div>
    <div>
        <span>Информация</span>
        <div></div>
    </div>
</body>
</html>
window.addEventListener('DOMContentLoaded', function () {

    var socket;

    // показать сообщение в #socket-info
    function showMessage(message) {
        var div = document.createElement('div');
        div.appendChild(document.createTextNode(message));
        document.getElementById('socket-info').appendChild(div);
    }

    /*
     * Установить соединение с сервером и назначить обработчики событий
     */
    document.getElementById('connect').onclick = function () {
        // новое соединение открываем, если старое соединение закрыто
        if (socket === undefined || socket.readyState !== 1) {
            socket = new WebSocket(document.getElementById('server').value);
        } else {
            showMessage('Надо закрыть уже имеющееся соединение');
        }

        /*
         * четыре функции обратного вызова: одна при получении данных и три – при изменениях в состоянии соединения
         */
        socket.onmessage = function (event) { // при получении данных от сервера
            showMessage('Получено сообщение от сервера: ' + event.data);
        }
        socket.onopen = function () { // при установке соединения с сервером
            showMessage('Соединение с сервером установлено');
        }
        socket.onerror = function(error) { // если произошла какая-то ошибка
            showMessage('Произошла ошибка: ' + error.message);
        };
        socket.onclose = function(event) { // при закрытии соединения с сервером
            showMessage('Соединение с сервером закрыто');
            if (event.wasClean) {
                showMessage('Соединение закрыто чисто');
            } else {
                showMessage('Обрыв соединения'); // например, «убит» процесс сервера
            }
            showMessage('Код: ' + event.code + ', причина: ' + event.reason);
        };
    };

    /*
     * Отправка сообщения серверу
     */
    document.getElementById('send-msg').onclick = function () {
        if (socket !== undefined && socket.readyState === 1) {
            var message = document.getElementById('message').value;
            socket.send(message);
            showMessage('Отправлено сообщение серверу: ' + message);
        } else {
            showMessage('Невозможно отправить сообщение, нет соединения');
        }
    };

    /*
     * Закрыть соединение с сервером
     */
    document.getElementById('disconnect').onclick = function () {
        if (socket !== undefined && socket.readyState === 1) {
            socket.close();
        } else {
            showMessage('Соединение с сервером уже было закрыто');
        }
    };

});
body > div {
    margin-bottom: 15px;
    overflow: hidden;
}
span {
    display: block;
    margin-bottom: 2px;
}
input {
    padding: 5px;
    box-sizing: border-box;
}
input[type="text"] {
    width: 100%;
}
input[type="button"] {
    width: 25%;
    float: left;
    margin-top: 5px;
    margin-right: 5px;
}
div#socket-info {
    padding: 5px;
    border: 1px solid #ddd;
}

Проверим его в работе. Открываем HTML-страницу в браузере и заполняем первое поле «Сервер»:

ws://echo.websocket.org

Это гарантированно работающий WebSocket echo-сервер, которые отправляет все сообщения обратно. Жмем кнопку «Установить соединение», набираем текст сообщения в поле «Сообщение», жмем кнопку «Отправить сообщение»:

А теперь код WebSocket сервера на PHP:

<?php
/**
 * Класс WebSocket сервера
 */
class WebSocketServer {

    /**
     * Функция вызывается, когда получено сообщение от клиента
     */
    public $handler;

    /**
     * IP адрес сервера
     */
    private $ip;
    /**
     * Порт сервера
     */
    private $port;
    /**
     * Сокет для принятия новых соединений, прослушивает указанный порт
     */
    private $connection;
    /**
     * Для хранения всех подключений, принятых слушающим сокетом
     */
    private $connects;

    /**
     * Ограничение по времени работы сервера
     */
    private $timeLimit = 0;
    /**
     * Время начала работы сервера
     */
    private $startTime;
    /**
     * Выводить сообщения в консоль?
     */
    private $verbose = false;
    /**
     * Записывать сообщения в log-файл?
     */
    private $logging = false;
    /**
     * Имя log-файла
     */
    private $logFile = 'ws-log.txt';
    /**
     * Ресурс log-файла
     */
    private $resource;


    public function __construct($ip = '127.0.0.1', $port = 7777) {
        $this->ip = $ip;
        $this->port = $port;

        // эта функция вызывается, когда получено сообщение от клиента;
        // при создании экземпляра класса должна быть переопределена
        $this->handler = function($connection, $data) {
            $message = '[' . date('r') . '] Получено сообщение от клиента: ' . $data . PHP_EOL;
            if ($this->verbose) {
                echo $message;
            }
            if ($this->logging) {
                fwrite($this->resource, $message);
            }
        };
    }

    public function __destruct() {
        if (is_resource($this->connection)) {
            $this->stopServer();
        }
        if ($this->logging) {
            fclose($this->resource);
        }
    }

    /**
     * Дополнительные настройки для отладки
     */
    public function settings($timeLimit = 0, $verbose = false, $logging = false, $logFile = 'ws-log.txt') {
        $this->timeLimit = $timeLimit;
        $this->verbose = $verbose;
        $this->logging = $logging;
        $this->logFile = $logFile;
        if ($this->logging) {
            $this->resource = fopen($this->logFile, 'a');
        }
    }

    /**
     * Выводит сообщение в консоль и/или записывает в лог-файл
     */
    private function debug($message) {
        $message = '[' . date('r') . '] ' . $message . PHP_EOL;
        if ($this->verbose) {
            echo $message;
        }
        if ($this->logging) {
            fwrite($this->resource, $message);
        }
    }

    /**
     * Отправляет сообщение клиенту
     */
    public static function response($connect, $data) {
        socket_write($connect, self::encode($data));
    }

    /**
     * Запускает сервер в работу
     */
    public function startServer() {

        $this->debug('Try start server...');

        $this->connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

        if (false === $this->connection) {
            $this->debug('Error socket_create(): ' . socket_strerror(socket_last_error()));
            return;
        }

        $bind = socket_bind($this->connection, $this->ip, $this->port); // привязываем к ip и порту
        if (false === $bind) {
            $this->debug('Error socket_bind(): ' . socket_strerror(socket_last_error()));
            return;
        }

        // разрешаем использовать один порт для нескольких соединений
        $option = socket_set_option($this->connection, SOL_SOCKET, SO_REUSEADDR, 1);
        if (false === $option) {
            $this->debug('Error socket_set_option(): ' . socket_strerror(socket_last_error()));
            return;
        }

        $listen = socket_listen($this->connection); // слушаем сокет
        if (false === $listen) {
            $this->debug('Error socket_listen(): ' . socket_strerror(socket_last_error()));
            return;
        }

        $this->debug('Server is running...');

        $this->connects = array($this->connection);
        $this->startTime = time();

        while (true) {

            $this->debug('Waiting for connections...');

            // создаем копию массива, так что массив $this->connects не будет изменен функцией socket_select()
            $read = $this->connects;
            $write = $except = null;

            /*
             * Сокет $this->connection только прослушивает порт на предмет новых соединений. Как только поступило
             * новое соединение, мы создаем новый ресурс сокета с помощью socket_accept() и помещаем его в массив
             * $this->connects для дальнейшего чтения из него.
             */

            if ( ! socket_select($read, $write, $except, null)) { // ожидаем сокеты, доступные для чтения (без таймаута)
                break;
            }

            // если слушающий сокет есть в массиве чтения, значит было новое соединение
            if (in_array($this->connection, $read)) {
                // принимаем новое соединение и производим рукопожатие
                if (($connect = socket_accept($this->connection)) && $this->handshake($connect)) {
                    $this->debug('New connection accepted');
                    $this->connects[] = $connect; // добавляем его в список необходимых для обработки
                }
                // удаляем слушающий сокет из массива для чтения
                unset($read[ array_search($this->connection, $read) ]);
            }

            foreach ($read as $connect) { // обрабатываем все соединения, в которых есть данные для чтения
                $data = socket_read($connect, 100000);
                $decoded = self::decode($data);
                // если клиент не прислал данных или хочет разорвать соединение
                if (false === $decoded || 'close' === $decoded['type']) {
                    $this->debug('Connection closing');
                    socket_write($connect, self::encode('  Closed on client demand', 'close'));
                    socket_shutdown($connect);
                    socket_close($connect);
                    unset($this->connects[ array_search($connect, $this->connects) ]);
                    $this->debug('Closed successfully');
                    continue;
                }
                // получено сообщение от клиента, вызываем пользовательскую
                // функцию, чтобы обработать полученные данные
                if (is_callable($this->handler)) {
                    call_user_func($this->handler, $connect, $decoded['payload']);
                }
            }

            // если истекло ограничение по времени, останавливаем сервер
            if ($this->timeLimit && time() - $this->startTime > $this->timeLimit) {
                $this->debug('Time limit. Stopping server.');
                $this->stopServer();
                return;
            }

        }

    }

    /**
     * Останавливает работу сервера
     */
    public function stopServer() {
        // закрываем слушающий сокет
        socket_close($this->connection);
        if (!empty($this->connects)) { // отправляем все клиентам сообщение о разрыве соединения
            foreach ($this->connects as $connect) {
                if (is_resource($connect)) {
                    socket_write($connect, self::encode('  Closed on server demand', 'close'));
                    socket_shutdown($connect);
                    socket_close($connect);
                }
            }
        }
    }

    /**
     * Для кодирования сообщений перед отправкой клиенту
     */
    private static function encode($payload, $type = 'text', $masked = false) {
        $frameHead = array();
        $payloadLength = strlen($payload);

        switch ($type) {
            case 'text':
                // first byte indicates FIN, Text-Frame (10000001):
                $frameHead[0] = 129;
                break;
            case 'close':
                // first byte indicates FIN, Close Frame(10001000):
                $frameHead[0] = 136;
                break;
            case 'ping':
                // first byte indicates FIN, Ping frame (10001001):
                $frameHead[0] = 137;
                break;
            case 'pong':
                // first byte indicates FIN, Pong frame (10001010):
                $frameHead[0] = 138;
                break;
        }

        // set mask and payload length (using 1, 3 or 9 bytes)
        if ($payloadLength > 65535) {
            $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
            $frameHead[1] = ($masked === true) ? 255 : 127;
            for ($i = 0; $i < 8; $i++) {
                $frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
            }
            // most significant bit MUST be 0
            if ($frameHead[2] > 127) {
                return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)');
            }
        } elseif ($payloadLength > 125) {
            $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
            $frameHead[1] = ($masked === true) ? 254 : 126;
            $frameHead[2] = bindec($payloadLengthBin[0]);
            $frameHead[3] = bindec($payloadLengthBin[1]);
        } else {
            $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
        }

        // convert frame-head to string:
        foreach (array_keys($frameHead) as $i) {
            $frameHead[$i] = chr($frameHead[$i]);
        }
        if ($masked === true) {
            // generate a random mask:
            $mask = array();
            for ($i = 0; $i < 4; $i++) {
                $mask[$i] = chr(rand(0, 255));
            }
            $frameHead = array_merge($frameHead, $mask);
        }
        $frame = implode('', $frameHead);

        // append payload to frame:
        for ($i = 0; $i < $payloadLength; $i++) {
            $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
        }

        return $frame;
    }

    /**
     * Для декодирования сообщений, полученных от клиента
     */
    private static function decode($data) {
        if ( ! strlen($data)) {
            return false;
        }

        $unmaskedPayload = '';
        $decodedData = array();

        // estimate frame type:
        $firstByteBinary = sprintf('%08b', ord($data[0]));
        $secondByteBinary = sprintf('%08b', ord($data[1]));
        $opcode = bindec(substr($firstByteBinary, 4, 4));
        $isMasked = ($secondByteBinary[0] == '1') ? true : false;
        $payloadLength = ord($data[1]) & 127;

        // unmasked frame is received:
        if (!$isMasked) {
            return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)');
        }

        switch ($opcode) {
            // text frame:
            case 1:
                $decodedData['type'] = 'text';
                break;
            case 2:
                $decodedData['type'] = 'binary';
                break;
            // connection close frame:
            case 8:
                $decodedData['type'] = 'close';
                break;
            // ping frame:
            case 9:
                $decodedData['type'] = 'ping';
                break;
            // pong frame:
            case 10:
                $decodedData['type'] = 'pong';
                break;
            default:
                return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)');
        }

        if ($payloadLength === 126) {
            $mask = substr($data, 4, 4);
            $payloadOffset = 8;
            $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;
        } elseif ($payloadLength === 127) {
            $mask = substr($data, 10, 4);
            $payloadOffset = 14;
            $tmp = '';
            for ($i = 0; $i < 8; $i++) {
                $tmp .= sprintf('%08b', ord($data[$i + 2]));
            }
            $dataLength = bindec($tmp) + $payloadOffset;
            unset($tmp);
        } else {
            $mask = substr($data, 2, 4);
            $payloadOffset = 6;
            $dataLength = $payloadLength + $payloadOffset;
        }

        /**
         * We have to check for large frames here. socket_recv cuts at 1024 bytes
         * so if websocket-frame is > 1024 bytes we have to wait until whole
         * data is transferd.
         */
        if (strlen($data) < $dataLength) {
            return false;
        }

        if ($isMasked) {
            for ($i = $payloadOffset; $i < $dataLength; $i++) {
                $j = $i - $payloadOffset;
                if (isset($data[$i])) {
                    $unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
                }
            }
            $decodedData['payload'] = $unmaskedPayload;
        } else {
            $payloadOffset = $payloadOffset - 4;
            $decodedData['payload'] = substr($data, $payloadOffset);
        }

        return $decodedData;
    }

    /**
     * «Рукопожатие», т.е. отправка заголовков согласно протоколу WebSocket
     */
    private function handshake($connect) {

        $info = array();

        $data = socket_read($connect, 1000);
        $lines = explode("\r\n", $data);
        foreach ($lines as $i => $line) {
            if ($i) {
                if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
                    $info[$matches[1]] = $matches[2];
                }
            } else {
                $header = explode(' ', $line);
                $info['method'] = $header[0];
                $info['uri'] = $header[1];
            }
            if (empty(trim($line))) break;
        }

        // получаем адрес клиента
        $ip = $port = null;
        if ( ! socket_getpeername($connect, $ip, $port)) {
            return false;
        }
        $info['ip'] = $ip;
        $info['port'] = $port;

        if (empty($info['Sec-WebSocket-Key'])) {
            return false;
        }

        // отправляем заголовок согласно протоколу вебсокета
        $SecWebSocketAccept = 
            base64_encode(pack('H*', sha1($info['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
        $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
                   "Upgrade: websocket\r\n" .
                   "Connection: Upgrade\r\n" .
                   "Sec-WebSocket-Accept:".$SecWebSocketAccept."\r\n\r\n";
        socket_write($connect, $upgrade);

        return true;

    }

}

Для тестирования напишем небольшой PHP-скрипт, который запускает в работу сервер и все сообщения клиента отправляет обратно (echo-сервер):

<?php 
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();

require 'WebSocketServer.class.php';

$server = new WebSocketServer('127.0.0.1', 7777);
// максимальное время работы 100 секунд, выводить сообщения в консоль
$server->settings(100, true);

// эта функция вызывается, когда получено сообщение от клиента
$server->handler = function($connect, $data) {
    // полученные от клиента данные отправляем обратно
    WebSocketServer::response($connect, $data);
};

$server->startServer();

Запускаем скрипт в работу:

> php.exe -f echo-server.php
[Fri, 12 Oct 2018 15:08:13 +0300] Try start server...
[Fri, 12 Oct 2018 15:08:13 +0300] Server is running...
[Fri, 12 Oct 2018 15:08:13 +0300] Waiting for connections...

Еще один пример использования сервера — клиент отправляет команды, а сервер их выполняет:

<?php 
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();

require 'WebSocketServer.class.php';

$server = new WebSocketServer('127.0.0.1', 7777);
// максимальное время работы 100 секунд, выводить сообщения в консоль
$server->settings(100, true);

// эта функция вызывается, когда получено сообщение от клиента
$server->handler = function($connect, $data) {
    // анализируем поступившую команду и даем ответ
    if ( ! in_array($data, array('date', 'time', 'country', 'city'))) {
        WebSocketServer::response($connect, 'Неизвестная команда');
        return;
    }
    switch ($data) {
        case 'date'   : $response = date('d.m.Y'); break;
        case 'time'   : $response = date('H:i:s'); break;
        case 'country': $response = 'Россия';      break;
        case 'city'   : $response = 'Москва';      break;
    }
    WebSocketServer::response($connect, $response);
};

$server->startServer();

Альтернативная реализация WebSocket сервера с использованием функций для работы с потоками:

<?php
/**
 * Класс WebSocket сервера
 */
class WebSocketServer {

    /**
     * Функция вызывается, когда получено сообщение от клиента
     */
    public $handler;

    /**
     * IP адрес сервера
     */
    private $ip;
    /**
     * Порт сервера
     */
    private $port;
    /**
     * Для хранения слушающего сокета потока
     */
    private $connection;
    /**
     * Для хранения всех подключений
     */
    private $connects;

    /**
     * Ограничение по времени работы сервера
     */
    private $timeLimit = 0;
    /**
     * Время начала работы сервера
     */
    private $startTime;
    /**
     * Выводить сообщения в консоль?
     */
    private $verbose = false;
    /**
     * Записывать сообщения в log-файл?
     */
    private $logging = false;
    /**
     * Имя log-файла
     */
    private $logFile = 'ws-log.txt';
    /**
     * Ресурс log-файла
     */
    private $resource;


    public function __construct($ip = '127.0.0.1', $port = 7777) {
        $this->ip = $ip;
        $this->port = $port;

        // эта функция вызывается, когда получено сообщение от клиента;
        // при создании экземпляра класса должна быть переопределена
        $this->handler = function($connection, $data) {
            $message = '[' . date('r') . '] Получено сообщение от клиента: ' . $data . PHP_EOL;
            if ($this->verbose) {
                echo $message;
            }
            if ($this->logging) {
                fwrite($this->resource, $message);
            }
        };
    }

    public function __destruct() {
        if (is_resource($this->connection)) {
            $this->stopServer();
        }
        if ($this->logging) {
            fclose($this->resource);
        }
    }

    /**
     * Дополнительные настройки для отладки
     */
    public function settings($timeLimit = 0, $verbose = false, $logging = false, $logFile = 'ws-log.txt') {
        $this->timeLimit = $timeLimit;
        $this->verbose = $verbose;
        $this->logging = $logging;
        $this->logFile = $logFile;
        if ($this->logging) {
            $this->resource = fopen($this->logFile, 'a');
        }
    }

    /**
     * Выводит сообщение в консоль или записывает в лог-файл
     */
    private function debug($message) {
        $message = '[' . date('r') . '] ' . $message . PHP_EOL;
        if ($this->verbose) {
            echo $message;
        }
        if ($this->logging) {
            fwrite($this->resource, $message);
        }
    }

    /**
     * Отправляет сообщение клиенту
     */
    public static function response($connect, $data) {
        fwrite($connect, self::encode($data));
    }

    /**
     * Запускает сервер в работу
     */
    public function startServer() {
        
        $this->debug('Try start server...');

        $this->connection = stream_socket_server('tcp://' . $this->ip . ':' . $this->port, $errno, $errstr);
        
        if ( ! $this->connection) {
            $this->debug('Cannot start server: ' .$errstr. '(' .$errno. ')');
            return false;
        }

        $this->debug('Server is running...');

        $this->connects = array();
        $this->startTime = time();

        while (true) {

            $this->debug('Waiting for connections...');

            // формируем массив прослушиваемых сокетов
            $read = $this->connects;
            $read[] = $this->connection;
            $write = $except = null;

            if ( ! stream_select($read, $write, $except, null)) { // ожидаем сокеты доступные для чтения (без таймаута)
                break;
            }

            if (in_array($this->connection, $read)) { // есть новое соединение
                // принимаем новое соединение и производим рукопожатие
                if (($connect = stream_socket_accept($this->connection, -1)) && $this->handshake($connect)) {
                    $this->debug('New connection accepted');
                    $this->connects[] = $connect; // добавляем его в список необходимых для обработки
                }
                unset($read[ array_search($this->connection, $read) ]);
            }

            foreach ($read as $connect) { // обрабатываем все соединения
                $data = fread($connect, 100000);
                $decoded = self::decode($data);
                // если клиент не прислал данных или хочет разорвать соединение
                if (false === $decoded || 'close' === $decoded['type']) {
                    $this->debug('Connection closing');
                    fwrite($connect, self::encode('  Closed on client demand', 'close'));
                    fclose($connect);
                    unset($this->connects[ array_search($connect, $this->connects) ]);
                    $this->debug('Closed successfully');
                    continue;
                }
                // получено сообщение от клиента, вызываем пользовательскую
                // функцию, чтобы обработать полученные данные
                if (is_callable($this->handler)) {
                    call_user_func($this->handler, $connect, $decoded['payload']);
                }
            }

            // если истекло ограничение по времени, останавливаем сервер
            if ($this->timeLimit && time() - $this->startTime > $this->timeLimit) {
                $this->debug('Time limit. Stopping server.');
                $this->stopServer();
                return;
            }
        }
    }

    /**
     * Останавливает работу сервера
     */
    public function stopServer() {
        fclose($this->connection); // закрываем слушающий сокет
        if (!empty($this->connects)) { // отправляем все клиентам сообщение о разрыве соединения
            foreach ($this->connects as $connect) {
                if (is_resource($connect)) {
                    fwrite($connect, self::encode('  Closed on server demand', 'close'));
                    fclose($connect);
                }
            }
        }
    }

    /**
     * Для кодирования сообщений перед отправкой клиенту
     */
    private static function encode($payload, $type = 'text', $masked = false) {
        $frameHead = array();
        $payloadLength = strlen($payload);

        switch ($type) {
            case 'text':
                // first byte indicates FIN, Text-Frame (10000001):
                $frameHead[0] = 129;
                break;
            case 'close':
                // first byte indicates FIN, Close Frame(10001000):
                $frameHead[0] = 136;
                break;
            case 'ping':
                // first byte indicates FIN, Ping frame (10001001):
                $frameHead[0] = 137;
                break;
            case 'pong':
                // first byte indicates FIN, Pong frame (10001010):
                $frameHead[0] = 138;
                break;
        }

        // set mask and payload length (using 1, 3 or 9 bytes)
        if ($payloadLength > 65535) {
            $payloadLengthBin = str_split(sprintf('%064b', $payloadLength), 8);
            $frameHead[1] = ($masked === true) ? 255 : 127;
            for ($i = 0; $i < 8; $i++) {
                $frameHead[$i + 2] = bindec($payloadLengthBin[$i]);
            }
            // most significant bit MUST be 0
            if ($frameHead[2] > 127) {
                return array('type' => '', 'payload' => '', 'error' => 'frame too large (1004)');
            }
        } elseif ($payloadLength > 125) {
            $payloadLengthBin = str_split(sprintf('%016b', $payloadLength), 8);
            $frameHead[1] = ($masked === true) ? 254 : 126;
            $frameHead[2] = bindec($payloadLengthBin[0]);
            $frameHead[3] = bindec($payloadLengthBin[1]);
        } else {
            $frameHead[1] = ($masked === true) ? $payloadLength + 128 : $payloadLength;
        }

        // convert frame-head to string:
        foreach (array_keys($frameHead) as $i) {
            $frameHead[$i] = chr($frameHead[$i]);
        }
        if ($masked === true) {
            // generate a random mask:
            $mask = array();
            for ($i = 0; $i < 4; $i++) {
                $mask[$i] = chr(rand(0, 255));
            }
            $frameHead = array_merge($frameHead, $mask);
        }
        $frame = implode('', $frameHead);

        // append payload to frame:
        for ($i = 0; $i < $payloadLength; $i++) {
            $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
        }

        return $frame;
    }

    /**
     * Для декодирования сообщений, полученных от клиента
     */
    private static function decode($data) {
        if ( ! strlen($data)) {
            return false;
        }

        $unmaskedPayload = '';
        $decodedData = array();

        // estimate frame type:
        $firstByteBinary = sprintf('%08b', ord($data[0]));
        $secondByteBinary = sprintf('%08b', ord($data[1]));
        $opcode = bindec(substr($firstByteBinary, 4, 4));
        $isMasked = ($secondByteBinary[0] == '1') ? true : false;
        $payloadLength = ord($data[1]) & 127;

        // unmasked frame is received:
        if (!$isMasked) {
            return array('type' => '', 'payload' => '', 'error' => 'protocol error (1002)');
        }

        switch ($opcode) {
            // text frame:
            case 1:
                $decodedData['type'] = 'text';
                break;
            case 2:
                $decodedData['type'] = 'binary';
                break;
            // connection close frame:
            case 8:
                $decodedData['type'] = 'close';
                break;
            // ping frame:
            case 9:
                $decodedData['type'] = 'ping';
                break;
            // pong frame:
            case 10:
                $decodedData['type'] = 'pong';
                break;
            default:
                return array('type' => '', 'payload' => '', 'error' => 'unknown opcode (1003)');
        }

        if ($payloadLength === 126) {
            $mask = substr($data, 4, 4);
            $payloadOffset = 8;
            $dataLength = bindec(sprintf('%08b', ord($data[2])) . sprintf('%08b', ord($data[3]))) + $payloadOffset;
        } elseif ($payloadLength === 127) {
            $mask = substr($data, 10, 4);
            $payloadOffset = 14;
            $tmp = '';
            for ($i = 0; $i < 8; $i++) {
                $tmp .= sprintf('%08b', ord($data[$i + 2]));
            }
            $dataLength = bindec($tmp) + $payloadOffset;
            unset($tmp);
        } else {
            $mask = substr($data, 2, 4);
            $payloadOffset = 6;
            $dataLength = $payloadLength + $payloadOffset;
        }

        /**
         * We have to check for large frames here. socket_recv cuts at 1024 bytes
         * so if websocket-frame is > 1024 bytes we have to wait until whole
         * data is transferd.
         */
        if (strlen($data) < $dataLength) {
            return false;
        }

        if ($isMasked) {
            for ($i = $payloadOffset; $i < $dataLength; $i++) {
                $j = $i - $payloadOffset;
                if (isset($data[$i])) {
                    $unmaskedPayload .= $data[$i] ^ $mask[$j % 4];
                }
            }
            $decodedData['payload'] = $unmaskedPayload;
        } else {
            $payloadOffset = $payloadOffset - 4;
            $decodedData['payload'] = substr($data, $payloadOffset);
        }

        return $decodedData;
    }

    /**
     * «Рукопожатие», т.е. отправка заголовков согласно протоколу WebSocket
     */
    private function handshake($connect) {
        $info = array();

        $line = fgets($connect);
        $header = explode(' ', $line);
        $info['method'] = $header[0];
        $info['uri'] = $header[1];

        // считываем заголовки из соединения
        while ($line = rtrim(fgets($connect))) {
            if (preg_match('/\A(\S+): (.*)\z/', $line, $matches)) {
                $info[$matches[1]] = $matches[2];
            } else {
                break;
            }
        }

        // получаем адрес клиента
        $address = explode(':', stream_socket_get_name($connect, true));
        $info['ip'] = $address[0];
        $info['port'] = $address[1];

        if (empty($info['Sec-WebSocket-Key'])) {
            return false;
        }

        // отправляем заголовок согласно протоколу вебсокета
        $SecWebSocketAccept = 
            base64_encode(pack('H*', sha1($info['Sec-WebSocket-Key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
        $upgrade = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
                   "Upgrade: websocket\r\n" .
                   "Connection: Upgrade\r\n" .
                   "Sec-WebSocket-Accept:".$SecWebSocketAccept."\r\n\r\n";
        fwrite($connect, $upgrade);

        return $info;
    }

}
Дополнительно

Поиск: HandShake • JavaScript • PHP • Server • Socket • Web-разработка • WebSocket • Клиент • Протокол • Сервер • Сокет

tokmakov.msk.ru

PHP Server Monitor — Записки админа

Делаем очень простой мониторинг сервера или сайта с помощью скрипта PHP Server Monitor. Для установки и базовой работы подойдёт почти любой хостинг или сервер.

Для запуска скрипта, нам потребуется:

— PHP версии 5.3.7 и выше.
— PHP модули curl и mysql.
— Непосредственно MySQL\MariaDB база.

Установка PHP Server Monitor.

Ставим PHP Server Monitor как любой другой PHP скрипт.

1. Скачиваем, распаковываем, запускаем install.php в браузере. Ссылка на загрузку доступна на странице проекта. Загружаем архив на сервер удобным для нас способом (работая по SSH, если есть такая возможность, либо по FTP, если это обычный хостинг).

2. Жмём на кнопку Let’s go, следуем инструкциям системы.

  • Указываем данные для соединения с базой данных (она, конечно же, должна быть предварительно создана), адрес панели мониторинга.
  • Следующим шагом PSM попытается записать конфиг, если у него по какой-то причине не хватит прав, будет предложено создать config.php вручную.
  • Последним шагом здесь будет создание пользователя, которым мы и будем работать с мониторингом.
Добавляем ресурс в мониторинг.

1. Авторизуемся в панели созданным ранее пользователем и переходим в раздел Servers. Нажимаем на кнопку «Add new +» для добавления нового сервиса.

2. На открывшейся странице заполняем нужные нам данные:

  • Label — название (метка) ресурса в мониторинге.
  • Domain/IP — имя домена или IP адрес, который мы будем мониторить.
  • Type — тип мониторинга. Здесь мы можем выбрать простой пинг, указать конкретный сервис, либо указать адрес сайта. В соответствии с выбором далее система предложит настроить либо сервис/порт, который будет проверяться, либо ключевое слово, которое будет проверять мониторинг на странице. Так же, будет предложено настроить таймауты проверки.
  • Warning threshold — количество failed ответов, после которого сервер будет помечен как упавший.

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

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

Когда всё будет заполнено и настроено, нажимаем на кнопку Save, переходим во вкладку Status и видим там наш добавленный ресурс.

Настраиваем уведомления.

Монитоинг поддерживает несколько видов уведомлений — email, sms, pushover. Всё это настраивается во вкладке Config, далее, в соответствующей секции. Для почты имеется возможность прописать данные для SMTP авторизации, для отправки SMS уведомлений доступны на выбор несколько провайдеров, ну а для PUSH сообщений потребуется аккаунт на pushover.net

Дополнительные настройки.

Опять же, в разделе Config, в секции General мы при необходимости можем настроить язык интерфейса (русский имеется), указать ключ, которым будут зашифрованы введённые пароли, задать статус, в соответствии с которым будут отправляться уведомления, включить логи и, при необходимости, подключить работу через proxy сервер.

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

@SysadminNotes | https://sysadmin.pm

sysadmin.pm

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *