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 PHP settings container to keep configuration logic out of your app (not a DI container). Provides a SettingsContainerInterface with “property hook”-style access for PHP < 8.4, plus sane defaults for organizing and retrieving settings objects.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require chillerlan/php-settings-container
    

    Requires PHP 8.4+.

  2. Basic Container:

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

    $settings = new AppSettings(['app_name' => 'MyApp']);
    echo $settings->app_name; // "MyApp"
    echo $settings->debug_mode; // "false" (default)
    

Where to Look First

  • SettingsContainerAbstract: Core class for extending.
  • SettingsContainerInterface: Defines the contract (e.g., toArray(), toJSON()).
  • Property Hooks (PHP 8.4+): For advanced property-level logic (e.g., validation, transformations).

Implementation Patterns

1. Immutable Configuration Objects

Pattern: Use the container to encapsulate app/config settings as immutable objects.

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

// Initialize once (e.g., in a service provider)
$databaseConfig = new DatabaseConfig([
    'host'     => env('DB_HOST'),
    'port'     => env('DB_PORT'),
    'username' => env('DB_USER'),
    'password' => env('DB_PASS'),
]);

Workflow:

  • Pass config to services via constructor injection.
  • Modify defaults in a single place (e.g., DatabaseConfig class).

2. Trait-Based Composition

Pattern: Combine traits from libraries into a single container.

use Chillerlan\SettingsContainer\SettingsContainerAbstract;
use LibraryOne\OptionsTrait;
use LibraryTwo\MoreOptionsTrait;

class CompositeSettings extends SettingsContainerAbstract {
    use OptionsTrait, MoreOptionsTrait;

    protected string $custom_field;
}

// Initialize with merged config
$settings = new CompositeSettings([
    'foo' => 'bar', // From OptionsTrait
    'bar' => 'baz', // From MoreOptionsTrait
    'custom_field' => 'value',
]);

Integration Tips:

  • Traits define set_*/get_* methods for property logic (e.g., hashing, validation).
  • Call construct() in the parent class to trigger trait initialization.

3. Serialization/Deserialization

Pattern: Persist settings to JSON or serialize for caching.

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

// Load from JSON
$loadedSettings = new AppSettings();
$loadedSettings->fromJSON($json);

// Serialize for caching (e.g., Redis)
$serialized = serialize($settings);
$unserialized = unserialize($serialized);

Use Case: Store settings in a database or cache (e.g., Laravel's cache driver).


4. Validation via Property Hooks (PHP 8.4+)

Pattern: Use PHP 8.4 property hooks for runtime validation.

class ValidatedSettings extends SettingsContainerAbstract {
    protected string $email {
        set => $this->validateEmail($value);
    }

    private function validateEmail(string $email): void {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new \InvalidArgumentException("Invalid email: $email");
        }
    }
}

Alternative (Pre-8.4): Use set_* methods in traits.

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

5. Environment-Aware Settings

Pattern: Load settings from .env dynamically.

class EnvSettings extends SettingsContainerAbstract {
    protected string $env;
    protected string $debug;

    public function __construct() {
        $this->env = env('APP_ENV', 'production');
        $this->debug = env('APP_DEBUG', 'false');
    }
}

Workflow:

  • Use in Laravel service providers or console commands.
  • Override defaults via fromIterable() if needed.

Gotchas and Tips

Pitfalls

  1. Property Hooks vs. Magic Methods (PHP 8.4+):

    • If a property has a hook (e.g., set => ...), the set_* method is ignored.
    • Public properties never trigger magic methods (but hooks still run).
    • Fix: Use hasSetHook()/hasGetHook() to debug conflicts.
  2. Non-Existent Properties:

    • By default, accessing undefined properties returns null.
    • Enable strict mode with [ThrowOnInvalidProperty(true)]:
      #[ThrowOnInvalidProperty(true)]
      class StrictSettings extends SettingsContainerAbstract { ... }
      
      Now $settings->nonexistent throws InvalidPropertyException.
  3. Trait Initialization Order:

    • Traits are called in construct() after properties are set.
    • Gotcha: If a trait modifies a property, ensure it runs after the property is initialized.
    • Fix: Reorder traits or use construct() to control flow.
  4. Serialization Quirks:

    • serialize()/unserialize() bypass magic methods/hooks.
    • Use toArray()/fromIterable() for consistent behavior.

Debugging Tips

  1. Check Property Access:

    • Override __get()/__set() to log access:
      public function __get(string $name) {
          error_log("Getting property: $name");
          return parent::__get($name);
      }
      
  2. Validate Traits:

    • Ensure trait methods match property names (e.g., set_foo for $foo).
    • Use get_class_methods() to list available methods:
      print_r(get_class_methods($settings));
      
  3. JSON/Array Conversion:

    • toArray() now runs through magic getters (fixed in v3.2.0).
    • For debugging, compare raw properties vs. toArray() output.

Extension Points

  1. Custom Serialization:

    • Implement __serialize()/__unserialize() for non-standard formats.
    • Example: Store settings in a custom binary format.
  2. Dynamic Property Handling:

    • Extend SettingsContainerAbstract to add dynamic properties:
      public function __get(string $name) {
          if (str_starts_with($name, 'dynamic_')) {
              return $this->generateDynamicValue($name);
          }
          return parent::__get($name);
      }
      
  3. Attribute-Based Configuration:

    • Use PHP 8 attributes to mark properties for special handling:
      #[Attribute]
      class Encrypted {}
      
      class SecureSettings extends SettingsContainerAbstract {
          #[Encrypted] protected string $api_key;
      }
      
    • Override construct() to process attributes.
  4. Integration with Laravel:

    • Bind the container to the service container:
      $app->singleton(AppSettings::class, fn() => new AppSettings());
      
    • Use in config files:
      'settings' => [
          'app_name' => app(AppSettings::class)->app_name,
      ],
      

Performance Notes

  • Immutable by Design: Avoid modifying properties after initialization.
  • Avoid Reflection: Magic methods add slight overhead; benchmark if critical.
  • Caching: Serialize settings once and cache (e.g., Redis) for repeated use.
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope