folderweb/docs/04-development/01-architecture.md
Ruben b03511f99b Update AGENT.md and add architecture documentation
Update AGENT.md to reflect current project structure and philosophy

Add comprehensive architecture documentation covering:
- Directory layout
- Stable contracts
- Request flow
- Module dependencies
- Page vs list detection
- Deployment models
- Demo fallback

Remove outdated plugin system documentation
Add new content system documentation
Add configuration documentation
Add context API documentation
Add hooks and plugins documentation
Add templates documentation
Add rendering documentation
Add development environment documentation
2026-02-05 23:30:44 +01:00

137 lines
6.9 KiB
Markdown

# Architecture
## Directory Layout
```
app/ # Framework core — stable API surface
router.php # Entry point: all requests route here
constants.php # CONTENT_EXTENSIONS, COVER_IMAGE_EXTENSIONS
hooks.php # Hook enum + Hooks class
context.php # Context + Templates classes
config.php # createContext(): config merge + bootstrap
helpers.php # Utility functions (template resolution, extraction)
content.php # Content discovery, slug resolution, navigation
rendering.php # Markdown/HTML/PHP rendering, template wrapping
cache.php # Markdown render cache (/tmp/folderweb_cache)
plugins.php # PluginManager class
static.php # /app/* asset serving with traversal protection
vendor/ # Parsedown + ParsedownExtra (Markdown→HTML)
plugins/global/ # Built-in plugins (languages.php)
default/ # Demo/fallback theme (NOT for production use)
config.ini # Default configuration
templates/ # base.php, page.php, list.php, list-compact.php, list-grid.php
styles/ # Default stylesheet
languages/ # en.ini, no.ini
content/ # Demo content shown when custom/ has no content
custom/ # User layer — all site-specific work goes here
config.ini # Config overrides (merged over app/default/config.ini)
templates/ # Override any template by matching filename
styles/ # Stylesheets served via /app/styles/*
fonts/ # Fonts served via /app/fonts/*
assets/ # Static files served at document root (favicon, robots.txt)
languages/ # Translation overrides/additions (*.ini)
plugins/global/ # Custom global plugins
plugins/page/ # Custom page plugins (reserved, not yet active)
content/ # Website content (= document root in production)
devel/ # Dev environment (Containerfile, compose, Apache, perf tools)
docs/ # Documentation (01-03 human-facing, 04 machine-facing)
```
## Stable Contracts
**`app/` is the framework.** When developing `custom/` sites, never modify `app/`. When developing the framework itself, preserve these contracts:
| Contract | Guaranteed Behavior |
|---|---|
| Override chain | `custom/*` always takes priority over `app/default/*` for templates, styles, languages, config |
| Template names | `base.php`, `page.php`, `list.php` are the three core template names |
| Hook enum | `Hook::CONTEXT_READY`, `Hook::PROCESS_CONTENT`, `Hook::TEMPLATE_VARS` — signatures documented in `05-hooks-plugins.md` |
| Context API | `$ctx->set()`, `$ctx->get()`, `$ctx->has()` — stable key/value store |
| Content extensions | `md`, `html`, `php` — defined in `CONTENT_EXTENSIONS` |
| Config format | INI with sections, merged via `array_replace_recursive` |
| Metadata format | INI file named `metadata.ini` in content directories |
| URL structure | Folder path = URL path, with slug overrides via metadata |
| Plugin locations | `app/plugins/{scope}/` and `custom/plugins/{scope}/` |
| Asset routes | `/app/styles/*``custom/styles/`, `/app/fonts/*``custom/fonts/`, `/app/default-styles/*``app/default/styles/` |
| Trailing slash | Pages and lists enforce trailing slash via 301 redirect |
## Request Flow
```
Browser request
├─ Apache rewrite: all non-/app/ requests → app/router.php
router.php
├─ 1. Load modules (constants, hooks, context, helpers, plugins, config, content, rendering)
├─ 2. createContext()
│ ├─ Parse + merge config (default ← custom)
│ ├─ loadGlobalPlugins() → fires Hook::CONTEXT_READY
│ ├─ Determine contentDir (custom content or demo fallback)
│ ├─ Parse REQUEST_URI → requestPath
│ └─ Resolve template paths (custom fallback to default)
├─ 3. Check custom/assets/{path} → serve static file + exit
├─ 4. Check content/{path} for static asset (css/img/pdf/font) → serve + exit
├─ 5. Empty path? → frontpage: findAllContentFiles + renderMultipleFiles
└─ 6. parseRequestPath() → {type, path}
├─ "page": trailing slash redirect → findAllContentFiles → renderMultipleFiles
│ (fires Hook::PROCESS_CONTENT for file filtering)
│ (fires Hook::TEMPLATE_VARS before template render)
│ Template chain: content → page.php → base.php
├─ "list": trailing slash redirect → build items array from subdirectories
│ ├─ Check hide_list metadata → treat as page if true
│ ├─ Select list template from metadata page_template
│ ├─ For each subdir: loadMetadata, extractTitle, extractDateFromFolder, findCoverImage
│ ├─ Sort items by date (metadata `order` = ascending|descending)
│ ├─ Fire Hook::TEMPLATE_VARS
│ └─ Template chain: items → list-*.php → base.php
└─ "not_found": 404 response
```
## Module Dependency Order
Loaded sequentially in `router.php`:
```
constants.php → hooks.php → context.php → helpers.php → plugins.php → config.php → content.php → rendering.php
```
`config.php` calls `createContext()` which triggers plugin loading, so `hooks.php` and `plugins.php` must be loaded before it.
## Page vs List Detection
A resolved directory becomes a **list** if it contains subdirectories, otherwise a **page**. Override with `hide_list = true` in metadata to force page rendering on directories with children.
## Deployment Models
### Framework development (this repo)
Both `app/` and `custom/` live in the same repository. `custom/` holds demo/test overrides.
### Site development (separate repo)
The site is its own git repository containing `custom/`, content, and deployment config. `app/` is included as a symlink, git submodule, or copied directory pointing to a specific framework version. The site repo never modifies `app/`.
Typical site repo layout:
```
my-site/ # Site git repo
app/ → ../folderweb/app # Symlink to framework (or submodule, or copy)
custom/ # Site-specific templates, styles, plugins, config
content/ # Website content (often the document root)
devel/ # Site's own dev environment config (optional)
```
Either direction works: `app/` symlinked into a site repo, or `custom/` symlinked into the framework repo during development.
## Demo Fallback
When `content/` (document root) has no files, `app/default/content/` is used automatically. This is **demo mode only** — production sites always provide their own content via the document root.