本文目录导读:

我来详细解释如何在 Electron 中使用 PHP,有几种不同的方案:
使用 PHP 内置服务器(最简单)
项目结构
my-electron-app/
├── main.js
├── package.json
├── php/
│ └── server.php
└── public/
├── index.html
└── api/
└── test.php
主进程配置 (main.js)
const { app, BrowserWindow, shell } = require('electron');
const { spawn } = require('child_process');
const path = require('path');
const fs = require('fs');
let phpServer = null;
let mainWindow = null;
// 查找 PHP 可执行文件
function findPHP() {
const candidates = process.platform === 'win32'
? ['C:/php/php.exe', 'C:/wamp64/bin/php/php7.4.9/php.exe']
: ['/usr/bin/php', '/usr/local/bin/php', 'php'];
for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
return 'php'; // 最后尝试用系统 PATH 里的
}
function startPHPServer() {
const phpPath = findPHP();
const phpScriptPath = path.join(__dirname, 'php', 'server.php');
phpServer = spawn(phpPath, ['-S', '127.0.0.1:8000'], {
cwd: path.join(__dirname, 'public'),
stdio: ['ignore', 'pipe', 'pipe']
});
phpServer.stdout.on('data', (data) => {
console.log(`PHP Server: ${data}`);
});
phpServer.stderr.on('data', (data) => {
console.error(`PHP Server Error: ${data}`);
});
}
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
});
// 启动 PHP 服务器
startPHPServer();
// 加载 PHP 服务器上的页面
mainWindow.loadURL('http://127.0.0.1:8000');
// 处理外部链接
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
// 关闭 PHP 服务器
if (phpServer) {
phpServer.kill();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
PHP 启动脚本 (php/server.php)
<?php
// 简单的路由
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
switch ($uri) {
case '/':
echo file_get_contents(__DIR__ . '/../public/index.html');
break;
case '/api/data':
header('Content-Type: application/json');
echo json_encode(['message' => 'Hello from PHP!']);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Not found']);
}
?>
前端页面 (public/index.html)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">Electron + PHP</title>
</head>
<body>
<h1>Electron + PHP</h1>
<button id="fetchData">获取数据</button>
<div id="result"></div>
<script>
document.getElementById('fetchData').addEventListener('click', async () => {
try {
const response = await fetch('/api/data');
const data = await response.json();
document.getElementById('result').textContent = JSON.stringify(data);
} catch (error) {
console.error('Error:', error);
}
});
</script>
</body>
</html>
使用 PHP Web 服务器库(更专业)
使用 ReactPHP 或 Workerman
// main.js - 使用子进程
const { spawn } = require('child_process');
function startPHPBackend() {
const phpScript = `
<?php
require __DIR__ . '/vendor/autoload.php';
use Workerman\\Worker;
$worker = new Worker('http://127.0.0.1:8000');
$worker->count = 4;
$worker->onMessage = function($connection, $request) {
$connection->send('Hello from PHP with Workerman!');
};
Worker::runAll();
`;
// 写入临时文件
fs.writeFileSync('php_backend.php', phpScript);
const backend = spawn('php', ['php_backend.php']);
return backend;
}
使用 PHP 编译为原生模块
使用 phpmain 或 php-wasm
// 使用 PHP WebAssembly (php-wasm)
const { loadPHPRuntime } = require('php-wasm');
async function measurePerformance() {
const php = await loadPHPRuntime();
const output = php.run(`<?php echo "Hello from WASM PHP!";`);
console.log(output);
}
生产环境最佳实践
打包时内置 PHP(使用 electron-builder)
在 package.json 中:
{
"build": {
"extraResources": [
{
"from": "php/",
"to": "php/",
"filter": ["**/*"]
}
]
}
}
完整的主进程代码
const isDev = !app.isPackaged;
function getPHPPath() {
if (isDev) {
return 'php'; // 开发环境
} else {
// 生产环境路径
return path.join(process.resourcesPath, 'php', 'php.exe');
}
}
function initPHP() {
const phpPath = getPHPPath();
// 每次启动都使用新的端口,避免冲突
const port = 8000 + Math.floor(Math.random() * 1000);
phpServer = spawn(phpPath, ['-S', `127.0.0.1:${port}`], {
cwd: isDev ? path.join(__dirname, 'public') : path.join(process.resourcesPath, 'public'),
env: {
...process.env,
PORT: port
}
});
return `http://127.0.0.1:${port}`;
}
安全考虑
// preload.js
const { contextBridge } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
getPlatform: () => process.platform,
// 只暴露必要的 API
phpEndpoint: 'http://127.0.0.1:8000'
});
使用建议
- 开发时:直接使用本地 PHP 服务器
- 生产时:将 PHP 编译为二进制文件,或使用 Docker 容器
- 性能要求高:考虑使用 PHP 编译为本地二进制(如 RoadRunner)
- 简单需求:如果只是字符串处理,可以用 php-wasm
常见问题解决
PHP 不在 PATH 中
// 检测并添加 PHP 路径
function addPHPPath() {
const phpPaths = {
win32: 'C:\\xampp\\php',
darwin: '/Applications/MAMP/bin/php/php8.0.0/bin',
linux: '/usr/bin/php'
};
process.env.PATH += path.delimiter + phpPaths[process.platform] || '';
}
端口冲突
const net = require('net');
function getAvailablePort(start = 8000) {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.listen(start, () => {
server.close(() => resolve(start));
});
server.on('error', () => resolve(getAvailablePort(start + 1)));
});
}
这种方案完全可行,特别适合需要 PHP 后端的 Electron 应用,生产环境建议将 PHP 打包进应用,减少外部依赖。