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
3.9 KiB
Configuration
Config Loading
createContext() in app/config.php handles the full bootstrap:
- Parse
app/default/config.ini(framework defaults) - If
custom/config.iniexists, merge viaarray_replace_recursive(custom wins) - Load global plugins using merged config
- Determine content directory
- Create
Contextobject - Fire
Hook::CONTEXT_READY— plugins receive the merged$configarray
INI Format
Standard PHP parse_ini_file with sections enabled:
[languages]
default = "en"
available = "no,en"
[plugins]
enabled = "languages"
[custom_section]
key = "value"
Built-in Config Keys
| Section | Key | Type | Default | Purpose |
|---|---|---|---|---|
languages |
default |
string | "en" |
Default language (no URL prefix) |
languages |
available |
string | "no,en" |
Comma-separated ISO 639-1 codes |
plugins |
enabled |
string | "languages" |
Comma-separated plugin names (without .php) |
All other sections are custom — define whatever your plugins need.
The custom/ Override System
This is the primary mechanism for using FolderWeb. The custom/ directory is the user layer:
custom/
config.ini # Merged over app/default/config.ini
templates/ # Override by matching filename (e.g., base.php, page.php, list.php)
styles/ # Served at /app/styles/*
fonts/ # Served at /app/fonts/*
assets/ # Served at document root (favicon.ico, robots.txt, etc.)
languages/ # Merged over app/default/languages/*.ini
plugins/
global/ # Loaded alongside app/plugins/global/
page/ # Reserved for future use
Override Resolution Order
| Resource | Lookup | Fallback |
|---|---|---|
| Templates | custom/templates/{name}.php |
app/default/templates/{name}.php |
| Styles | custom/styles/* via /app/styles/* |
app/default/styles/* via /app/default-styles/* |
| Languages | custom/languages/{lang}.ini merged over |
app/default/languages/{lang}.ini |
| Config | custom/config.ini merged over |
app/default/config.ini |
| Plugins | custom/plugins/{scope}/{name}.php |
app/plugins/{scope}/{name}.php |
For plugins, custom takes priority: if both custom/plugins/global/foo.php and app/plugins/global/foo.php exist, the custom version loads.
For full template resolution details, see 06-templates.md "Template Resolution (canonical reference)".
Static Asset Routing
The router checks these locations before content routing:
custom/assets/{requestPath}— served withmime_content_type(){contentDir}/{requestPath}— served with explicit MIME map for:css,jpg,jpeg,png,gif,webp,svg,pdf,woff,woff2,ttf,otf
/app/* requests are handled by static.php:
/app/styles/*→custom/styles/*/app/fonts/*→custom/fonts/*/app/assets/*→custom/assets/*/app/default-styles/*→app/default/styles/*
Accessing Config in Plugins
The merged config array is passed to Hook::CONTEXT_READY:
Hooks::add(Hook::CONTEXT_READY, function(Context $ctx, array $config) {
$val = $config['my_section']['my_key'] ?? 'default';
$ctx->set('my_val', $val);
return $ctx;
});
Config is not directly available in templates. Expose values via Hook::TEMPLATE_VARS:
Hooks::add(Hook::TEMPLATE_VARS, function(array $vars, Context $ctx) {
global $config;
$vars['siteTitle'] = $config['site']['title'] ?? 'My Site';
return $vars;
});
Content Directory Resolution
In createContext():
$_SERVER['DOCUMENT_ROOT']is checked for content (>2 entries in scandir)- If content exists → use document root as
contentDir - If empty → fall back to
app/default/content/(demo mode)
Production deployments set Apache's DocumentRoot to the content directory. The app/ and custom/ directories live outside the document root, accessed via Apache aliases.