Offline applications
pad pack puts a whole application - its pages, its PHP, its www/ files - with the PAD engine and PHP compiled to WebAssembly into one directory. Opened from the disk it runs in the browser tab; put on any https host it installs as an application and works offline. There is no PHP server at all, and what the application writes - its SQLite database first - is kept in the browser between visits.
pad pack --install # once: what it packs with, in ~/.cache/pad-wasm
pad pack notes # DATA/wasm/notes/
pad pack notes --serve # served on http://127.0.0.1:8090/ - installable
pad pack notes,ledger,kanban # several, with a launcher
|
PHP |
|---|
|
HTML fragments/offline_applications_1.pad |
|
| Result |
Two ways to run it
| Opened from the disk | Served over https, or on localhost | |
|---|---|---|
| Start |
double-click index.html
|
open the address; the browser offers to install it |
| PHP runs | in the tab | in the service worker, for every window |
| The pages |
in a frame filling the window, the address after the #
|
at their own addresses |
| Offline | always | after the first visit |
Both answer every request as a server would: cookies are kept - sessions, CSRF tokens, flash messages, a login - any method and header of the page's own JavaScript is passed on, an upload arrives in $_FILES, an answer may be binary or a download.
What is kept
Everything under DATA/ - SQLite databases, uploads, JSON files, sessions - is written to the browser's IndexedDB after every request that changed it, and put back when the application starts again. A new pack of the same applications finds that data: an application that keeps its schema in _migrations/ and runs padMigrate() when one is pending brings the database a visitor kept up to date - the ledger application does.
The database
PHP in the browser has SQLite and no MySQL. An application that is SQLite already (padSqlDriver=sqlite, see Sqlite) runs as it is. One written for MySQL on its server can switch for the browser in _offline.json, whose env the runtime passes where padEnv looks first:
{
"name": "Shop", "short": "Shop", "theme": "#0f766e",
"icon": "icon.svg",
"icons": { "192": "icon-192.png", "512": "icon-512.png" },
"env": {
"padSqlDriver": "sqlite",
"padSqlDatabase": "shop/shop.sqlite",
"padSqlSetup": "_install/schema.sql"
}
}
.env files are never packed: they hold a server's passwords, and whoever has the pack can read it.
Import and export
The badge in a corner of every page - or Ctrl+Shift+K - opens the runtime's panel. Its Database tab browses the tables, runs SQL, exports a database as the .sqlite file or as SQL that builds it again, and imports either - SQLite checks what comes in before it replaces anything. Its Storage tab backs up all of DATA/ as one zip and puts such a zip back. An application can offer the same from its own pages, on a server as in the browser: the database file with padUploadSend, its rows as JSON or CSV with $padExpose - the notes application's backup page does both.
The page's own requests
fetch, XMLHttpRequest, a {live} region, an {ajax}, htmx asking for a {fragment}, an EventSource - they all reach PHP in the browser. One PHP answers every request of the application, so a stream that held it would hold the application: padSse answers one round per connection there, and EventSource comes back after the interval by itself, with the last event's id - see Server sent events. A page that should behave differently in the browser asks padWasm():
|
PHP fragments/offline_applications_2.php |
|---|
|
|
HTML fragments/offline_applications_2.pad |
|
| Result |
|
This page runs on the server: a message written here is sent at once. |
Limits
- No MySQL, and no network from PHP: {curl},
padCurl,padPrefetchand a fetch of the server's own pages answer nothing offline. - No sodium and no intl in the build -
padEncryptand the intl formatting fall back or are not there; the sequence subsystem's large tables are left out. - One request at a time: a page that sleeps holds the others.
- Opened from the disk, an application's module scripts cannot import their neighbours and
document.cookiedoes not show PHP's cookies. Served over https none of this applies.
The applications
Three applications are made to show it, each running on this server as well:
- notes - Markdown notes in SQLite: search as you type, live pins, attachments, a JSON export and import, the database as a download.
- ledger - a household budget built by its migrations: charts, budgets, CSV in and out, a quick entry posted as JSON.
- kanban - a task board: drag and drop posted as JSON, fragments, every open window kept in step by server-sent events.
pad pack notes,ledger,kanban packs them together, with a launcher. How it is built - the runtime, the service worker, the panel - is in wasm/README.md.