Erste Schritte

Eine Website auf Phpanta folgt einem festen Aufbau, bindet das Framework als Git-Submodul ein und sagt ihm über eine einzige Klasse, was es über sie wissen muss.

Der Aufbau

site/ ├── autoload.php requires phpanta/autoload.php, maps the site's namespace, boots the app ├── composer.json psr-4: the site's namespace → src/<Ns>/, and Phpanta\ → phpanta/src/ ├── public/index.php a last-resort handler, then <Site>::current()->run() ├── src/<Ns>/ the app class, controllers, views, models, words ├── data/ what the site reads at runtime, outside the webroot ├── assets/ts/ the site's TypeScript; assets/ts/phpanta links to the framework's ├── assets/css/ the site's stylesheet, as one @import manifest └── phpanta/ the framework, as a git submodule

Einbinden

git submodule add ../phpanta.git phpanta ln -s ../../phpanta/assets/ts assets/ts/phpanta git config submodule.recurse true git config push.recurseSubmodules on-demand

Die URL ist relativ, also findet jedes Remote, auf das die Website gepusht wird, seine eigene Kopie des Frameworks direkt daneben. Das Framework an Ort und Stelle zu bearbeiten ist gewollt: Eine Änderung ist ein Commit in phpanta/, dann git add phpanta und ein Commit in der Website.

Die App

Eine Website ist eine Unterklasse von Phpanta\App, einmal pro Prozess von ihrem Autoloader gebootet. Sie schuldet dem Framework eine Handvoll Antworten:

final class Site extends App { public function name(): string { return 'Acme'; } public function above(): Directory { return new Directory(dirname(__DIR__, 2)); } public function languages(): Languages { return new Languages(Language::English); } public function shell(): Shell { return new Layout(); } public function vocabulary(): Vocabulary { return Vocabulary::standard(); } public function buildId(): string { return AssetManifest::SCRIPT; } public function routes(): Collection { return new Collection(Route::class)->with( new Route(AcmePath::Home, fn() => new HomeController()), ); } public function notFound(Request $request): Response { return new ViewResponse(new NotFoundView(), HttpStatusCode::NotFound); } protected function ownDataFiles(): Collection { return new Collection(DataFileName::class); } }

Alles andere — wo data/ liegt, welches Verzeichnis der Webroot ist, wo die Update-Seriennummer liegt, das Fehlerlog, die Route zur API — leitet das Framework daraus ab, und diese Ableitungen sind final.

Eine Seite

Eine Route verbindet einen Path-Case mit einem Controller, und der Controller liefert eine Antwort. Eine ViewResponse rendert ihre View in der Shell der App — oder, für eine Anfrage von Navigation, als Fragment, angeführt von ihrem Titel.

final class HomeView extends View { public function pageTitle(): Translatable { return self::title(); } public function content(): Node { return new Element(HtmlTag::P)->containing(AcmeText::Hello); } }

Bauen

tsc # assets/ts/ → public/assets/js/ node phpanta/tools/build-css.mjs # assets/css/main.css → public/assets/css/style.css node phpanta/tools/build-assets.mjs # the stamped AssetManifest the shell reads node phpanta/tools/build-prod.mjs # build/dist/ — bundled, minified, no maps php -S localhost:8080 -t public phpanta/tools/dev-router.php

Die Build-Werkzeuge finden das Projekt, indem sie von dort, wo sie gestartet werden, zum nächsten composer.json hinaufsteigen, und lesen den Namespace der Website aus dessen psr-4.

Deployen

Ein PHP-Host bekommt schlichte Dateien: phpanta/src/ und phpanta/autoload.php, src/ und autoload.php der Website und den Prod-Webroot — auf beliebigem Weg kopiert, oder als eine einzige signierte Anfrage durch den PushUpdate-Befehl des Frameworks geschickt. Ein statischer Host bekommt einen Export:

php phpanta/tools/export.php --out build/pages --base /phpanta/

Jede Seite wird vom Controller ihrer eigenen Route gerendert — noch einmal in jeder Sprache, unter einer Adresse, die sie nennt —, jede Adresse wird unter den Basispfad verschoben, und der Export scheitert an jedem Link auf eine Seite, die er nicht geschrieben hat, oder auf einen Anker, den diese Seite nicht hat. Diese Website ist die Ausgabe dieses Befehls.