Как стать автором
Обновить
1448.57
Timeweb Cloud
То самое облако

Компилируем быстрые консольные .exe приложения на PHP 8.1 в 2023 году, а почему бы и нет? (upd)

Уровень сложностиСредний
Время на прочтение4 мин
Количество просмотров6.6K
С каждым релизом PHP становится всё быстрее, а при включении JIT (Just-In-Time) компиляции, достигает почти отметок того же C.

image

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

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

Чтобы поностальгировать и продемонстрировать нового Франкенштейна, мы соберем полноценное консольное exe-приложение на PHP.

PHP — интерпретируемый язык, поэтому для работы нам просто необходимо иметь его бинарные файлы.

Для создания приложения нам понадобятся основной exe-файл и dll-библиотека движка. 32-битная версия PHP 8.1 занимает 7 Мб! Hello World с размером в 7 метров нам не подходит, согласитесь.

Следуя инструкции, мне удалось скомпилировать PHP без каких-либо дополнительных модулей, оставив только JIT и FFI (есть причина).

Размер получившихся файлов составил 5 Мб (x64), что уже не плохо, но недостаточно. Мы продолжим работать с x64, так как разница между ним и x86 составляет всего 100кб.

php.exe + php8.dll:


image


Теперь нам нужен PHP-код для нашего приложения.

Ради примера возьмём скрипт Zend Benchmark и добавим несколько аргументов командной строки для тестов.

Чтобы упаковать наше приложение в один файл, мы будем использовать бесплатную утилиту Enigma Virtual Box.

Но для этого Энигме нужен главный обработчик, который вызовет интерпретатор.

Скачиваем 32 битную версию gcc, устанавливаем, перезаходим в учётную запись, берем и делаем «gcc -o dabro.exe program.c», содержимое program.c (не забудьте поменять имя папки):

#include <windows.h>
#include <stdio.h>

int main(int argc, char **argv) {
    char command[1024];
    sprintf(command, "\"%s\\имя папки\\render.exe\" -f \"%s\\имя папки\\exe.c\" %s", getenv("LOCALAPPDATA"), getenv("LOCALAPPDATA"), GetCommandLineA() + strlen(argv[0]) + 1);

    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;

    BOOL success = CreateProcess(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
    if (!success) {
        DWORD error = GetLastError();
        printf("Error: %d\n", error);
        return error;
    }

    WaitForSingleObject(pi.hProcess, INFINITE);

    DWORD exitCode;
    GetExitCodeProcess(pi.hProcess, &exitCode);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    return exitCode;
}


Версия с мультиязычной поддержкой в названиях папок и параметров (Нужен Visual Studio 2013+ для компиляции):

Скрытый текст
#define _CRT_SECURE_NO_WARNINGS

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <locale.h>

int wmain(int argc, wchar_t **argv) {

	wchar_t command[32000];

	if (argc >= 2 && wcslen(argv[1]) > 0 && argv[1][0] == L'-') {
		argv++;
		argc--;
		// Сбрасываем строку, если первая команда содержит тире
	}

	wchar_t localAppData[MAX_PATH];
	if (GetEnvironmentVariableW(L"LOCALAPPDATA", localAppData, MAX_PATH) == 0) {
		wprintf(L"Error: Unable to retrieve LOCALAPPDATA environment variable!\n");
		return -1;
	}

	swprintf(command, 32000, L"\"%s\\имя папки\\render.exe\" -f \"%s\\имя папки\\exe.c\" %s", localAppData, localAppData, GetCommandLineW() + wcslen(argv[0]) + 1);

	STARTUPINFOW si = { sizeof(si) };
	PROCESS_INFORMATION pi;
	if (!CreateProcessW(NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
		DWORD error = GetLastError();
		wprintf(L"Error: %d\n", error);
		return error;
	}

	WaitForSingleObject(pi.hProcess, INFINITE);

	DWORD exitCode;
	GetExitCodeProcess(pi.hProcess, &exitCode);

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);

	return exitCode;
}



Иконку программы вы можете добавить через --icon=file.ico при компилировании в gcc, или в Resource Tuner, добавив ресурс Icon.

Выбираем обработчик (input file), забрасываем наши бинарники с исходниками в окно Энигмы, используем папку Local, %Application Data Folder%. В Files Option кликаем на галочку компрессии.

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

image

Скомпилировав приложение мы получаем портативный файл, который весит 2.7 Мб!

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

Сжатый .7z архив с нынешними бинарниками весит всего 1.5 Мб.

Для сравнения, скомпилированный Hello World бинарник Go приложения, весит 1.2 Мб.

Тестируем:

image

Работает как часы, чтобы вы понимали про заявленную скорость ассемблер кода генерируемым JIT, моя машина проходит бенчмарк за 0.7с (с настройкой function JIT 0.3с), при выключенном же JIT, она просядает до 2.4с.
Реализовать параллельность для ресурсоёмких вычислительных операций не проблема (функции popen() / proc_open), можно также воспользоваться готовыми библиотеками, как amphp/parallel.

Запуск самих приложений быстрый и составляет менее секунды.

Вычислим ещё корневой Merkle хеш большого файла:

image


Вы не забыли про FFI?

Благодаря этому расширению мы можем писать C код прямо в PHP, подключать dll библиотеки, создавать WinAPI приложения, вот пример кода MessageBox:

<?php
$ffi = FFI::cdef("
    int MessageBoxA(void*, const char*, const char*, int);
", "user32.dll");

$ffi->MessageBoxA(null, "Hello!", "Habr", 0);
?>

А вот результат:
image


Вывод:


Писать простые исполняемые приложения на PHP можно (первое такое приложение по описанным выше методам). Они могут выступать бэкенд утилитами для графических обёрток, но не следует этого делать, поскольку PHP отлично проявил себя в web-разработке, а не в десктоп-решениях.

Эксперименты на сырой платформе могут привести к проблемам.

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram-канале

Теги:
Хабы:
Всего голосов 27: ↑23 и ↓4+27
Комментарии22

Публикации

Информация

Сайт
timeweb.cloud
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия
Представитель
Timeweb Cloud