PHP - Create your own PHP Options Resolver ( Like Symfony )
F.R Michel
Posted on June 13, 2021
Options Resolver: A Simple Tool for Processing and Validating Option Arrays
Required PHP Version: 7.3 and above
Now, let's create an OptionsResolver.php
file to house the resolver logic.
<?php
declare(strict_types=1);
namespace DevCoder\Resolver;
final class OptionsResolver
{
/**
* @var \ArrayObject<Option>
*/
private $options;
public function __construct(array $options)
{
$this->options = new \ArrayObject();
foreach ($options as $option) {
$this->add($option);
}
}
public function resolve(array $options): array
{
$this->checkDiff($options);
/**
* @var Option $option
*/
$optionsResolved = [];
foreach ($this->options as $option) {
$optionName = $option->getName();
if (\array_key_exists($optionName, $options)) {
$value = $options[$optionName];
if ($option->isValid($value) === false) {
throw new \InvalidArgumentException(\sprintf('The option "%s" with value %s is invalid.', $optionName, self::formatValue($value)));
}
$optionsResolved[$optionName] = $value;
continue;
}
if ($option->hasDefaultValue()) {
$optionsResolved[$optionName] = $option->getDefaultValue();
continue;
}
throw new \InvalidArgumentException(\sprintf(
'The required option "%s" is missing.', $optionName)
);
}
return $optionsResolved;
}
private function add(Option $option): self
{
$this->options->offsetSet($option->getName(), $option);
return $this;
}
private function checkDiff(array $options): void
{
$defined = $this->options->getArrayCopy();
$diff = \array_diff_key($options, $defined);
if (\count($diff) > 0) {
throw new \InvalidArgumentException(\sprintf(
'The option(s) "%s" do(es) not exist. Defined options are: "%s".',
\implode(', ', \array_keys($diff)),
\implode('", "', \array_keys($defined)))
);
}
}
private static function formatValue($value): string
{
if (\is_object($value)) {
return \get_class($value);
}
if (\is_string($value)) {
return '"' . $value . '"';
}
if (false === $value) {
return 'false';
}
if (true === $value) {
return 'true';
}
return \gettype($value);
}
}
Option Definition: Creating the Option.php File for Defining an Option
The Option.php file below allows you to define an option with properties such as the name, default value, presence of a default value, a validator, and more.
<?php
declare(strict_types=1);
namespace DevCoder\Resolver;
final class Option
{
/**
* @var string
*/
private $name;
/**
* @var mixed
*/
private $defaultValue;
/**
* @var bool
*/
private $hasDefaultValue = false;
/**
* @var \Closure|null
*/
private $validator;
/**
* Option constructor.
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
/**
* @return mixed
*/
public function getDefaultValue()
{
return $this->defaultValue;
}
/**
* @param mixed $defaultValue
* @return Option
*/
public function setDefaultValue($defaultValue): self
{
$this->hasDefaultValue = true;
$this->defaultValue = $defaultValue;
return $this;
}
public function hasDefaultValue(): bool
{
return $this->hasDefaultValue;
}
public function validator(\Closure $closure): self
{
$this->validator = $closure;
return $this;
}
public function isValid($value): bool
{
if ($this->validator instanceof \Closure) {
$validator = $this->validator;
return $validator($value);
}
return true;
}
}
How to Use?
To use this Options Resolver, follow these steps:
1. Define Required Options:
<?php
use DevCoder\Resolver\Option;
use DevCoder\Resolver\OptionsResolver;
class Database
{
public function __construct(array $options = [])
{
$resolver = new OptionsResolver([
new Option('host'),
new Option('username'),
new Option('password'),
new Option('dbname'),
]);
$this->options = $resolver->resolve($options);
}
}
$database = new Database([
'host' => 'localhost',
'dbname' => 'app',
]);
// Uncaught InvalidArgumentException: The required option "username" is missing.
$database = new Database([
'host' => 'localhost',
'dbname' => 'app',
'username' => 'root',
'password' => 'root',
]);
// OK
2. Define default options
<?php
use DevCoder\Resolver\Option;
use DevCoder\Resolver\OptionsResolver;
class Database
{
public function __construct(array $options = [])
{
$resolver = new OptionsResolver([
(new Option('host'))->setDefaultValue('localhost'),
(new Option('username'))->setDefaultValue('root'),
(new Option('password'))->setDefaultValue('root'),
(new Option('dbname'))->setDefaultValue('app'),
]);
/**
* array(4) {
* ["host"]=>
* string(9) "localhost"
* ["username"]=>
* string(4) "root"
* ["password"]=>
* string(4) "root"
* ["dbname"]=>
* string(3) "app"
* }
*/
$this->options = $resolver->resolve($options);
}
}
$database = new Database([]);
// OK
<?php
use DevCoder\Resolver\Option;
use DevCoder\Resolver\OptionsResolver;
class Database
{
public function __construct(array $options = [])
{
$resolver = new OptionsResolver([
(new Option('host'))->setDefaultValue('localhost'),
(new Option('username'))->setDefaultValue('root'),
(new Option('password'))->setDefaultValue('root'),
(new Option('dbname'))->setDefaultValue('app'),
]);
/**
* array(4) {
* ["host"]=>
* string(9) "localhost"
* ["username"]=>
* string(4) "root"
* ["password"]=>
* string(4) "root"
* ["dbname"]=>
* string(3) "app-2"
* }
*/
$this->options = $resolver->resolve($options);
}
}
$database = new Database([
'dbname' => 'app-2'
]);
// OK
3. Non-existent options
<?php
use DevCoder\Resolver\Option;
use DevCoder\Resolver\OptionsResolver;
class Database
{
public function __construct(array $options = [])
{
$resolver = new OptionsResolver([
(new Option('host'))->setDefaultValue('localhost'),
(new Option('username'))->setDefaultValue('root'),
(new Option('password'))->setDefaultValue('root'),
(new Option('dbname'))->setDefaultValue('app'),
]);
$this->options = $resolver->resolve($options);
}
}
$database = new Database([
'url' => 'mysql://root:root@localhost/app',
]);
// Uncaught InvalidArgumentException: The option(s) "url" do(es) not exist. Defined options are: "host", "username", "password", "dbname"
4. Validate options values
<?php
use DevCoder\Resolver\Option;
use DevCoder\Resolver\OptionsResolver;
class Database
{
public function __construct(array $options = [])
{
$resolver = new OptionsResolver([
(new Option('host'))
->validator(static function($value) {
return is_string($value);
})
->setDefaultValue('localhost'),
(new Option('username'))
->validator(static function($value) {
return is_string($value);
})
->setDefaultValue('root')
,
(new Option('password'))
->validator(static function($value) {
return is_string($value);
})
->setDefaultValue('root'),
(new Option('dbname'))
->validator(static function($value) {
return is_string($value);
})
->setDefaultValue('app'),
(new Option('driver'))
->validator(static function($value) {
return in_array($value, ['pdo_mysql', 'pdo_pgsql']);
})
->setDefaultValue('pdo_mysql'),
]);
$this->options = $resolver->resolve($options);
}
}
$database = new Database([
'host' => '192.168.1.200',
'username' => 'root',
'password' => 'root',
'dbname' => 'my-app',
'driver' => 'pdo_sqlite'
]);
// Uncaught InvalidArgumentException: The option "driver" with value "pdo_sqlite" is invalid.
Ideal for small project
Simple and easy!
https://github.com/devcoder-xyz/php-options-resolver
💖 💪 🙅 🚩
F.R Michel
Posted on June 13, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.