- PVSM.RU - https://www.pvsm.ru -
Я часто заморачиваюсь на тему минимизации размера своих GUI приложений. Прошлая моя статья была про Nuklear [1]. Но сейчас захотелось более современных технологий. Чтоб HTML5, CSS3 и PHP. Чтоб приложение ни от чего не зависело, т.е. построено по принципу "всё включено". И чтоб конечный размер приложения не превысил 2 мегабайта. Получится ли?
В Linux я часто пользуюсь утилитой df [2]. Мне её очень не хватает в Windows, а искать аналоги лень. Так что было сделано волевое решение сделать свою, на РНР 5, с бутстрапом и JQuery.
Краткое решение моей задачи: CivetWeb [3] + WebView [4] + PH7 [5].
Т.е. результирующее приложение это и веб-сервер, который раздаёт статические файлы и выполняет скрипты (CivetWeb). И непосредственно браузер, который к этому веб-серверу подключается (WebView). PHP выполняется как СGI-BIN, через сторонний интерпретатор PH7.
Здесь заключена основная фишка связки - в качестве интерпретатора СGI-BIN может быть использован любой язык/компилятор/интерпретатор. Хоть Haxe используй, хоть Go, хоть на Powershell веб-страницы генерируй, хоть полноценный PHP возьми. Так же прямо в CivetWeb встроен интерпретатор Lua, который позволяет делать легковесные, но полноценные приложения.

Хватит лирики, перейдём к коду. PHP не самый лучший язык для создания системных приложений. Например, в нём нет функции для получения системных дисков. Но когда это нас останавливало? В Windows воспользуемся перебором всего алфавита и проверкой существования директории. В Linux же функция построчно читает файл /etc/fstab , разбивает каждую строку по пробелам чтоб получить столбцы, и проверяет на существование директории:
function fs_get_roots() {
static $roots = null;
if ($roots === null) {
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
$driveLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for ($i = 0; $i < strlen($driveLetters); $i++) {
$curDrive = $driveLetters[$i].':\';
if (is_dir($curDrive)) {
$roots[] = $curDrive;
}
}
} else {
foreach (file('/etc/fstab') as $line) {
if ($line[0] != '#') {
$line = str_replace('t', ' ', $line);
do $line = str_replace(' ', ' ', $line, $count); while($count);
$rows = explode(' ', $line);
if (count($rows) && is_dir($rows[1])) {
$roots[] = $rows[1];
}
}
}
}
}
return $roots;
}
Корни файловой системы кэшируются в статической переменной $roots. Т.е. если несколько раз вызвать эту функцию при генерации страницы, то последующий вызов отработает очень быстро, повторно диск сканироваться не будет.

С одной стороны, код получился не очень красивым. Прямой парсинг системных файлов используется в Linux для написания низкоуровневых утилит. Например, какая-нибудь из реализаций df вполне может читать /etc/fstab, такой код нормален для программ написанных на Си. Но не для высокоуровневых языков программирования. Угадывание имени диска по алфавиту - вообще злостный хак. Например, если в DVD-приводе будет вставлен диск, то при каждом обращении он будет раскручиваться...
С другой стороны, результат достигнут. PHP предназначен для генерирования веб-страниц. Но при желании даже на нём можно писать системное программное обеспечение.
В качестве отображения использую пример прогрессбара на бутстрапе [6]. Здесь вам и сам Bootstrap, и HTML5, и ещё и JQuery зачем-то подключен.
Одним из желаний было чтобы в приложение была встроена система локализации. Чтоб русскоязычные пользователи видели приложение на русском, чехи - на чешском и т.д. Система локализации будет сделана на JQuery. Ну надо же его хоть для чего-нибудь использовать:
$(document).ready(function() {
var userLang = (navigator.language || navigator.userLanguage)?.substring(0, 2)?.toLowerCase();
$('[data-' + userLang + ']').each(function(element) {
var localized = $(this).data(userLang);
$(this).text(localized);
});
});
В переменной userLang хранится 2 буквы языка браузера пользователя. Например, ru. Далее перебираем все теги у которых есть атрибут data-ru . Далее просто устанавливается текст текущего элемента, полученный из данных.
Пример HTML разметки для использования такой системы локализации:
<h2 data-ru="Системные диски" data-cz="Systémové disky">System Drives</h2>
Здесь сразу же виден основной недостаток такой системы локализации - код становится очень перегруженным, все пользователи всегда загружают данные для всех языков. Такая локализация подходит если в проекте нужно перевести порядка десятка коротких фраз и на малое количество языков. Т.е. годится только для примера.
PH7 [5] - альтернативная реализация PHP для встраиваемых систем. PH7 разрабатывалась чтоб оживить интерфейс в роутерах, где до этого часто HTML-коды встраивались прямо в прошивку, написанную на Си.
PH7 бесплатен для проектов с открытым исходным кодом. Основным преимуществом является крайне малый размер - у меня получилось скомпилировать ph7-cgi в приложение размером порядка 300 килобайт.
Недостатков у PH7 чуть больше чем достоинств. Основные:
Устаревшая версия PHP. Версия 5.3 вышла в 2009 году...
Нет поддержки регулярных выражений. Это автоматически отметает возможность использования многих библиотек (Smarty, Twig и т.д.)
Не все функции работают кроссплатформенно.
Не все функции работают.
Например, функции disk_total_space и disk_free_space всегда возвращают пустое значение в Linux, что видно на скриншоте. Я не стал делать альтернативную реализацию этих функций (например через разбор выхлопа df) т.к. это не является целью данной публикации. Да и в принципе проблематично вызывать сторонние утилиты, т.к. на месте функции exec стоит заглушка всегда возвращающая пустое значение.
В общем, PHP в данной публикации скорее элемент юмора, чем реальной пользы.
Краткое решение уже было написано выше. Рассмотрим клиентско-серверную часть: CivetWeb [3] + WebView [4].
CivetWeb (бывший Mongoose до смены лицензии) является стандартом де-факто когда нужно что-то быстро раздать по HTTP на десктопе. Это не промышленное решение типа Nginx - моему локальному серверу не нужно раздавать миллионы файлов в секунду. И сотни злобных хакеров не будут пытаться его взломать. И очень большой вопрос, будет ли он вообще когда-либо пущен во внешнюю сеть.
WebView же - просто возможность встроить системный браузер в своё приложение. Для Windows это будет Edge, для Linux - GTK WebKit, для мака - Cocoa или WebKit. Т.е. какой-нибудь однозначно современный браузер, который хорошо поддерживает и HTML5, и CSS3, и JavaScript. При этом результирующее приложение не разрастётся на сотни мегабайт, т.к. непосредственно движок в полученное приложение встроен не будет.
Обе эти технологии для меня выглядят хорошо. Единственное, я так и не нашёл ни одного проекта, объединяющих их. Поэтому пришлось делать свой: CivetWebView - https://github.com/DeXP/CivetWebView [7]
По сути CivetWebView - это объединение кода двух примеров: CivetWeb и WebView. На текущий момент проект скорее в стадии прототипа - реализован лишь необходимый мне функционал. Многие вещи заданы просто как константы в коде.
Пример развития проекта: умение загружать значения из файла конфигурации, поддержка полноэкранного режима, возможность динамической установки заголовка окна и его размеров.
Полное Win32 приложение занимает 1.53МБ. Самым объёмным компонентом является CivetWebView, который занимает почти мегабайт. Следующим идёт интерпретатор РНР размером 282 килобайта. Завершают тройку лидеров Bootstrap и JQuery, которые в сумме занимают 274 килобайта (без отладочных map-файлов). Сам код приложения (PHP + HTML + CSS) занимает порядка 5 килобайт.

Исходные коды и архивы с бинарными файлами можно скачать на гитхабе: https://github.com/DeXP/CivetWebView-PH7-Example [8]
Всё кроме PH7 лицензировано под "да делайте с этим что хотите, нам абсолютно всё равно". Т.е. если исключить из связки PH7, то можно делать коммерческие приложения. Кто-нибудь мне объяснит, зачем?)

Изначально у меня не стояла задача создания сферического GUI проекта в вакууме. Был проект онлайн интерпретатора визуальных новелл с консоли DS - VNDS-Online [9]. И мне сразу хотелось иметь возможность запускать эти игры не только на каком-нибудь сайте, но и локально на своём компьютере. Или на каком-нибудь другом устройстве.

vnds-online не может работать без серверной части. Как минимум, нужно иметь возможность получать список имеющихся игр. Этот функционал было не слишком сложно переписать на LUA [10].
Кроме того, хотелось дать возможность запускать игры простым двойным кликом по ЕХЕ-файлу. Чтоб даже самый далёкий от веб-разработки человек мог играть.
Проект в итоге получился компактным за счёт того, что большая часть кода подразумевает исполнение в браузере. Который уже установлен в систему, хорошо отлажен и оптимизирован. В общем, спасибо большое ВаЮрику [11] за замечательный движок.
Однако vnds-online тоже есть куда развиваться. Например, в движок явно нужно встроить возможность локализации. Ещё хотелось бы возможность адаптировать картинку под современные широкие экраны. Ну и отладка существующих багов разумеется.
Вместо вывода хочу задать сообществу два вопроса:
Кому-нибудь нужен CivetWebView? Стоит ли его развивать? Вы бы им пользовались? Для чего? Есть ли какие-нибудь настоящие сценарии использования, где бы оно пригодилось? Или мир уже захватили Electron на пару с Node.js?
Кому-нибудь нужен ещё один интерпретатор VNDS? Возможно под какие-нибудь странные платформы, но с современным браузером. Стоит ли вообще развивать vnds-online?
GitHub: CivetWebView [7]
GitHub: CivetWebView пример PH7 [8] (герой данной статьи)
Автор: Храбров Дмитрий
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/377066
Ссылки в тексте:
[1] Nuklear: https://habr.com/ru/post/338106/
[2] df: https://opensource.com/article/21/7/check-disk-space-linux-df
[3] CivetWeb: https://github.com/civetweb/civetweb
[4] WebView: https://github.com/webview/webview
[5] PH7: https://github.com/symisc/PH7
[6] пример прогрессбара на бутстрапе: https://bbbootstrap.com/snippets/linear-progress-bar-99672162
[7] https://github.com/DeXP/CivetWebView: https://github.com/DeXP/CivetWebView
[8] https://github.com/DeXP/CivetWebView-PH7-Example: https://github.com/DeXP/CivetWebView-PH7-Example
[9] VNDS-Online: https://github.com/VaYurik/vnds-online
[10] переписать на LUA: https://github.com/VaYurik/vnds-online/blob/master/lua/get_games_list.lua
[11] ВаЮрику: https://github.com/VaYurik
[12] Эта публикация на английском: https://dexp.in/articles/civetwebview/
[13] Источник: https://habr.com/ru/post/674192/?utm_source=habrahabr&utm_medium=rss&utm_campaign=674192
Нажмите здесь для печати.