Наш канал в telegram

Микрокомпьютер Omega2. Часть 6. Установка, настройка и использование php для интерактивного взаимодействия

Как вы знаете, я являюсь сторонником внедрения web-технологий в автоматизацию систем управления (вот здесь можно почитать подробнее о том, как я это себе представляю). В то же время современные web-технологии невозможно представить без php, — языка серверных сценариев, на котором генерируется подавляющая часть динамических web-страниц.

Давайте посмотрим, а что же предлагает микрокомпьютер Omega2 в плане web? Можно ли на него установить php? Как можно использовать php для организации web-управления?

Сразу отвечу на первый вопрос. Под OpenWRT, на базе которого собрана прошивка для наших омег, обычно используется довольно лёгкий и отлично работающий с php web-сервер под названием uhttpd (но можно поставить и другой). Установка и настройка web-сервера — тема отдельная и сегодня мы её рассматривать не будем, поэтому переходим к php.

Установка и настройка php на Omega2

Тут всё просто. Сначала подключаемся в консоль через Serial0 или по ssh и обновляем список пакетов:

root@Omega-XXXX:/# opkg update

Мы будем устанавливать самую новую версию, то есть php7. Смотрим, какие в плане php7 предлагаются варианты:

Список найденных вариантов под катом

root@Omega-XXXX:/# opkg list | grep php7
php7 - 7.1.1-1 - PHP is a widely-used general-purpose scripting language that is especially suited for Web development and can be embedded into HTML. This package contains only the PHP config file. You must actually choose your PHP flavour (cli, cgi or fastcgi).  Please note, that installing php5 and php7 in parallel on the same target is not supported in OpenWrt/LEDE.
php7-cgi - 7.1.1-1 - PHP is a widely-used general-purpose scripting language that is especially suited for Web development and can be embedded into HTML. This package contains the CGI version of the PHP7 interpreter.
php7-cli - 7.1.1-1 - PHP is a widely-used general-purpose scripting language that is especially suited for Web development and can be embedded into HTML. This package contains the CLI version of the PHP7 interpreter.
php7-fastcgi - 7.1.1-1 - As FastCGI support is now a core feature the php7-fastcgi package now depends on the php7-cgi package, containing just the startup script.
php7-fpm - 7.1.1-1 - PHP is a widely-used general-purpose scripting language that is especially suited for Web development and can be embedded into HTML. This package contains the FastCGI Process Manager of the PHP7 interpreter.
php7-mod-calendar - 7.1.1-1 - Calendar shared module
php7-mod-ctype - 7.1.1-1 - Ctype shared module
php7-mod-curl - 7.1.1-1 - cURL shared module
php7-mod-dom - 7.1.1-1 - DOM shared module
php7-mod-exif - 7.1.1-1 - EXIF shared module
php7-mod-fileinfo - 7.1.1-1 - Fileinfo shared module
php7-mod-ftp - 7.1.1-1 - FTP shared module
php7-mod-gd - 7.1.1-1 - GD graphics shared module
php7-mod-gettext - 7.1.1-1 - Gettext shared module
php7-mod-gmp - 7.1.1-1 - GMP shared module
php7-mod-hash - 7.1.1-1 - Hash shared module
php7-mod-iconv - 7.1.1-1 - iConv shared module
php7-mod-intl - 7.1.1-1 - Internationalization Functions shared module
php7-mod-json - 7.1.1-1 - JSON shared module
php7-mod-ldap - 7.1.1-1 - LDAP shared module
php7-mod-mbstring - 7.1.1-1 - MBString shared module
php7-mod-mcrypt - 7.1.1-1 - Mcrypt shared module
php7-mod-mysqli - 7.1.1-1 - MySQL Improved Extension shared module
php7-mod-opcache - 7.1.1-1 - OPcache shared module
php7-mod-openssl - 7.1.1-1 - OpenSSL shared module
php7-mod-pcntl - 7.1.1-1 - PCNTL shared module
php7-mod-pdo - 7.1.1-1 - PHP Data Objects shared module
php7-mod-pdo-mysql - 7.1.1-1 - PDO driver for MySQL shared module
php7-mod-pdo-pgsql - 7.1.1-1 - PDO driver for PostgreSQL shared module
php7-mod-pdo-sqlite - 7.1.1-1 - PDO driver for SQLite 3.x shared module
php7-mod-pgsql - 7.1.1-1 - PostgreSQL shared module
php7-mod-phar - 7.1.1-1 - Phar Archives shared module
php7-mod-session - 7.1.1-1 - Session shared module
php7-mod-shmop - 7.1.1-1 - Shared Memory shared module
php7-mod-simplexml - 7.1.1-1 - SimpleXML shared module
php7-mod-soap - 7.1.1-1 - SOAP shared module
php7-mod-sockets - 7.1.1-1 - Sockets shared module
php7-mod-sqlite3 - 7.1.1-1 - SQLite3 shared module
php7-mod-sysvmsg - 7.1.1-1 - System V messages shared module
php7-mod-sysvsem - 7.1.1-1 - System V shared memory shared module
php7-mod-sysvshm - 7.1.1-1 - System V semaphore shared module
php7-mod-tokenizer - 7.1.1-1 - Tokenizer shared module
php7-mod-xml - 7.1.1-1 - XML shared module
php7-mod-xmlreader - 7.1.1-1 - XMLReader shared module
php7-mod-xmlwriter - 7.1.1-1 - XMLWriter shared module
php7-mod-zip - 7.1.1-1 - ZIP shared module
php7-pecl-dio - 0.0.9-1 - Direct I/O functions
php7-pecl-http - 3.1.0-2 - Extended HTTP Support
php7-pecl-libevent - 2016-08-30-9e72744ce6224beafc7b54ce2a3a990f1c552a5a-1 - Libevent - event notification
php7-pecl-propro - 2.0.1-2 - Property proxy
php7-pecl-raphf - 2.0.0-2 - Resource and persistent handles factory

[свернуть]

Как видите, php7 присутствует. При желании можно установить и php5, но в родном репозитории эта версия уже недоступна. Убедиться в этом можно выполнив команду root@Omega-XXXX:/# opkg list | grep php5 или просто root@Omega-XXXX:/# opkg list | grep php.

Итак, чтобы поставить php7, — выполняем в консоли:

opkg install php7 php7-cgi

Теперь нужно настроить наш web-сервер для работы с php. Оригинальная дока советует делать это с помощью встроенного текстового редактора vi. Я, однако, предпочитаю скачивать файлы к себе на комп, редактировать их в привычном notepad++ и заливать обратно. Ещё один вариант — воспользоваться редактором, встроенным в файловый менеджер Midnight Commander, если конечно этот менеджер у вас установлен (про установку Midnight Commander и обмен файлами с омегой я ранее уже писал).

В общем, — редактируем файл конфигурации сервера (/etc/config/uhttpd), дописывая в раздел config uhttpd ‘main’ две строчки, которые содержат место расположения интерпретатора php, а также имя страницы по-умолчанию:

list interpreter ".php=/usr/bin/php-cgi"
option index_page 'index.php'

Далее нужно перезапустить web-сервер, чтобы наши правки вступили в силу:

root@Omega-XXXX:/# /etc/init.d/uhttpd restart

Осталось только проверить работоспособность. Создаём для тестов какой-нибудь файл, например вот такой:

<?php
echo phpinfo();
?>

Закачиваем его куда-нибудь внутрь папки /www (например, в папку /www/phptest/), запускаем браузер и переходим по адресу http://[your-omega-ip]/phptest/. Если всё работает правильно, то вы увидите информацию о версии и настройках вашего php.

Кстати, если бы мы загрузили нашу страничку прямо в папку /www, то она загружалась бы прямо при переходе по адресу http://[your-omega-ip], а для запуска штатной страницы настройки, которая запускалась раньше, пришлось бы в явном виде набирать http://[your-omega-ip]/index.html. Помните, мы ранее установили имя страницы по-умолчанию опцией index_page? Это относится ко всем папкам, в том числе и к корню web-сервера (только не забывайте перезапускать web-сервер и удалять кэш браузера).

Вообще, опцией index_page, можно определять не только имя одной единственной страницы по умолчанию, но и задать целый список таких страниц, установив приоритеты (кто первый в списке — тот и более приоритетный).

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

Работа с модулями Omega2 из php

Для управления модулем Omega2 через php можно воспользоваться встроенными в php функциями выполнения внешних программ и команд в оболочке shell. Подробности обо всех подобных способах можно нагуглить запросом «функции исполнения системных команд в php», но главные из таких способов, это конечно же функции, перечисленные ниже:

  • shell_exec — выполняет команду shell и возвращает полный вывод в виде строки
  • exec — выполняет внешнюю программу и возвращает последнюю строку вывода. Может заполнить строками вывода указанный массив, а также вернуть статус возврата выполненной команды в указанную переменную.
  • system — выполняет внешнюю программу и возвращает последнюю строку вывода или false в случае неудачи. Может дополнительно вывести статус возврата выполненной команды в указанную переменную.

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

Создаём файл test.php, содержащий следующий код:

<?php
$result = shell_exec('omega2-ctrl gpiomux get 2>&1');
echo "&lt;pre&gt;".$result."&lt;/pre&gt;";
?>

Загружаем этот файл в омегу, в папку /www/phptest/, открываем браузер и переходим по адресу http://[your-omega-ip]/www/phptest/test.php. Если ошибок нет, то вы увидите на экране вывод, аналогичный выводу команды omega2-ctrl gpiomux get в консоли.

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

Запись 2>&1 в конце команды означает, что вывод результатов работы команды, отправляемый в stderr, нужно перенаправить в stdout. Дело в том, что результаты работы нашей команды по-умолчанию отправляется в stderr (так часто делают с диагностическими сообщениями), а shell_exec возвращает нам вывод, отправленный в stdout, поэтому нужно использовать перенаправление. Если бы мы выполняли команду из консоли, то в результате оба потока вывода (stdout и stderr) были бы отправлены в консоль, мы в любом случае увидели бы ответ и нам по-сути было бы без разницы как они в консоль попали, через stdout или через stderr.

По умолчанию 0 — устройство ввода (stdin), 1 — устройство вывода (stdout), 2 — устройство вывода (stderr). Если вы хотите протестировать, куда отправляется результат выполнения команды, можно запустить из командной строки:

команда 1>/tmp/stdout.txt 2>/tmp/stderr.txt

В результате выполнения такой конструкции вывод отправляемый в устройство stdout будет записан в файл /tmp/stdout.txt, а вывод, отправляемый в stderr будет записан в файл stderr.txt.

Хотелось бы отметить ещё один момент. Не смотря на то, что ответ можно анализировать средствами самого php, не стоит пренебрегать таким мощным встроенным инструментом самого линуха, как команда grep.

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

Командой grep мы уже пользовались выше, когда в общем списке доступных пакетов искали те, которые имеют отношение к php7. Помните, наша команда выглядела вот так:

root@Omega-XXXX:/# opkg list | grep php7

Таким образом мы фильтровали вывод команды opkg list, которая выводит список вообще всех доступных для установки пакетов, и задавали условие, что хотим видеть не весь ответ, а только те строки, которые содержат подстроку php7

Следует учесть, что команда grep имеет много разных дополнительных опций, позволяющих, например, не учитывать регистр букв при анализе ответа (опция -i) или выводить номера строк, в которых найдена заданная подстрока (опция -n). Кроме того, эта команда умеет искать в файлах (и даже не в одном, а сразу во всех файлах каталога), рекурсивно обходить все вложенные каталоги и много чего ещё.

Ладно, не будем сильно отвлекаться, вернёмся к php.

Интерактивное взаимодействие с Omega2, вызов php-скриптов через Ajax

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

А есть у нас что-то для динамического изменения содержимого web-страницы, не перегружая при этом полностью саму страницу? Конечно, для этого у нас есть Ajax.

Как это работает покажу на реальном примере. Нам понадобятся две страницы: 1) test.html — web-страница с кнопкой, нажатие на которую будет с помощью технологии Ajax запускать расположенный на сервере php-скрипт, на этой же странице будет отображаться возвращённый скриптом результат работы; 2) test_push.php — скрипт, который будет запускаться кликом по кнопке на странице test.html и возвращать обратно на эту страницу результаты своей работы.

Листинг страницы test.html

<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'>
 
<head><title>Omega2 Ajax Test</title>
 
<script type='text/javascript'>
function getXmlHttpRequestObject()	// функция создания нового объекта XMLHttpRequest
{	var x;
	if (window.XMLHttpRequest)	// мы в нормальном браузере?
	{	// если в нормальном (IE7+, Firefox,Chrome, Opera, Safari) - создаём объект
		x=new XMLHttpRequest();	// создаём объект XMLHttpRequest
	}
	else x=false;	// если в ненормальном - болт
	return x;	// возвращаем указатель на созданный объект
}
 
// глобальные переменные
var top_http;		// здесь будем хранить указатель на объект XMLHttpRequest
var status;			// эта переменная нужна чтобы опознать, что ответ получен и обработан
var elements_col;	// количество переменных, которые мы будем запрашивать/получать
 
function handleTop()	// обработчик ответа сервера
{	if(top_http.readyState == 4)	// если запрос выполнен (4 - состояние complete)
	{	if(top_http.status == 200)	// если статус ответа - 200 ( Ok! )
		{	var value = top_http.responseText;	// получаем в переменную текст ответа
			var ArrVal=value.split(';');		// разделяем ответ на массив параметров (параметры пришли через ;)
			var obj_tag;			// здесь будет указатель на контейнер
			var i=0;				// это просто счётчик
			elements_col=ArrVal[0];	// количество элементов, которые нужно обработать 
									// первый переданный элемент - это общее кол-во параметров
									// (пар id/значение), а далее сами пары id/значение
			while(i<elements_col)
			{	// проверяем, есть ли на странице контейнер с нужным id?
				obj_tag=document.getElementById(ArrVal[2*i+1]);
				if(obj_tag)					// если есть
				{	// меняем текст внутри него на принятый от сервера
					obj_tag.innerHTML = ArrVal[2*i+2];
				}
				i=i+1;	// проверяем следующий элемент
			}
			status=1;	// меняем статус (1 - ответ получен и обработан)
		}
	}
}
 
function sendTop()	// В этой функции мы формируем запрос и отправляем его на сервер
{	status=0;	// сбрасываем статус (0 - ответ не получен и не обработан)
	top_http = getXmlHttpRequestObject();	// вызываем функцию создания объекта XMLHttpRequest
	top_http.onreadystatechange = handleTop;// цепляем на onreadystatechange обработчик ответа
											// (когда он придёт - автоматически запустится функция handle_top)
	var url='/test_push.php';				// строка запроса (запустить на сервере файл /test_push.php)
 
	top_http.open('GET', url, true);// настраиваем асинхронный запрос с адресом url
									// (это url от корня того сервера, с которого загружена страница)
	top_http.setRequestHeader('If-Modified-Since','0');	// добавляем заголовки
	top_http.setRequestHeader('Cache-Control','no-cache');	
	top_http.send(null);	// отправляем пустой запрос (поскольку у GET запроса
							// нет тела, всё, что нужно, - запихано в url)
}
</script>
 
<!-- добавим глобальный стиль для таблиц и для кнопок -->
<style type="text/css">
	td	{	border: 1px solid blue; 
			padding: 10px; 
		}
	td.button
		{	background-color: #40C781;
			box-shadow: 0 -3px #35A76E inset;
		}
	td.button:hover
		{	background-color: #35A76E;
		}
	td.button:active
		{	background-color: #21935A;
			box-shadow: 0 3px #21935A inset;
		}
</style>
 
</head>
<body>
	<table style="border: 1px solid blue">
		<tr><td class="button" onclick="sendTop()">Push Button</td><td id="result"></td></tr>
	</table>
</body>
</html>

[свернуть]
Листинг страницы test_push.php

<?php
$answer = "1;result;Yu-hu!";	// записываем в переменную строку ответа
echo $answer;	// вывод ответа (он будет отправлен обратно на страницу, с которой вызван скрипт)
?>

[свернуть]

Загружаем эти страницы в омегу, в папку www и перезапускаем веб-сервер. Теперь переходим по адресу http://[your_omega2_ip_address]/test.html и нажимаем кнопку «Push Button». Если всё работает правильно — справа должна появиться переданная нам скриптом информация (в данном случае текст «Yu-hu»).

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

Итак, что мы теперь умеем? 1) Мы умеем запускать из php-скриптов внешние программы и команды. 2) Мы умеем запускать с web-страницы php-скрипты. Собственно, это всё, что нам нужно для управления микрокомпьютером Omega2 через web. Однако, более подробно мы займёмся темой управления (и красивой визуализации) как-нибудь в другой раз, а пока — пока.

  1. Часть 1. Первое знакомство
  2. Часть 2. Использование GPIO
  3. Часть 3. Док-плата
  4. Часть 4. Работа с файлами
  5. Часть 5. Работа с USB flash-дисками
  6. Часть 6. Установка, настройка и использование php для интерактивного взаимодействия

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