Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Configuration Laravel Package

slick/configuration

Configuration management for Laravel: load, merge, validate, and access app settings through a clean, typed API. Cache config values, support environment-specific overrides, and keep settings organized in one place.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and read in this order

composer require slick/configuration
  • README.md — the only real spec; read it fully before writing code.
  • src/Configuration.php — the facade you will use 95% of the time.
  • src/Driver/ — one class per file format (PhpDriver, IniDriver, YamlDriver, JsonDriver, etc.). This directory tells you what is actually supported and how merge/namespace behaviour works.
  • tests/ — when the README is ambiguous, the tests are the real contract for dot-notation, defaults, and load order.

2. First config file

Create a PHP file that returns an array — this is the lowest-friction driver and needs no extra parser dependency:

// config/app.php
return [
    'debug' => true,
    'database' => [
        'host' => '127.0.0.1',
        'name' => 'app',
    ],
];

3. First use case — load a group and read with dot notation

use Slick\Configuration\Configuration;

$config = new Configuration();
$config->load(__DIR__ . '/config/app.php', 'app');

$debug = $config->get('app.debug', false);
$host  = $config->get('app.database.host', 'localhost');

That is the whole day-one workflow: load a file under a namespace, read values by dotted path, always pass a default. Confirm exact method signatures against the README — the package is small and the API surface is narrow, so there is no reason to guess.

4. Programmatic overrides

$config->set('app.database.host', 'db.internal');

Use this for tests and for environment-specific overrides loaded after the base file.

Implementation Patterns

Namespace per file, one file per concern. Load app.php as app, db.php as db, mail.php as mail. Flat, prefixed keys keep get() calls readable and make it obvious where a value comes from. Avoid one giant config.php.

Environment layering by load order. Keep a base file and a local override, and load the override second (assuming last-loaded wins — verify this in the driver tests before relying on it):

$config->load('config/app.php', 'app');
if (is_file('config/app.local.php')) {
    $config->load('config/app.local.php', 'app'); // overrides
}
$config->set('app.debug', getenv('APP_DEBUG') === '1');

Explicit ordering beats implicit merging. If merge semantics are shallow, re-load the whole group rather than relying on per-key merging.

Wrap it in your own interface. This is the single most valuable pattern for a 0-star dependency:

interface AppConfig
{
    public function debug(): bool;
    public function dbHost(): string;
}

Bind the Slick-backed implementation in your container. Your application code never imports Slick\Configuration\Configuration, so swapping the library later is a one-file change. It also gives you one place to cast, validate, and fail fast on missing required keys.

Consume via constructor injection, not statics. If the package exposes a singleton/initialize() helper, avoid it in application code. Pass a ConfigurationInterface into services so tests can supply a hand-built instance or a mock. Keep a single factory that builds the fully-loaded config object at bootstrap and injects it.

Hydrate typed DTOs at the edge. Read raw values in a factory and construct value objects:

final class DatabaseConfig
{
    public static function fromConfiguration(ConfigurationInterface $c): self
    {
        return new self(
            host: (string) $c->get('app.database.host', 'localhost'),
            port: (int) $c->get('app.database.port', 3306),
        );
    }
}

Config arrays are untyped; convert once, then work with real types.

Custom drivers are the extension point. If you need .env or .toml, implement the package's DriverInterface (see src/Driver/ for the contract) and register/map it for your extension. Keep such drivers in your own codebase, not in a fork.

Testing. Build a Configuration in the test (or a small fake implementing the interface), set() the keys the subject needs, assert behaviour. Never load real config files in unit tests; do that in one integration test for the loader itself.

Gotchas and Tips

  • Dots in keys collide with dot notation. A key literally named foo.bar is indistinguishable from nested foo => bar. Enforce "no dots in key names" in your config files, or the lookup will silently return the wrong thing.
  • PHP driver executes code. load() on a .php file includes it. Never pass a user-controlled or writable path; treat config files as trusted source code and keep them out of the web root.
  • Load-order and merge semantics are the main source of surprises. Before depending on overrides, write one test that loads two files into the same namespace and asserts which value wins and whether nested arrays are merged deeply or replaced wholesale. Document the answer in your own code.
  • Missing keys. Decide once whether a missing key returns null, throws, or is a bug. Pass explicit defaults everywhere and use has() for optional sections. A typo in a dotted path looks identical to an intentionally absent value.
  • No automatic env-var interpolation. Most lightweight config packages do not expand ${VAR} inside values. Do the substitution yourself in the factory, or keep secrets in getenv() and inject them with set().
  • Extension detection quirks. Drivers are usually selected by file extension — check case sensitivity (.YML) and what happens for an unknown extension. Prefer explicit driver selection over magic when it is offered.
  • Cache parsed files in production. YAML/INI/JSON parsing on every request is measurable. Either memoise the loaded Configuration in your bootstrap/container or precompile to a PHP array in your deploy step.
  • Statics and singletons leak between tests. If you use any global/initialize() API, reset it in tearDown() or you will chase ghost state. Constructor-based instances avoid this entirely.
  • Low bus factor — pin and isolate. Zero stars, zero dependents, a single recent release. Pin the exact version in composer.json, keep the wrapper interface described above, and add one small test asserting the behaviours you rely on (dot access, defaults, override order). That test is your upgrade safety net if the package is abandoned.
  • Debugging: when a value is not what you expect, dump the fully-loaded configuration once at bootstrap (redact secrets) and confirm the namespace, the file actually loaded (is_file before load), and the order. Most "broken config" reports are a wrong path or a later load clobbering an earlier one.
  • YAML/INI extras: confirm whether the optional driver dependencies are pulled in automatically. If not, you must install the parser yourself and the failure appears as a class-not-found at load time, not at install time.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings