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

Options Resolver Laravel Package

symfony/options-resolver

Symfony OptionsResolver is array_replace on steroids: define required options, defaults, allowed types/values, normalizers, and validation for robust option/config handling in your PHP code. Great for APIs, components, and reusable libraries.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Install the package:

    composer require symfony/options-resolver
    
  2. Basic usage:

    use Symfony\Component\OptionsResolver\OptionsResolver;
    
    $resolver = new OptionsResolver();
    $resolver->setDefaults([
        'driver' => 'mysql',
        'timeout' => 30,
    ]);
    $resolver->setRequired(['host']);
    $resolver->setAllowedTypes('timeout', ['int', 'null']);
    
    $config = $resolver->resolve([
        'host' => 'localhost',
        'timeout' => 'invalid', // Will throw an exception
    ]);
    
  3. First use case: Validate and normalize configuration for a Laravel service (e.g., App\Services\PaymentGateway):

    $resolver = new OptionsResolver();
    $resolver->setDefaults([
        'api_key' => null,
        'timeout' => 10,
        'retry' => 3,
    ]);
    $resolver->setRequired(['api_key']);
    $resolver->setAllowedTypes('timeout', 'int');
    $resolver->setNormalizer('retry', fn($val) => max(0, $val));
    
    $config = $resolver->resolve(config('services.payment'));
    

Where to Look First

  • Official Documentation (API reference, examples).
  • OptionsResolver class (core methods: setDefaults(), setRequired(), setAllowedTypes(), resolve()).
  • Options class (for nested configurations, e.g., setOptions() in Symfony 8+).
  • Laravel integration: Use with config() or service containers (e.g., bind resolved configs to interfaces).

Implementation Patterns

Core Workflows

1. Service Configuration Validation

// In a Laravel service provider
$resolver = new OptionsResolver();
$resolver->setDefaults([
    'queue' => 'default',
    'timeout' => 60,
    'retry_after' => 30,
]);
$resolver->setAllowedValues('queue', ['default', 'high', 'low']);
$resolver->setAllowedTypes('timeout', 'int');

$config = $resolver->resolve(config('app.job_processor'));
$this->app->singleton(JobProcessor::class, fn() => new JobProcessor($config));

2. Nested Configurations (e.g., Database, API Clients)

$dbResolver = new OptionsResolver();
$dbResolver->setRequired(['host', 'port']);
$dbResolver->setAllowedTypes('port', 'int');

$resolver = new OptionsResolver();
$resolver->setDefaults([
    'database' => $dbResolver->resolve([]), // Nested resolver
    'api' => [
        'base_uri' => 'https://api.example.com',
        'timeout' => 30,
    ],
]);
$resolver->setAllowedTypes('api.timeout', 'int');

3. Dynamic Defaults (Closures)

$resolver = new OptionsResolver();
$resolver->setDefaults([
    'debug' => fn() => app()->environment('local'),
    'cache_ttl' => fn() => env('CACHE_TTL', 3600),
]);

4. Deprecation Handling

$resolver = new OptionsResolver();
$resolver->setDeprecated('old_driver', '2.0', 'Use `new_driver` instead.');
$resolver->setDefault('new_driver', fn() => config('old_driver', 'default'));

5. Reusable Resolvers (Packages)

// In a custom package (e.g., `laravel-notifier`)
class NotifierResolver {
    public static function getResolver(): OptionsResolver {
        $resolver = new OptionsResolver();
        $resolver->setRequired(['driver']);
        $resolver->setAllowedValues('driver', ['mail', 'slack', 'sms']);
        $resolver->setDefaults(['timeout' => 10]);
        return $resolver;
    }
}

// Usage in Laravel
$config = NotifierResolver::getResolver()->resolve(config('services.notifier'));

Laravel-Specific Patterns

Binding Resolved Configs to Interfaces

$this->app->bind(PaymentGatewayInterface::class, function ($app) {
    $resolver = new OptionsResolver();
    $resolver->setRequired(['api_key']);
    $resolver->setAllowedTypes('timeout', 'int');
    $config = $resolver->resolve(config('services.stripe'));
    return new StripeGateway($config);
});

Validation Middleware

use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;

public function handle($request, Closure $next) {
    try {
        $resolver = new OptionsResolver();
        $resolver->setRequired(['api_token']);
        $resolver->resolve($request->input('config'));
    } catch (InvalidOptionsException $e) {
        abort(422, $e->getMessage());
    }
    return $next($request);
}

Dynamic Configuration for Commands

protected $resolver;

public function __construct() {
    $this->resolver = new OptionsResolver();
    $this->resolver->setDefaults(['limit' => 100]);
    $this->resolver->setAllowedTypes('limit', 'int');
}

protected function getConfig(): array {
    return $this->resolver->resolve([
        'limit' => $this->option('limit'),
        'offset' => $this->option('offset') ?? 0,
    ]);
}

Gotchas and Tips

Pitfalls

  1. Nested Options in Symfony <8.0:

    • Issue: setDefault() for nested options (e.g., setDefault('database.host', 'localhost')) is deprecated in Symfony 8+. Use setOptions() instead.
    • Fix:
      // Symfony 7.x (deprecated in 8.0)
      $resolver->setDefault('database.host', 'localhost');
      
      // Symfony 8.0+
      $resolver->setOptions([
          'database' => new OptionsResolver(),
      ]);
      $resolver->setDefault('database', ['host' => 'localhost']);
      
  2. Closure Evaluation Timing:

    • Issue: Closures in setDefaults() are evaluated once (when the resolver is created), not per-resolve.
    • Fix: Use fn() => ... for dynamic values:
      $resolver->setDefaults([
          'cache_ttl' => fn() => env('CACHE_TTL', 3600), // Evaluated per-resolve
      ]);
      
  3. Error Paths in Nested Resolvers:

    • Issue: Error messages for nested options may omit the prototype key (fixed in Symfony 7.3.3+).
    • Fix: Update to Symfony 7.3.3+ or manually prepend paths:
      try {
          $resolver->resolve($input);
      } catch (InvalidOptionsException $e) {
          throw new \RuntimeException("Invalid 'database' config: " . $e->getMessage());
      }
      
  4. Performance with Large Configs:

    • Issue: Resolving deeply nested configs with many options can be slow.
    • Fix: Cache the resolver instance or use OptionsResolver::class in Laravel’s container:
      $this->app->singleton(OptionsResolver::class, fn() => new OptionsResolver());
      
  5. Type Safety with PHP 8.0+:

    • Issue: Union types (e.g., int|string) may not work as expected in older PHP versions.
    • Fix: Use setAllowedTypes() with arrays:
      $resolver->setAllowedTypes('timeout', ['int', 'null']);
      

Debugging Tips

  1. Inspect Resolver State:

    $resolver->resolve([]); // Throws if required options are missing
    $resolver->getDefinedOptions(); // List all defined options
    $resolver->getDefinedOptionNames(); // List option names
    
  2. Enable Strict Mode:

    $resolver->setStrict(true); // Throws on unknown options
    
  3. Custom Error Messages:

    $resolver->setErrorMessage('timeout', 'Timeout must be a positive integer.');
    
  4. Log Resolved Configs:

    $config = $resolver->resolve($input);
    \Log::debug('Resolved config', ['config' => $config]);
    

Extension Points

  1. Custom Normalizers:
    $resolver->setNormalizer('slug', fn($val) => Str::slug($val));
    
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle