Building Desktop Apps with PHP: The Guide

Abdulbasit Rubeya
4 min readJan 13, 2024

--

PHP, renowned for its versatility in web development, has long been recognized as a powerful server-side programming language. Often associated with backend operations, PHP developers find themselves confined within the realms of web applications. However, what if the boundaries of PHP’s capabilities could be stretched beyond the web, allowing developers to craft robust desktop applications for diverse platforms such as Windows, Linux, or OSX?

Theoretically, a couple of methods exist to tread this uncharted territory. One approach involves the utilization of Quercus or jPHP in tandem with JavaFX, albeit with the undeniable Java footprint. Another avenue is the NativePHP framework, promising the fusion of PHP and Electron for desktop app development. However, NativePHP’s inherent instability and the caveat of being exclusive to Laravel have left developers hesitating at the threshold.

In this comprehensive guide, we embark on a journey to demystify the process of building desktop applications with PHP and Electron. While pondering the question of whether you should venture into this amalgamation of technologies, the intriguing possibilities unfold. Is it time to break free from the constraints of conventional PHP development and embrace the expansive realm of desktop applications? Let’s unravel the answers together. 😏

Before you start, make sure you have the following:

nb: this guide is mostly covered for users using Windows, but if you are an advanced user, it should not be hard to replicate the same procedures on another OS

  1. Node.js LTS version installed on your machine.

2. Install Electron Builder into your system globally (recommended)

npm install -g electron-packager

3. Create a new file package.json

{
"name": "your-project-name",
"version": "1.0.0",
"description": "Your PHP Desktop App",
"main": "main.js",
"scripts": {
"start": "electron .",
"pack": "electron-packager . phpulse --out=dist --overwrite --icon=assets/build/icon",
},
"repository": {
"type": "git",
"url": "your-git-repository-url"
},
"keywords": ["php", "electron", "desktop", "nodejs"],
"devDependencies": {
"electron": "^28.1.3"
}
}

4. Install dependencies by executing “npm install”

In general, you should have a folder structure like this one

ProjectFile
|-- app
| |-- public
| | |--index.php
|-- php
|-- node_modules
|-- main.js
|-- package.json

The app directory is where your PHP code shall reside, and where public folder is the default entry point of your PHP application

php folder is where your PHP binary files shall reside, this is because it is very unlikely that the end user would have PHP already installed in their system

The last step to complete your app engine is to modify the main.js, which is the entry point and main running point of your desktop app

First of all, we’ll need to code import necessary modules from Electron (app and BrowserWindow), the spawn method from the 'child_process' module, and the path module.

const { app, BrowserWindow } = require('electron');
const { spawn } = require('child_process');
const path = require('path');

Add a constant that would store our configurations, The appConfig object will contain configuration settings for the PHP application. Specify the port on which the PHP server should run, the path to the PHP executable, the dev console status (whether to open the developer console or not) and the PHP application entry point

const appConfig = {
"port": 48183,
"dev_console": true,
"php_path": "php/php.exe", // relative to app root
"entry_point": "app/public" // relative to app root
};

Up next is to create the logic to run your PHP application, and add a new functionstartPHPServer function that will usespawn method to start a PHP server using the specified PHP executable and configuration. we’ll also set up event listeners for the server's output and closure.

const startPHPServer = () => {
const phpScriptPath = path.join(__dirname, appConfig.entry_point);
phpServerProcess = spawn(appConfig.php_path, ['-S', `localhost:${appConfig.port}`, '-t', phpScriptPath);

// Event listeners for server output and closure
// ...
};

Now let’s open a browser window and initialize our app

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
});

// Load the PHP server URL
win.loadURL(`http://localhost:${appConfig.port}`);

// Open the DevTools.
if(appConfig.dev_console){
win.webContents.openDevTools();
}
};

// initialize the app
app.whenReady().then(() => {
startPHPServer();
createWindow();

// Event listener for window activation
// ...
});

So basically that’s what you need to implement to package your php app as a desktop application, In the end with basic event listeners, the main.js file should look somehow similar to

const { app, BrowserWindow } = require('electron');
const { spawn } = require('child_process');
const path = require('path');

// php application config
const appConfig = {
"port": 48183,
"dev_console": true,
"php_path": "php/php.exe", // relative to app root
"entry_point": "app/public/index.php" // relative to app root
};

let phpServerProcess;

const startPHPServer = () => {
const phpScriptPath = path.join(__dirname, appConfig.entry_point);
phpServerProcess = spawn(appConfig.php_path, ['-S', `localhost:${appConfig.port}`, '-t', phpScriptPath]);

phpServerProcess.stdout.on('data', (data) => {
console.log(`PHP Server: ${data}`);
});

phpServerProcess.stderr.on('data', (data) => {
console.log(`PHP Server Log: ${data}`);
});

phpServerProcess.on('close', (code) => {
console.log(`PHP Server exited with code ${code}`);
});
};

const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
});

// Load the PHP server URL
win.loadURL(`http://localhost:${appConfig.port}`);

// Open the DevTools.
if(appConfig.dev_console){
win.webContents.openDevTools();
}
};

app.whenReady().then(() => {
startPHPServer();
createWindow();

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

app.on('before-quit', () => {
if (phpServerProcess) {
phpServerProcess.kill();
}
});

Run npm start to live load your app, and npm run pack to pack and build your application

Congratulations, you’ve built your first desktop app with PHP but what we’ve covered here is just a glimpse of the vast possibilities you can explore when packaging PHP applications as desktop apps with Electron. There are numerous advanced concepts and features you can implement to enhance your desktop application further.

As you continue with your project, consider exploring additional concepts such as: building a splash screen, adding Platform-Specific Commands, and much more.

PS: consider visiting or contributing to PHPulse, a new micro framework to package your PHP app at https://github.com/ibnsultan/PHPulse

--

--

Abdulbasit Rubeya
Abdulbasit Rubeya

Responses (1)