Config Files
Beyond the environment, your app is wired up with PHP *.config.php files that
register access roles, settings, file paths, languages and migrations. The build collects the registrations
and generates typed code from them.
How they load
Any file whose name ends in .config.php is discovered and included from your app at startup —
the vendor directory is skipped and the location otherwise does not matter, so a
config/ folder is only a convention. The framework ships an example of each file: copy the ones
you need into your app and extend them. Register nothing and the framework falls back to its own defaults.
By convention they live together in a config/ folder — one file per concern:
config/
├── Access.config.php # access roles
├── Settings.config.php # typed settings
├── File.config.php # extra storage paths
├── Intl.config.php # languages & copy dirs
├── Migration.config.php # migrations folder & last applied
├── MigrationTables.config.php # table renames
└── MigrationColumns.config.php # column renames
./framework build.Access roles
Register the access levels your app uses and group them:
use Framework\Core\AccessRole;
AccessRole::register("General", "General");
AccessRole::register("Admin", "Admin");
AccessRole::register("API", "API");If you provide no Access.config.php at all, the framework falls back to exactly this
configuration — so the General, Admin and API roles always exist. The
build turns whichever roles are registered into a typed Access enum — see how it is generated and
how routes use it in Authentication.
Settings
Declare typed settings; the build generates a typed getter for each and a migration stores them in the database. See Settings for the types, storage and generated class.
use Framework\Core\SettingConfig;
use Framework\Core\VariableType;
SettingConfig::register("siteName", "General", VariableType::String);
SettingConfig::register("maxItems", "General", VariableType::Integer);File paths
Register extra storage paths and sub-directories on top of the built-in ones (source, thumbs, avatars,
temp). The build generates a typed Path class for them, and the directories can be created by a
migration. See Files & Storage.
use Framework\File\FilePath;
FilePath::register("exports");
FilePath::registerDirectory("archive");Languages
Set the default language and point the internationalization layer at your copy directories — strings, emails and notifications, each holding one JSON file per language:
use Framework\Intl\IntlConfig;
IntlConfig::setDefaultLanguage("en");
IntlConfig::setStringsDir("nls/strings");
IntlConfig::setEmailsDir("nls/emails");
IntlConfig::setNotificationsDir("nls/notifications");Each directory holds one JSON file per language, named by its code (en.json,
es.json). The framework loads the file that matches the active language and falls back to the
default you set above — so a typical nls/ tree looks like this:
nls/
├── strings/ # UI strings, one file per language
│ ├── en.json
│ └── es.json
├── emails/ # email copy, loaded per language
│ ├── en.json
│ └── es.json
└── notifications/ # notification copy, per language
├── en.json
└── es.json
Migrations
The migration config is split across three files. The main one points the runner at your migrations folder and, when adopting it on an existing database, sets the last migration already applied:
use Framework\Database\Migration;
Migration::setPath("migrations");
Migration::setLastApplied("2024_01_initial");A second file lists the table renames and a third the column renames. Both run before the schema is synced,
so the sync matches your models against the new names instead of dropping and recreating. Table names may be
snake_case or PascalCase, ID columns SNAKE_CASE:
use Framework\Database\Migration;
Migration::renameTable("old_table", "new_table");
Migration::renameTable("Client", "Customer");use Framework\Database\Migration;
Migration::renameColumn("TableName", "oldColumn", "newColumn");
Migration::renameColumn("Customer", "CLIENT_ID", "CUSTOMER_ID");