Strict, Fluent, and Type-Safe Option Validation for PHP.

michelphp eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden
src eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden
tests eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden
.gitignore eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden
LICENSE eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden
README.md eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden
composer.json eb73ef04c7 Release v1.0.0 – Initial Release 1 dag geleden

README.md

PHP Options Resolver

Strict, Fluent, and Type-Safe Option Validation for PHP.

Stop guessing what's in your $options array. This library provides a robust, fluent API to define, validate, and resolve options with strict type enforcement and custom validation logic. Designed for developers who value clarity and code quality.

Installation

To install this library, use Composer

composer require michel/options-resolver

Requirements

  • PHP version 7.4 or higher

Documentation (English)

Basic Usage

Define the options for your class using OptionsResolver with the expected options. You can use static factory methods on the Option class to define types easily.

<?php

use Michel\Resolver\Option;
use Michel\Resolver\OptionsResolver;

class Database
{
    private array $options;

    public function __construct(array $options = [])
    {
        $resolver = new OptionsResolver([
            Option::string('host')->setOptional('localhost'),
            Option::string('username')->required(),
            Option::string('password')->required(),
            Option::string('dbname')->required(),
            Option::int('port')->setOptional(3306),
        ]);

        $this->options = $resolver->resolve($options);
    }
}

// Example usage:
try {
    $database = new Database([
        'username' => 'root',
        'password' => 'secret',
        'dbname' => 'app_db',
    ]);
    // 'host' will be 'localhost' and 'port' will be 3306
} catch (InvalidArgumentException $e) {
    echo "Error: " . $e->getMessage();
}

Available Types

The Option class provides several static factory methods to enforce types automatically. Here are examples for each type:

String

Option::string('host')->setOptional('localhost');

Integer

Option::int('port')->setOptional(3306);

Float

Option::float('timeout')->setOptional(2.5);

Boolean

Option::bool('active')->setOptional(true);

Array

Option::array('tags')->setOptional(['php', 'library']);

Iterable

Option::iterable('items')->required();

Mixed (No type enforcement)

Option::mixed('metadata')->setOptional(null);

Required vs Optional

  • Required: Use required() to enforce that an option must be passed. If missing, an exception is thrown.
  • Optional: Use setOptional($defaultValue) to define a default value if the option is not provided.

    Option::string('apiKey')->required(); // Must be provided
    Option::bool('debug')->setOptional(false); // Defaults to false if missing
    

Custom Validation

You can add custom validation logic using the validator() method. The closure must return a bool.

Option::string('driver')
    ->setOptional('mysql')
    ->validator(function ($value) {
        return in_array($value, ['mysql', 'pgsql', 'sqlite']);
    });

Handling Errors

The resolve() method throws an InvalidArgumentException if:

  • A required option is missing.
  • An undefined option is provided.
  • An option value is invalid (wrong type or failed custom validation).

Conditional Requirements

You can make an option required only if another option has a specific value using addRequiredIf.

$resolver = new OptionsResolver([
    Option::bool('has_database')->setOptional(false),
    Option::string('db_host')->setOptional(null),
]);

// 'db_host' becomes required only if 'has_database' is true
$resolver->addRequiredIf('db_host', 'has_database', true);

Deprecating Options

You can mark an option as deprecated. A E_USER_DEPRECATED error will be triggered if the option is used.

Option::string('old_option')->deprecate('Use "new_option" instead.');

Additional Constraints

The library provides helpers for common constraints like min and max. These work for strings (length), numbers (value), and arrays (count).

Option::string('username')->min(3)->max(20);
Option::int('age')->min(18);
Option::array('tags')->max(5);

Multiple Validators

You can chain multiple validators. All of them must pass.

Option::string('code')
    ->validator(fn($v) => str_starts_with($v, 'A'))
    ->validator(fn($v) => str_ends_with($v, 'Z'));

License

This project is licensed under the MIT License. See the LICENSE file for details.


Documentation (Français)

Usage de base

Définissez les options attendues pour votre classe en utilisant OptionsResolver. Vous pouvez utiliser les méthodes statiques de la classe Option pour définir les types facilement.

<?php

use Michel\Resolver\Option;
use Michel\Resolver\OptionsResolver;

class Database
{
    private array $options;

    public function __construct(array $options = [])
    {
        $resolver = new OptionsResolver([
            Option::string('host')->setOptional('localhost'),
            Option::string('username')->required(),
            Option::string('password')->required(),
            Option::string('dbname')->required(),
            Option::int('port')->setOptional(3306),
        ]);

        $this->options = $resolver->resolve($options);
    }
}

// Exemple d'utilisation :
try {
    $database = new Database([
        'username' => 'root',
        'password' => 'secret',
        'dbname' => 'app_db',
    ]);
    // 'host' vaudra 'localhost' et 'port' vaudra 3306
} catch (InvalidArgumentException $e) {
    echo "Erreur : " . $e->getMessage();
}

Types Disponibles

La classe Option fournit plusieurs méthodes statiques pour forcer les types automatiquement. Voici des exemples pour chaque type :

Chaîne de caractères (String)

Option::string('host')->setOptional('localhost');

Entier (Integer)

Option::int('port')->setOptional(3306);

Flottant (Float)

Option::float('timeout')->setOptional(2.5);

Booléen (Boolean)

Option::bool('active')->setOptional(true);

Tableau (Array)

Option::array('tags')->setOptional(['php', 'library']);

Itérable (Iterable)

Option::iterable('items')->required();

Mixte (Mixed - Pas de vérification de type)

Option::mixed('metadata')->setOptional(null);

Requis vs Optionnel

  • Requis : Utilisez required() pour obliger l'utilisateur à fournir une option. Si elle est manquante, une exception est levée.
  • Optionnel : Utilisez setOptional($defaultValue) pour définir une valeur par défaut si l'option n'est pas fournie.

    Option::string('apiKey')->required(); // Doit être fourni
    Option::bool('debug')->setOptional(false); // Vaut false par défaut si absent
    

Validation Personnalisée

Vous pouvez ajouter une logique de validation personnalisée via la méthode validator(). La closure doit retourner un bool.

Option::string('driver')
    ->setOptional('mysql')
    ->validator(function ($value) {
        return in_array($value, ['mysql', 'pgsql', 'sqlite']);
    });

Gestion des Erreurs

La méthode resolve() lance une InvalidArgumentException si :

  • Une option requise est manquante.
  • Une option non définie est fournie.
  • Une valeur d'option est invalide (mauvais type ou échec de validation personnalisée).

Prérequis Conditionnels

Vous pouvez rendre une option obligatoire uniquement si une autre option a une valeur spécifique en utilisant addRequiredIf.

$resolver = new OptionsResolver([
    Option::bool('has_database')->setOptional(false),
    Option::string('db_host')->setOptional(null),
]);

// 'db_host' devient requis uniquement si 'has_database' est true
$resolver->addRequiredIf('db_host', 'has_database', true);

Obsolescence (Deprecation)

Vous pouvez marquer une option comme obsolète. Une erreur E_USER_DEPRECATED sera déclenchée si l'option est utilisée.

Option::string('old_option')->deprecate('Utilisez "new_option" à la place.');

Contraintes Supplémentaires

La bibliothèque fournit des aides pour des contraintes courantes comme min et max. Elles fonctionnent pour les chaînes (longueur), les nombres (valeur) et les tableaux (nombre d'éléments).

Option::string('username')->min(3)->max(20);
Option::int('age')->min(18);
Option::array('tags')->max(5);

Validateurs Multiples

Vous pouvez enchaîner plusieurs validateurs. Tous doivent être valides.

Option::string('code')
    ->validator(fn($v) => str_starts_with($v, 'A'))
    ->validator(fn($v) => str_ends_with($v, 'Z'));

Licence

Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus de détails.