NativePHP for Desktop Documentation
repository·main·Indexed 16 days ago
https://github.com/nativephp/desktopA framework for building native desktop applications using PHP and Electron. It includes tools for managing application lifecycles, process cleanup, deep linking, and auto-updates. The framework provides a specialized Menubar class and Positioner library for creating system tray applications with precise window placement and lifecycle event management.
What's inside NativePHP for Desktop
- NativePHP for Desktop allows developers to write native desktop applications using PHP. It provides a bridge to build desktop software by leveraging existing PHP ecosystems.
Understand the Electron Positioner Library
mainThe Electron Positioner Library is a vendored dependency used by NativePHP'smenubarfunctionality. It is a copy of the originalelectron-positionerlibrary, included directly within the repository to avoid external dependencies on unmaintained packages inpackage.json.How NativePHP manages application lifecycle and processes
mainNativePHP manages a collection of child processes to ensure the PHP environment and its associated services (like the scheduler) are correctly cleaned up when the Electron application exits.
Process Cleanup
When the Electron app receives a
before-quitevent, NativePHP performs a coordinated shutdown:- Kill Framework Processes: It first stops the framework's own processes (like the PHP server) to prevent new requests from spawning fresh child processes during shutdown.
- Stop App Processes: It sends a
SIGTERMto the application's child processes, allowing them to perform their own cleanup (flushing data, persisting state). - Graceful Wait: It waits up to 12 seconds for processes to exit naturally before forcing a shutdown.
Scheduler Lifecycle
The scheduler runs tasks at minute intervals. It is sensitive to system power states:
- Suspend: When the system suspends, the scheduler is stopped.
- Resume: When the system resumes, the scheduler is restarted.
Security
To secure communication between the Electron frontend and the PHP backend, NativePHP injects an
X-NativePHP-Secretheader into all requests sent to the local PHP server (http://127.0.0.1:${state.phpPort}/*).Configure Deep Linking and Auto-Updates via NativePHP Config
mainNativePHP reads configuration from the underlying PHP environment to configure Electron-specific features. The following settings are extracted from the NativePHP configuration:
app_id: Used to set the ElectronappUserModelId.deeplink_scheme: If provided, NativePHP registers this protocol (e.g.,myapp://) with the operating system. It also handlesopen-url(macOS) andsecond-instance(Windows/Linux) events to notify the Laravel application of the incoming URL.updater:enabled: Boolean to enable/disable automatic updates.default: The name of the default provider to use.providers: A map of provider configurations. Each provider can have apublic_urlused to set theautoUpdaterfeed URL.
When a deep link is triggered, NativePHP notifies Laravel by dispatching the
\Native\Desktop\Events\App\OpenedFromURLevent with the URL as the payload.Configure NativePHP updater providers
mainNativePHP uses a configuration-driven approach to manage application updates via different drivers. Providers are defined in your application configuration under the
nativephp.updater.providerskey. Each provider must specify adrivertype.Supported drivers identified in the
UpdaterManagerinclude:github(viaGitHubProvider)s3(viaS3Provider)spaces(viaSpacesProvider)
You can also define a default updater driver using the
nativephp.updater.defaultconfiguration key.Configure Menubar application options
mainWhen creating a menubar application, you provide an
Optionsobject to define the behavior, appearance, and window properties of the menubar. Key configurations include the window dimensions, the icon used in the system tray, the URL to load, and the positioning of the window relative to the tray.Window and URL Configuration
browserWindow: An object containingBrowserWindowConstructorOptions(e.g.,width,height).dir: The directory containing your application source.index: The URL to load. Defaults tofile://+dir+index.html. Set tofalseto prevent automatic loading.loadUrlOptions: Options passed directly to Electron'sbrowserWindow.loadURL().
Appearance and Tray
icon: A path to a PNG icon or anElectron.NativeImage. For retina support, provide a 2x sized image with@2xappended to the filename (e.g.,icon@2x.png).tooltip: The text displayed when hovering over the menubar tray icon.tray: An optionalElectron.Trayinstance. If provided, theiconoption is ignored.showDockIcon: (macOS only) Iffalse, hides the application dock icon.
Behavior and Positioning
activateWithApp: Iftrue(default), the menubar opens when the app is activated viaapp.on('activate').preloadWindow: Iftrue, creates theBrowserWindowinstance before use to speed up loading at the cost of higher initial resource usage.showOnAllWorkspaces: (macOS) Makes the window available on all OS X workspaces.showOnRightClick: Iftrue, the window shows on a 'right-click' event instead of a regular click.windowPosition: Defines where the window appears relative to the tray or screen corners.
const options = { height: 640, width: 480 }; const mb = new Menubar({ browserWindow: options });Troubleshoot bifrost:init errors
mainIf
bifrost:initfails, the command provides specific guidance based on the error encountered:- Authentication Failure: If you are not authenticated, run:
php artisan bifrost:login - No Teams Found (403 Error): You must create a team before you can manage projects. Visit the onboarding URL provided in the terminal output.
- Incomplete Team Setup (422 Error): Your team setup might be incomplete or requires a subscription. Visit your dashboard to complete the setup.
- General API Errors: If the request fails for other reasons, the command will suggest visiting your dashboard to resolve the issue.
- Authentication Failure: If you are not authenticated, run:
Control the Emoji Panel
mainThe
Appclass provides methods to interact with the system emoji panel.isEmojiPanelSupported(): Returns aboolindicating if the emoji panel can be displayed on the current platform.showEmojiPanel(): Triggers the display of the emoji panel.
if ($app->isEmojiPanelSupported()) { $app->showEmojiPanel(); }Manage the scheduler with runScheduler() and killScheduler()
mainThe scheduler manages background tasks.
runScheduler(): Starts the scheduler process. If a scheduler is already running, it will be killed before a new one starts to prevent multiple instances.killScheduler(): Gracefully terminates the currently running scheduler process if it exists and has not already been killed.
import { runScheduler, killScheduler } from './server/index.js'; // Start the scheduler runScheduler(); // Stop the scheduler killScheduler();Show and hide the Menubar window
mainYou can manually control the visibility of the menubar window using the following methods:
showWindow(trayPos?): Asynchronously shows the window. IftrayPos(anElectron.Rectangle) is provided, the window will attempt to position itself relative to those bounds. If not provided, it uses cached bounds or the current tray bounds.hideWindow(): Hides the current window and clears any pending blur timeouts.
// Manually show the window await menubar.showWindow(); // Manually hide the window menubar.hideWindow();Start the PHP application with startPhpApp()
mainUse
startPhpApp()to launch the PHP application process. This function initializes the app using the current state's random secret, the Electron API port, and PHP INI settings. It also updates the global state with the assigned PHP port and appends necessary cookies. It returns theChildProcessinstance of the running PHP application.import { startPhpApp } from './server/index.js'; const phpProcess = await startPhpApp(); // phpProcess is a ChildProcess instanceStop, Restart, or Message a child process
mainOnce a process is running, you can control it using its
alias.- Stop a process: Use
stop(?string $alias = null). If no alias is provided, it stops the process instance the current object represents. - Restart a process: Use
restart(?string $alias = null). Returns a newChildProcessinstance if successful, ornullif it fails. - Send a message: Use
message(string $message, ?string $alias = null). Sends a string message to the process via its alias.
// Stop a specific process $childProcess->stop('my-alias'); // Restart a specific process $newProcess = $childProcess->restart('my-alias'); // Send a message to a process $childProcess->message('RELOAD_CONFIG', 'my-alias');- Stop a process: Use