Building Desktop Apps with PHP: The Guide
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
- 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