Skip to content

Repository files navigation

PHP GUI

Build native desktop apps with PHP — no Electron, no web server, no compromises.

Latest ReleasePHPLicensePlatform


demo-video.mp4

PHP GUI gives you two ways to build desktop applications from the same PHP codebase:

ModeBest forEngine
Native WidgetsSystem-style UIs — forms, dialogs, toolsTcl/Tk via FFI
WebViewModern UIs with HTML/CSS/JS (like Tauri, but PHP)WebKitGTK / WKWebView / WebView2

Both modes work on Linux, macOS, and Windows with zero system dependencies on Linux — libraries are bundled.


Requirements

Minimum
PHP8.1+
Extensionext-ffi enabled (ffi.enable=true in php.ini)
Composerany recent version

Linux — no extra packages needed (Tcl/Tk is bundled).
macOS — no extra packages needed.
Windows — no extra packages needed.

Enable FFI if not already on:

; php.iniextension=ffi
ffi.enable=true

Installation

composer require developersharif/php-gui

That's it. No system Tcl/Tk install, no native build steps.


Quick Start

Create app.php:

<?phprequire_once__DIR__ . '/vendor/autoload.php';
usePhpGui\Application;
usePhpGui\Widget\Window;
usePhpGui\Widget\Label;
usePhpGui\Widget\Button;
$app = newApplication();
$window = newWindow(['title' => 'Hello PHP GUI', 'width' => 400, 'height' => 250]);
$label = newLabel($window->getId(), ['text' => 'Hello, World!']);
$label->pack(['pady' => 20]);
$button = newButton($window->getId(), [
'text' => 'Click Me',
'command' => fn() => $label->setText('You clicked it!'),
]);
$button->pack();
$app->run();
php app.php

A native window opens immediately. No compilation, no manifest files, no packaging step.


Native Widgets

Native widgets render as real OS controls using Tcl/Tk under the hood. The PHP API is simple and consistent across all platforms.

Available Widgets

WidgetDescriptionDocs
WindowMain application window
TopLevelSecondary window / dialog launcher
LabelStatic or dynamic text display
ButtonClickable button with callback
Input / EntrySingle-line text field
CheckbuttonCheckbox with on/off state
ComboboxDropdown selection
FrameContainer for grouping widgets
MenuMenu bar with submenus and commands
MenubuttonStandalone menu button
CanvasDrawing surface for shapes and images
MessageMulti-line text display
ImageDisplay images inside windows

Layout

Every widget supports three layout managers. Mix them freely within a window.

// Pack — flow layout (simplest)$widget->pack(['side' => 'top', 'pady' => 10, 'fill' => 'x']);
// Grid — row/column table$label->grid(['row' => 0, 'column' => 0, 'sticky' => 'w']);
$input->grid(['row' => 0, 'column' => 1]);
// Place — absolute position$badge->place(['x' => 20, 'y' => 20]);

Styling

Pass Tcl/Tk options directly in the constructor array or update them at runtime:

$button = newButton($window->getId(), [
'text' => 'Save',
'bg' => '#4CAF50',
'fg' => 'white',
'font' => 'Helvetica 14 bold',
'relief' => 'raised',
'padx' => 12,
'pady' => 6,
]);
// Update at runtime$button->setBackground('#2196F3');
$label->setText('Saved!');

Common options: bg, fg, font, relief (flatraisedsunkengrooveridge), padx, pady, width, height, cursor.

Dialogs

TopLevel provides native system dialogs — no extra packages:

// File picker$file = TopLevel::getOpenFile();
// Directory picker$dir = TopLevel::chooseDirectory();
// Color picker$color = TopLevel::chooseColor();
// Message box — returns 'ok', 'cancel', 'yes', 'no'$result = TopLevel::messageBox('Are you sure?', 'yesno');

Menus

$menu = newMenu($window->getId(), ['type' => 'main']);
$fileMenu = $menu->addSubmenu('File');
$fileMenu->addCommand('New', fn() => newFile());
$fileMenu->addCommand('Open', fn() => openFile());
$fileMenu->addSeparator();
$fileMenu->addCommand('Exit', fn() => exit(), ['foreground' => 'red']);
$editMenu = $menu->addSubmenu('Edit');
$editMenu->addCommand('Copy', fn() => copy());
$editMenu->addCommand('Paste', fn() => paste());

Complete Example

<?phprequire_once__DIR__ . '/vendor/autoload.php';
usePhpGui\Application;
usePhpGui\Widget\{Window, Label, Button, Input, Menu, TopLevel};
$app = newApplication();
$window = newWindow(['title' => 'Demo', 'width' => 500, 'height' => 400]);
// Menu bar$menu = newMenu($window->getId(), ['type' => 'main']);
$fileMenu = $menu->addSubmenu('File');
$fileMenu->addCommand('Open', function () use (&$status) {
$file = TopLevel::getOpenFile();
if ($file) $status->setText("Opened: " . basename($file));
});
$fileMenu->addSeparator();
$fileMenu->addCommand('Exit', fn() => exit());
// Input + button$input = newInput($window->getId(), ['text' => 'Type something...']);
$input->pack(['pady' => 10, 'padx' => 20, 'fill' => 'x']);
$status = newLabel($window->getId(), ['text' => 'Ready', 'fg' => '#666']);
$status->pack(['pady' => 5]);
$btn = newButton($window->getId(), [
'text' => 'Submit',
'bg' => '#2196F3',
'fg' => 'white',
'command' => function () use ($input, $status) {
$status->setText('You typed: ' . $input->getValue());
},
]);
$btn->pack(['pady' => 10]);
$app->run();

WebView Mode

WebView lets you build the UI with HTML, CSS, and JavaScript while keeping all your business logic in PHP. Think of it as Tauri for PHP.

<?phprequire_once__DIR__ . '/vendor/autoload.php';
usePhpGui\Application;
usePhpGui\Widget\WebView;
$app = newApplication();
$wv = newWebView(['title' => 'My App', 'width' => 900, 'height' => 600]);
$wv->setHtml('<h1 style="font-family:sans-serif">Hello from PHP + HTML!</h1>');
$wv->onClose(fn() => $app->quit());
$app->addWebView($wv);
$app->run();

PHP ↔ JavaScript Bridge

JS → PHP — call PHP functions from the browser:

// PHP: register a handler$wv->bind('getUser', function (string$reqId, string$args) use ($wv): void {
$id = json_decode($args, true)[0];
$user = getUserFromDatabase($id);
$wv->returnValue($reqId, 0, json_encode($user));
});
// JavaScript: call it like a local functionconstuser=awaitinvoke('getUser',42);console.log(user.name);

PHP → JS — push events to the frontend:

// PHP: emit an event$wv->emit('orderUpdated', ['id' => 99, 'status' => 'shipped']);
// JavaScript: listen for itonPhpEvent('orderUpdated',(order)=>{document.getElementById('status').textContent=order.status;});

Serving a Frontend App

Load a built frontend (React, Vue, Svelte, Vanilla — anything) directly from disk. No HTTP server, no open ports, no firewall prompts:

$wv->serveFromDisk(__DIR__ . '/frontend/dist');
PlatformMechanismURL
Linuxphpgui:// custom URI schemephpgui://app/index.html
WindowsWebView2 virtual hostnamehttps://phpgui.localhost/
macOSloadFileURL:allowingReadAccess:file:///path/to/dist/

Vite Dev + Production in One Line

serveVite() auto-detects whether the dev server is running:

// In dev: hot-reloads via the Vite dev server (HMR works)// In prod: loads dist/ from disk — no server needed$wv->serveVite(__DIR__ . '/frontend/dist');

Recommended vite.config.js for cross-platform builds:

exportdefault{base: './',// required for macOS file:// servingbuild: {outDir: 'dist'},}

Bypass CORS — Transparent Fetch Proxy

Cross-origin API calls fail from phpgui:// / file:// origins. One call routes all fetch() requests through PHP:

$wv->enableFetchProxy(); // add this before serveFromDisk() / serveVite()
// Works identically on all platforms, no changes to your frontend codeconstdata=awaitfetch('https://api.example.com/data').then(r=>r.json());

Full Vite App Example

<?phprequire_once__DIR__ . '/vendor/autoload.php';
usePhpGui\Application;
usePhpGui\Widget\WebView;
$app = newApplication();
$wv = newWebView(['title' => 'My Vite App', 'width' => 1024, 'height' => 768]);
$wv->enableFetchProxy();
$wv->serveVite(__DIR__ . '/frontend/dist');
// Expose a PHP function to JavaScript$wv->bind('readFile', function (string$reqId, string$args) use ($wv): void {
$path = json_decode($args, true)[0];
$content = is_file($path) ? file_get_contents($path) : null;
$wv->returnValue($reqId, 0, json_encode($content));
});
$wv->onClose(fn() => $app->quit());
$app->addWebView($wv);
$app->run();

See the full WebView documentation →


Platform Support

PlatformNative WidgetsWebViewNotes
Linux (x86-64)Tcl/Tk bundled. WebView needs libwebkit2gtk-4.1-dev
Linux (ARM64)Tcl/Tk bundled
macOSNo extra dependencies
WindowsNo extra dependencies

Linux WebView dependency:

sudo apt install libwebkit2gtk-4.1-dev # Debian / Ubuntu
sudo dnf install webkit2gtk4.1-devel # Fedora / RHEL

Documentation

Guide
Getting StartedFFI setup, first app, layout, events
ArchitectureHow the FFI bridge and event loop work
WebViewFull WebView API reference

Widget reference:Window · Button · Label · Input · Entry · Checkbutton · Combobox · Frame · Canvas · Menu · Menubutton · TopLevel · Message


License

MIT

About

Cross-platform GUI development package.

Topics

Resources

Security policy

Stars

11 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages