115 lines
3.9 KiB
Markdown
115 lines
3.9 KiB
Markdown
|
|
# Configuration
|
||
|
|
|
||
|
|
## Config Loading
|
||
|
|
|
||
|
|
**`createContext()`** in `app/config.php` handles the full bootstrap:
|
||
|
|
|
||
|
|
1. Parse `app/default/config.ini` (framework defaults)
|
||
|
|
2. If `custom/config.ini` exists, merge via `array_replace_recursive` (custom wins)
|
||
|
|
3. Load global plugins using merged config
|
||
|
|
4. Determine content directory
|
||
|
|
5. Create `Context` object
|
||
|
|
6. Fire `Hook::CONTEXT_READY` — plugins receive the merged `$config` array
|
||
|
|
|
||
|
|
## INI Format
|
||
|
|
|
||
|
|
Standard PHP `parse_ini_file` with sections enabled:
|
||
|
|
|
||
|
|
```ini
|
||
|
|
[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:
|
||
|
|
|
||
|
|
1. `custom/assets/{requestPath}` — served with `mime_content_type()`
|
||
|
|
2. `{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`:
|
||
|
|
|
||
|
|
```php
|
||
|
|
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`:
|
||
|
|
|
||
|
|
```php
|
||
|
|
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()`:
|
||
|
|
|
||
|
|
1. `$_SERVER['DOCUMENT_ROOT']` is checked for content (>2 entries in scandir)
|
||
|
|
2. If content exists → use document root as `contentDir`
|
||
|
|
3. 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.
|