1) Problem: Apps im Unterordner
In vielen Projekten werden Pfade/Links „hart“ geschrieben (z. B. /assets/app.css oder
Includes wie require '/var/www/...'). Verschiebt man die App in einen
Unterordner (z. B. von / nach /test), brechen:
- Links & Assets (
/csszeigt plötzlich auf die falsche Stelle) - Includes/Requires (Dateipfade sind relativ zum falschen Verzeichnis)
- Weiterleitungen (
Location: /loginlandet außerhalb der App)
Hosting-Varianten (Plesk, Shared Hosting) liefern unterschiedliche $_SERVER-Werte.
Darauf darf man sich nicht blind verlassen.
2) Ziele der Lösung
- Einmalige Ermittlung von **FS-Basen** und **URL-Basen** unabhängig vom Unterordner
- Kurze, sichere Helper für Pfade/Links: keine harten
/-Präfixe mehr - Optional konfigurierbar (z. B. Subpfad erzwingen oder anderer Asset-Ordner)
- Mehrere Instanzen parallel betreibbar (z. B.
/docs/doc1,/docs/doc2…)
3) Architektur & Konzepte
3.1 Bootstrap-Konstanten (aus _boot.php)
APP_ROOT– Projektwurzel (eine Ebene über/public)PUBLIC_DIR– absoluter FS-Pfad zu/publicAPP_FS_BASE– FS-Basis der aktuellen App (/public/app)APP_URL_BASE– URL-Basis der aktuellen App (z. B./test)
3.2 Helper-Funktionen
app_path('…')→ absoluter Dateipfad relativ zuAPP_FS_BASEapp_url('…')→ URL relativ zuAPP_URL_BASEapp_asset_path('…')/app_asset_url('…')→ Pfad/URL für Assets der Appapp_asset_url_v('…')→ wie oben + Cache-Busting mitfilemtime()h($s)→ HTML-Escaping, sicher für Ausgaben
Alle Includes, Links, Asset-Einbindungen nutzen diese Helper – damit ist die App subdir-sicher.
4) Installation (Schritt für Schritt)
-
Lege die App unter
/public/<app>an
Beispiel:/public/testmit Dateien:/public/test/ _boot.php index.php config.app.php ← optional includes/theme.php ← optional assets/ test.css js/test.js images/test.webp -
Nutze die gelieferte
_boot.php
Sie setzt die Konstanten und definiert die Helper (siehe API-Anhang unten). -
(Optional)
config.app.phpanlegen
Nur wenn du etwas übersteuern willst (Subpfad, Asset-Ordner):<?php // config.app.php (optional) define('APP_URL_BASE', '/test'); // festen Subpfad erzwingen define('APP_ASSETS_DIR', 'assets'); // alternativ z. B. 'static' -
In Templates nur noch Helper nutzen
Statt/assets/app.cssbitte:<link rel="stylesheet" href="<?= app_asset_url_v('app.css') ?>"> <img src="<?= app_asset_url('images/logo.webp') ?>" alt="Logo"> <script src="<?= app_asset_url_v('js/app.js') ?>" defer></script> -
Includes immer relativ zu
APP_FS_BASErequire app_path('includes/something.php');
5) Optionale Konfiguration (config.app.php)
APP_URL_BASE– erzwingt den URL-Subpfad (z. B.'/test'oder'/')APP_ASSETS_DIR– Name des Asset-Ordners unterhalb der App (Standard:assets)APP_FS_BASE– (selten nötig) Fixierung der App-FS-Basis
Wenn der Hoster/Proxy „komische“ $_SERVER-Werte liefert, kannst du mit
APP_URL_BASE hart den Subpfad festlegen – die Helper übernehmen den Rest.
6) Assets & Cache-Busting
Für Assets gibt es drei zentrale Helper:
app_asset_path('test.css')→ absoluter Dateipfad, gut füris_file()app_asset_url('test.css')→ URL relativ zuAPP_URL_BASEapp_asset_url_v('test.css')→ wie oben, plus?v=<mtime>für Cache-Busting
<link rel="stylesheet" href="<?= app_asset_url_v('test.css') ?>">
<script src="<?= app_asset_url_v('js/app.js') ?>" defer></script>
Wenn die Asset-Checks „nein“ anzeigen, liegen die Dateien meistens nicht unter
{APP_FS_BASE}/<ASSET_DIR>/…. Pfad prüfen!
7) Optionales Theme
Wenn vorhanden, wird includes/theme.php geladen. Es stellt eine kleine API bereit:
<?php
declare(strict_types=1);
/**
* Theme-API (optional)
* (c) 2025 Johannes Teitge · johannes@teitge.de · teitge.de
* Lizenz: GPL-3.0-or-later
*
* Liefert Branding-Farben, die im Template zu CSS-Variablen werden können.
*/
function theme_load(): array {
return [
'brand' => '#003A77',
'accent' => '#E2A900',
'ts' => date('c'),
];
}
Im Template kannst du die Variablen direkt nutzen:
<?php $t = function_exists('theme_load') ? theme_load() : [];
$brand = $t['brand'] ?? '#003A77';
$accent = $t['accent'] ?? '#E2A900';
?>
<style>
:root{ --brand: <?= $brand ?>; --accent: <?= $accent ?>; }
a{ color: var(--accent) } a:hover{ color: var(--brand) }
</style>
8) Mehrere Instanzen (z. B. /docs/doc1, /docs/doc2)
Lege einfach mehrere Unterordner unter /public/docs an:
/public/docs/doc1/_boot.php, index.php, assets/…
/public/docs/doc2/_boot.php, index.php, assets/…
/public/docs/doc3/_boot.php, index.php, assets/…
Jede Instanz ermittelt ihre eigene APP_FS_BASE und APP_URL_BASE. Die Helper arbeiten
in jedem Ordner gleich – keine Codeänderungen nötig.
9) Migration & Checkliste
- Alle harten Links
/…durchapp_url()bzw.app_asset_url()ersetzen - Alle Includes auf
app_path()umstellen - JS/CSS mit
app_asset_url_v()laden (Cache-Busting) - (Optional)
config.app.phpergänzen (Subpfad/Asset-Ordner) - Test mit der mitgelieferten Path-Tester-Seite (zeigt Konstanten & Asset-Checks)
10) Troubleshooting
Assets werden 404 / „Existiert? = nein“
- Stimmen die Pfade unter
{APP_FS_BASE}/assets/…(oder deinAPP_ASSETS_DIR)? - Im Template wirklich
app_asset_url()oderapp_asset_url_v()genutzt?
Subpfad wird falsch erkannt
Erzwinge den Pfad in config.app.php:
define('APP_URL_BASE', '/test');
Theme wird nicht geladen
includes/theme.phpexistiert nicht → unkritischtheme_load()fehlt → einfach hinzufügen (siehe Abschnitt „Theme“)
Warum nicht das HTML-<base>-Tag?
Es beeinflusst alle relativen URLs (auch externe), kann Form-Targets & Skript-Ladepfade unerwartet ändern. Der Helper-Ansatz ist lokaler und kontrollierter.
11) FAQ
Funktioniert das hinter einem Reverse Proxy?
Ja. Falls der Proxy den Pfad nicht sauber durchreicht, setze APP_URL_BASE in config.app.php.
Kann ich globales theme.php teilen?
Ja. Du kannst zuerst APP_ROOT/includes/theme.php prüfen und dann app-lokal – so lässt sich Branding zentralisieren.
Ist CLI-Betrieb möglich?
Im CLI fehlen einige $_SERVER-Werte. Nutze in Skripten define('APP_URL_BASE','/test') oder arbeite nur mit FS-Helpern.
12) API-Anhang: Konstanten & Helper
Konstanten
APP_ROOT– Projektwurzel (eine Ebene über/public)PUBLIC_DIR– absoluter Pfad zu/publicAPP_FS_BASE– absoluter Pfad der App (/public/<app>)APP_URL_BASE– URL-Basis der App (z. B./test)APP_ASSETS_DIR– Name des Asset-Ordners (Default:assets)
Helper (Signaturen)
h(string $s): string
public_path(string $rel=''): string
app_path(string $rel=''): string
app_url(string $rel=''): string
asset_path(string $rel): string // Alias auf app_path('assets/…') – historisch
asset_url(string $rel): string // Alias auf app_url('assets/…') – historisch
asset_url_v(string $rel): string // wie oben + ?v=mtime
app_asset_path(string $rel): string // eindeutig App-lokal
app_asset_url(string $rel): string
app_asset_url_v(string $rel): string
Beispiele
// Includes
require app_path('includes/common.php');
// Links
<a href="<?= app_url('admin/') ?>">Admin</a>
// Assets
<link rel="stylesheet" href="<?= app_asset_url_v('main.css') ?>">
<img src="<?= app_asset_url('images/logo.webp') ?>" alt="Logo">
<script src="<?= app_asset_url_v('js/app.js') ?>" defer></script>
Lizenz & Urheber
© 2025 Johannes Teitge · johannes@teitge.de · teitge.de
Dieses Dokument steht unter der GNU GPL v3 oder neuer (GPL-3.0-or-later).