Application Configuration
Rhapsody keeps all configuration in the config/ directory and the .env file. You should never modify files inside the /vendor directory. This ensures that framework updates won't overwrite your customisations. Rhapsody is built with extensibility in mind; consult the documentation to learn how to extend or create custom functionality.
Configuration Files
.env– environment‑specific variables (database, mail, caching driver, etc.).config.php– general settings (middleware, theme, logging paths, etc.).
Adding New Configuration
You can create additional config files in the config/ folder and load them in your controllers or services as needed. This is useful for tasks like defining constants or paths, or for custom modules you create. Your custom config files will never be touched when updating the framework.
Environment Variables (.env)
The .env file contains sensitive or environment‑specific data. Never commit it to version control. Use .env.example as a template.
Reading Environment Values
Environment variables are always strings, and PHP types $_ENV loosely. The Rhapsody\Core\Support\Env class reads a variable and converts it to the type you want, falling back to a default when the variable is unset (or can't be converted):
use Rhapsody\Core\Support\Env;
$name = Env::string('APP_NAME', 'Rhapsody'); // string
$timeout = Env::int('GEMINI_TIMEOUT', 60); // int; non-numeric values fall back to the default
$debug = Env::bool('APP_DEBUG', false); // bool
$raw = Env::get('SOME_KEY'); // ?string, null when unset
Env::bool() treats true, 1, yes and on as true, and false, 0, no and off as false (case-insensitive). Any other value returns the default.
Reading Config Values
Values from config.php can be read through Rhapsody\Core\Support\Config, a typed reader with dot notation. It does for the config array what Env does for environment variables, with one big difference: it fails loudly. A missing key or a value of the wrong type throws a RuntimeException at the point of use, instead of letting a bad value travel through your code.
use Rhapsody\Core\Support\Config;
$config = $container->get(Config::class);
$host = $config->string('mail.host'); // throws if 'mail.host' is missing
$port = $config->int('mail.port', 587); // 587 when the key is missing
$debug = $config->bool('app.debug', false);
$items = $config->array('cache.stores', []);
$raw = $config->get('app.name', 'Rhapsody'); // any type; the default when missing
if ($config->has('mail.host')) {
// the key exists
}
The keys above are examples; use the keys defined in your own config.php.
Dot Notation
'mail.host' reads $config['mail']['host'], and you can go as deep as your config array does. An exact top-level key is tried first, so a key that literally contains a dot still works.
Typed Getters
| Method | Accepts | Missing key |
|---|---|---|
string($key, $default = null) | Strings, ints and floats | Throws, unless you pass a default |
int($key, $default = null) | Ints and whole-number strings such as '587' | Throws, unless you pass a default |
bool($key, $default = null) | Bools, and the values true, false, 1, 0, on, off, yes, no | Throws, unless you pass a default |
array($key, $default = null) | Arrays | Throws, unless you pass a default |
get($key, $default = null) | Anything (no type check) | Returns the default |
has($key) | Returns true if the key exists, even when its value is null | Returns false |
all() | Returns the whole config array | – |
Because config values often come from .env (where everything is a string), the scalar getters are forgiving about format: '587' reads as the integer 587, and 'true' or 'on' reads as true. A value of null counts as missing for the typed getters, so the default applies, or they throw if you gave none.
Errors
The exception messages name the key, so a misconfiguration is easy to track down:
Missing config key 'mail.host'.
Config key 'mail.port' must be int, string given.
Config, Env, or Request?
Configthrows on a missing or wrongly-typed key, because a broken setting is a bug in your application.Envfalls back to the default you supply, so an unset variable is never fatal.Requestaccessors (string(),int(),bool(),array()) also fall back to a default, because user input is expected to be messy. See The Request Object.
In tests you can build a reader directly from an array: new Config(['mail' => ['host' => 'smtp.test']]). More on these helpers is on the Support Helpers page.
View File Hierarchy & Theme System
Rhapsody uses Twig for templating. The framework looks for view files in a specific order, making it easy to override core templates or build custom themes.
Directory Structure
The default view directories are:
views/
├── themes/
│ ├── default/ ← Fallback theme (always available)
│ │
│ └── my_custom_theme/ ← Your active theme (if set)
│ ├── layouts/
│ └── home/
└── vendor/arout/rhapsody-core/resources/views/themes/ ← Core framework views (default, but overridable.)
├── default/
├── docs/
└── ...
Theme Resolution Order
When you render a view from your controller (e.g., $this->view('home/index.twig')), the framework searches in this order:
- Active theme –
views/themes/modern/← this is what you currently have set in.env - Default theme –
views/themes/default/ - Core views –
/vendor/arout/rhapsody-core/resources/views/themes/default/
/vendor directory. It is only listed above for informational purposes. Instead, if you want to use the framework's styling/theme, create a new `default` directory in /views/themes/ if it does not already exist.
If a template exists in your active theme, it is used. Otherwise, the framework falls back to the default theme, and finally to the core view. The application will throw a fatal error if it cannot find your template in one of the above directories.
Setting the Active Theme
Define the theme in your .env file:
APP_THEME=my_custom_theme
If not set, the default theme is used.
Overriding Core Templates
To override a core template (e.g., the main layout), simply create a file with the same path inside your active theme. For example, to override views/core/layouts/main.twig, create views/themes/my_custom_theme/layouts/main.twig.
You can also extend core templates using Twig’s {% extends %} tag with the @core namespace:
{% extends "@core/layouts/main.twig" %}
Custom View Namespaces
If you need additional view directories (e.g., for reusable modules), you can add them to the Twig loader in bootstrap.php:
$loader->addPath(__DIR__ . '/modules/blog/views', 'blog');
Then render a view using the namespace: $this->view('@blog/index.twig').
Error Pages
Custom error pages (404, 500, etc.) can be placed in themes/modern/errors/. The framework will automatically use the theme’s error template if it exists, falling back to the default theme.
Middleware Configuration
See the Middleware documentation for details on setting up middleware.
Cache Driver
Switch between file and redis drivers in .env:
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
Logging
Configure log paths in config.php under the logging key.