Skip to content

Commit 86a569a

Browse files
committed
[ENH] Impelement OptionsResolver module
0 parents  commit 86a569a

File tree

8 files changed

+301
-0
lines changed

8 files changed

+301
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vendor

.idea/.gitignore

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

composer.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "wepesi/optionsresolver",
3+
"description": "The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.",
4+
"type": "library",
5+
"license": "Apache-2.0",
6+
"autoload": {
7+
"psr-4": {
8+
"Wepesi\\Resolver\\": "src/"
9+
}
10+
},
11+
"authors": [
12+
{
13+
"name": "bim-g",
14+
"email": "ibmussafb@gmail.com"
15+
}
16+
],
17+
"minimum-stability": "dev",
18+
"require": {
19+
"php" : ">=7.4"
20+
}
21+
}

demo/Database.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/*
3+
* Copyright (c) 2022.
4+
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
5+
*/
6+
7+
namespace Wepesi\Demo;
8+
9+
use Wepesi\Resolver\Option;
10+
use Wepesi\Resolver\OptionsResolver;
11+
12+
class Database
13+
{
14+
public array $options;
15+
16+
public function __construct(array $options = [])
17+
{
18+
$resolver = new OptionsResolver([
19+
(new Option('host'))->setDefaultValue('localhost'),
20+
new Option('username'),
21+
new Option('password'),
22+
new Option('dbname'),
23+
]);
24+
25+
$this->options = $resolver->resolve($options);
26+
}
27+
}

demo/index.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
/*
3+
* Copyright (c) 2022.
4+
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
5+
*/
6+
7+
use Wepesi\Demo\Database;
8+
9+
include __DIR__."/../vendor/autoload.php";
10+
include __DIR__."/Database.php";
11+
12+
$database = new Database([
13+
'dbname' => 'app',
14+
]);
15+
// Uncaught InvalidArgumentException: The required option "username" is missing.
16+
17+
// $database = new Database([
18+
// 'host' => 'localhost',
19+
// 'dbname' => 'app',
20+
// 'username' => 'root',
21+
// 'password' => 'root',
22+
// ]);
23+
var_dump($database->options);

src/Option.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
/*
3+
* Copyright (c) 2022.
4+
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
5+
*/
6+
7+
namespace Wepesi\Resolver;
8+
9+
final class Option
10+
{
11+
/**
12+
* @var string
13+
*/
14+
private string $name;
15+
16+
/**
17+
* @var mixed
18+
*/
19+
private $defaultValue;
20+
21+
/**
22+
* @var bool
23+
*/
24+
private bool $hasDefaultValue;
25+
26+
/**
27+
* @var \Closure|null
28+
*/
29+
private ?\Closure $validator;
30+
31+
/**
32+
* Option constructor.
33+
* @param string $name
34+
*/
35+
public function __construct(string $name)
36+
{
37+
$this->name = $name;
38+
}
39+
40+
public function getName(): string
41+
{
42+
$this->hasDefaultValue = false;
43+
return $this->name;
44+
}
45+
46+
/**
47+
* @return mixed
48+
*/
49+
public function getDefaultValue()
50+
{
51+
return $this->defaultValue;
52+
}
53+
54+
/**
55+
* @param mixed $defaultValue
56+
* @return Option
57+
*/
58+
public function setDefaultValue($defaultValue): self
59+
{
60+
$this->hasDefaultValue = true;
61+
$this->defaultValue = $defaultValue;
62+
return $this;
63+
}
64+
65+
public function hasDefaultValue(): bool
66+
{
67+
return $this->hasDefaultValue;
68+
}
69+
70+
public function validator(\Closure $closure): self
71+
{
72+
$this->validator = $closure;
73+
return $this;
74+
}
75+
76+
public function isValid($value): bool
77+
{
78+
if ($this->validator instanceof \Closure) {
79+
$validator = $this->validator;
80+
return $validator($value);
81+
}
82+
return true;
83+
}
84+
}

src/OptionsResolver.php

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
/*
3+
* Copyright (c) 2022.
4+
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
5+
*/
6+
7+
namespace Wepesi\Resolver;
8+
9+
use Wepesi\Resolver\Traits\ExceptionTraits;
10+
11+
final class OptionsResolver
12+
{
13+
/**
14+
* @var \ArrayObject
15+
*/
16+
private \ArrayObject $options;
17+
18+
use ExceptionTraits;
19+
20+
/**
21+
* @param array $options
22+
*/
23+
public function __construct(array $options)
24+
{
25+
$this->options = new \ArrayObject();
26+
foreach ($options as $option) {
27+
$this->add($option);
28+
}
29+
}
30+
31+
/**
32+
* @param array $options
33+
* @return array
34+
*/
35+
public function resolve(array $options): array
36+
{
37+
try {
38+
$checkDiff = $this->checkDiff($options);
39+
if(isset($checkDiff['exception'])){
40+
return $checkDiff;
41+
}
42+
/**
43+
* @var Option $option
44+
*/
45+
$optionsResolved = [];
46+
foreach ($this->options as $option) {
47+
$optionName = $option->getName();
48+
if (\array_key_exists($optionName, $options)) {
49+
$value = $options[$optionName];
50+
if ($option->isValid($value) === false) {
51+
throw new \InvalidArgumentException(sprintf('The option "%s" with value %s is invalid.', $optionName, self::formatValue($value)));
52+
}
53+
$optionsResolved[$optionName] = $value;
54+
continue;
55+
}
56+
57+
if ($option->hasDefaultValue()) {
58+
$optionsResolved[$optionName] = $option->getDefaultValue();
59+
continue;
60+
}
61+
throw new \InvalidArgumentException(sprintf('The required option "%s" is missing.', $optionName));
62+
}
63+
return $optionsResolved;
64+
} catch (\Exception $ex) {
65+
return $this->exception($ex);
66+
}
67+
}
68+
69+
/**
70+
* @param Option $option
71+
* @return void
72+
*/
73+
private function add(Option $option): void
74+
{
75+
$this->options->offsetSet($option->getName(), $option);
76+
}
77+
78+
/**
79+
* @param array $options
80+
* @return array
81+
*/
82+
private function checkDiff(array $options): array
83+
{
84+
try {
85+
$defined = $this->options->getArrayCopy();
86+
$diff = array_diff_key($options, $defined);
87+
if (count($diff) > 0) {
88+
$arr_diff = implode(', ', array_keys($diff));
89+
$arr_defined = implode('", "', array_keys($defined));
90+
$error_message = sprintf('The option(s) "%s" do(es) not exist. Defined options are: "%s".',$arr_diff ,$arr_defined);
91+
throw new \InvalidArgumentException($error_message);
92+
}
93+
return [];
94+
} catch (\Exception $ex) {
95+
return $this->exception($ex);
96+
}
97+
}
98+
99+
/**
100+
* @param $value
101+
* @return string
102+
*/
103+
private static function formatValue($value): string
104+
{
105+
if (is_object($value)) {
106+
return \get_class($value);
107+
}
108+
109+
if (is_string($value)) {
110+
return '"' . $value . '"';
111+
}
112+
113+
if (false === $value) {
114+
return 'false';
115+
}
116+
117+
if (true === $value) {
118+
return 'true';
119+
}
120+
return \gettype($value);
121+
}
122+
}

src/Traits/ExceptionTraits.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
/*
3+
* Copyright (c) 2022.
4+
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
5+
*/
6+
7+
namespace Wepesi\Resolver\Traits;
8+
9+
trait ExceptionTraits
10+
{
11+
protected function exception($ex):array
12+
{
13+
return [get_class($ex) => $ex->getMessage()];
14+
}
15+
}

0 commit comments

Comments
 (0)