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 |
| 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.
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.
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.
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.
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. |
padCurl, padPrefetch and a
fetch of the server's own pages answer nothing offline.padEncrypt and the intl formatting fall back or
are not there; the sequence subsystem's large tables are left out.document.cookie does not show PHP's cookies. Served over https none of this
applies.Three applications are made to show it, each running on this server as well:
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.