# 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](https://getcomposer.org/) ```bash 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 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 ```php Option::string('host')->setOptional('localhost'); ``` #### Integer ```php Option::int('port')->setOptional(3306); ``` #### Float ```php Option::float('timeout')->setOptional(2.5); ``` #### Boolean ```php Option::bool('active')->setOptional(true); ``` #### Array ```php Option::array('tags')->setOptional(['php', 'library']); ``` #### Iterable ```php Option::iterable('items')->required(); ``` #### Mixed (No type enforcement) ```php 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. ```php 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`. ```php 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`. ```php $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. ```php 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). ```php 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. ```php 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](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 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) ```php Option::string('host')->setOptional('localhost'); ``` #### Entier (Integer) ```php Option::int('port')->setOptional(3306); ``` #### Flottant (Float) ```php Option::float('timeout')->setOptional(2.5); ``` #### Booléen (Boolean) ```php Option::bool('active')->setOptional(true); ``` #### Tableau (Array) ```php Option::array('tags')->setOptional(['php', 'library']); ``` #### Itérable (Iterable) ```php Option::iterable('items')->required(); ``` #### Mixte (Mixed - Pas de vérification de type) ```php 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. ```php 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`. ```php 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`. ```php $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. ```php 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). ```php 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. ```php 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](LICENSE) pour plus de détails.