marko/config
Configuration management for the Marko PHP framework: load PHP config files, merge defaults, and retrieve values using dot notation. Environment-aware overrides and optional config caching keep settings organized and bootstrapping fast. A lightweight, Laravel-style config repository.
Reality check first: marko/config is a read-only split of the marko-php/marko monorepo. The GitHub repo you're looking at is a mirror — issues and PRs live in the main repo. Treat this package as framework-internal plumbing rather than a standalone library you'd composer require into an arbitrary Laravel app. It only makes sense inside a Marko project.
Minimal steps to get productive:
marko/config transitively) rather than the package on its own. If you're building a Marko skeleton, the package is already wired into the service provider bootstrap.config/ directory first. This is the file-first entry point. Expect PHP files returning arrays (config/app.php, config/database.php), mirroring Laravel's convention. That's your primary integration surface — most day-to-day work is editing these files, not touching the package API.Config repository is bound into the container. That tells you the config path, the environment name derivation, and the loading/merge order.config('app.name') (helper) or $container->get(Config::class)->get('app.name') (dot-notation accessor). Confirm the accessor style against the bound contract before assuming Laravel's exact helper name.If you're evaluating whether to adopt it standalone: don't. With 0 stars and 0 dependents outside Marko, there's no ecosystem benefit over Laravel's own config repository, and you'd be betting on a small framework's internals.
File-per-domain, merged into one tree. The standard pattern is one config file per concern (app, database, cache, services), each returning a nested PHP array, all loaded and merged into a single repository keyed by filename. Namespace access by the file name stem, then dot into the array: database.connections.mysql.host.
Package-default → app-override merge. The idiomatic integration for any Marko package is: the package ships defaults under its own config/ directory, and the app's config/ overrides them. When you publish or vendor a package, rely on the framework's merge-on-boot behavior rather than copying files and editing in place. This keeps upgrades clean.
Environment-aware loading. Expect the loader to run after .env/environment detection and before providers that depend on config. The canonical workflow: put environment-specific values in .env, reference them inside config files (env('DB_HOST', '127.0.0.1')), and keep the config files themselves environment-agnostic where possible. Never call env() outside config files — see gotchas.
Dot-notation reads and writes. Treat the repository as get('a.b.c', $default) / set('a.b.c', $value) / has(...). Use set() in tests and in provider register()/boot() to inject runtime overrides (e.g., swapping a driver in test setup) instead of mutating files.
Caching in production. A framework config loader almost always exposes a cache command (mirroring config:cache) that serializes the merged tree to a single PHP file for fast bootstrapping. The workflow is: deploy → run the cache command → verify boot. Local development runs uncached.
Provider integration. Where you need config at construction time, inject the repository (or the specific values) — but be aware some providers receive a fully-loaded config and some don't, depending on boot order. Prefer resolving config in boot() over hard-coding reads in register().
marko-php/marko repo or you'll get nowhere. Budget for that if you hit a loader/merge edge case.env() leakage. The classic Laravel-shaped trap applies: if the loader bakes env() calls at cache time, env() calls scattered through application code return null once config is cached. Keep every environment read inside config files.config/ or .env in a cached environment, re-run the cache command. In dev, confirm you're actually running uncached.set(). Debugging "wrong value" almost always means you assumed a different order. When in doubt, get() the full namespace array and inspect what actually merged in.set() for dynamic values rather than subclassing internals that lack a stable contract.How can I help you explore Laravel packages today?