Weave Code
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

Php Settings Container Laravel Package

chillerlan/php-settings-container

Lightweight settings container for PHP that decouples configuration logic from your application. Provides a SettingsContainerInterface with property-hook style access (for PHP < 8.4). Not a dependency injection container.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require chillerlan/php-settings-container
    

    Requires PHP 8.4+ (or PHP 8.1+ for older versions).

  2. Basic Container:

    use Chillerlan\SettingsContainer\SettingsContainerAbstract;
    
    class AppSettings extends SettingsContainerAbstract {
        protected string $app_name;
        protected int $max_retries = 3;
    }
    
  3. First Use Case:

    $settings = new AppSettings(['app_name' => 'MyApp']);
    echo $settings->app_name; // "MyApp"
    $settings->max_retries = 5; // Override default
    

Where to Look First


Implementation Patterns

1. Immutable Configuration Objects

Use the container to create immutable configuration objects that can be passed around safely:

class DatabaseConfig extends SettingsContainerAbstract {
    protected string $host;
    protected int $port;
    protected string $username;
    protected string $password;

    public function __construct(array $config) {
        parent::__construct($config);
        $this->password = password_hash($this->password, PASSWORD_DEFAULT);
    }
}

2. Trait-Based Configuration

Combine traits from different libraries into a single container:

use LibraryOne\OptionsTrait;
use LibraryTwo\MoreOptionsTrait;

class CombinedSettings extends SettingsContainerAbstract {
    use OptionsTrait, MoreOptionsTrait;

    protected string $custom_field;
}

3. JSON Serialization/Deserialization

Leverage built-in JSON support for configuration files:

// Load from JSON
$settings = new AppSettings();
$settings->fromJSON(file_get_contents('config.json'));

// Save to JSON
file_put_contents('config.json', $settings->toJSON());

4. Magic Getters/Setters with Validation

Use magic methods for runtime validation:

class UserSettings extends SettingsContainerAbstract {
    protected string $email;

    protected function set_email(string $value): void {
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException("Invalid email");
        }
        $this->email = $value;
    }
}

5. Integration with Laravel

Use the container for Laravel config (e.g., in AppServiceProvider):

public function boot(): void {
    $this->app->singleton('settings', function () {
        return new AppSettings(config('app.settings'));
    });
}

6. Environment-Based Configuration

Load settings from .env:

$settings = new AppSettings($_ENV);

7. Dependency Injection

Bind the container to Laravel's DI container:

$this->app->bind('settings', function () {
    return new AppSettings(config('settings'));
});

Gotchas and Tips

Pitfalls

  1. Property Hooks Conflict (PHP 8.4+):

    • If a property has both a property hook (PHP 8.4+) and a magic getter/setter, the hook takes precedence.
    • Avoid defining both set_foo() and get_foo() if the property has hooks.
  2. Non-Existent Properties:

    • By default, accessing undefined properties returns null.
    • Enable strict mode with [ThrowOnInvalidProperty(true)] to throw exceptions:
      #[ThrowOnInvalidProperty(true)]
      class StrictSettings extends SettingsContainerAbstract { ... }
      
  3. Trait Initialization Order:

    • Traits are initialized in the order they are declared. Use construct() to enforce logic:
      protected function OptionsTrait(): void {
          $this->foo = strtoupper($this->foo);
      }
      
  4. Serialization Caveats:

    • serialize()/unserialize() bypass magic getters/setters. Use toArray()/fromIterable() for consistent behavior.
  5. Public Properties:

    • Public properties never trigger magic getters/setters, but their hooks (PHP 8.4+) are still called.

Debugging Tips

  1. Check Property Access: Use var_dump($container->toArray()) to inspect all properties.

  2. Enable Strict Mode: Temporarily enable [ThrowOnInvalidProperty(true)] to catch undefined property access.

  3. Trait Debugging: Override construct() to log trait initialization:

    protected function construct(): void {
        error_log('Initializing traits...');
        parent::construct();
    }
    

Performance Tips

  1. Avoid Magic Methods in Loops: Cache frequently accessed properties:

    private ?string $cachedFoo = null;
    
    public function getFoo(): string {
        return $this->cachedFoo ?? ($this->cachedFoo = $this->foo);
    }
    
  2. Use toArray() for Bulk Access: Fetch all properties at once to minimize magic method calls:

    $allSettings = $container->toArray();
    

Extension Points

  1. Custom JSON Handling: Override toJSON()/fromJSON() for custom serialization:

    public function toJSON(int $options = 0): string {
        return json_encode($this->toArray(), $options | JSON_PRETTY_PRINT);
    }
    
  2. Add Validation Traits: Create reusable validation traits:

    trait ValidatedEmail {
        protected function set_email(string $value): void {
            if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
                throw new \InvalidArgumentException("Invalid email");
            }
            $this->email = $value;
        }
    }
    
  3. Merge Configurations: Implement fromIterable() to merge multiple sources:

    public function fromIterable(iterable $properties): static {
        $current = $this->toArray();
        foreach ($properties as $key => $value) {
            $current[$key] = $value;
        }
        return new static($current);
    }
    
  4. Environment-Aware Defaults: Use construct() to set defaults based on environment:

    protected function construct(): void {
        if (app()->environment('local')) {
            $this->debug = true;
        }
    }
    

Laravel-Specific Tips

  1. Cache Settings: Cache the container instance in Laravel:

    $settings = Cache::remember('app.settings', now()->addHours(1), function () {
        return new AppSettings(config('app.settings'));
    });
    
  2. Config Binding: Bind the container to Laravel's config:

    $this->app->bind('settings', function () {
        return new AppSettings(config('settings'));
    });
    
  3. Environment-Specific Containers: Create environment-specific containers:

    class LocalSettings extends SettingsContainerAbstract { ... }
    class ProductionSettings extends SettingsContainerAbstract { ... }
    
    $settings = app()->environment('local')
        ? new LocalSettings(config('settings'))
        : new ProductionSettings(config('settings'));
    
  4. Service Provider Integration: Load settings in a service provider:

    public function register(): void {
        $this->app->singleton('settings', function () {
            return new AppSettings(config('settings'));
        });
    }
    
  5. Validation with Laravel Rules: Combine with Laravel's validation:

    use Illuminate\Support\Facades\Validator;
    
    $validator = Validator::make($settings->toArray(), [
        'email' => 'required|email',
        'max_retries' => 'integer|min:1',
    ]);
    

Common Anti-Patterns

  1. Overusing Magic Methods: Avoid excessive magic getters/setters for simple properties. Use direct access when possible.

  2. Ignoring Immutability: Treat the container as immutable after initialization. Avoid modifying properties directly unless using magic setters.

  3. Mixing State and Logic: Keep business logic out of the container. Use traits or separate classes for complex logic.

  4. Global State: Avoid using a single global container instance. Prefer dependency injection or scoped bindings.

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport
twbs/bootstrap4
php-http/client-implementation
phpcr/phpcr-implementation
cucumber/gherkin-monorepo
haydenpierce/class-finder
psr/simple-cache-implementation
uri-template/tests