Skip to content

Commit

Permalink
[ENH] Impelement OptionsResolver module
Browse files Browse the repository at this point in the history
  • Loading branch information
bim-g committed Dec 29, 2022
0 parents commit 86a569a
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
vendor
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "wepesi/optionsresolver",
"description": "The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.",
"type": "library",
"license": "Apache-2.0",
"autoload": {
"psr-4": {
"Wepesi\\Resolver\\": "src/"
}
},
"authors": [
{
"name": "bim-g",
"email": "[email protected]"
}
],
"minimum-stability": "dev",
"require": {
"php" : ">=7.4"
}
}
27 changes: 27 additions & 0 deletions demo/Database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
/*
* Copyright (c) 2022.
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
*/

namespace Wepesi\Demo;

use Wepesi\Resolver\Option;
use Wepesi\Resolver\OptionsResolver;

class Database
{
public array $options;

public function __construct(array $options = [])
{
$resolver = new OptionsResolver([
(new Option('host'))->setDefaultValue('localhost'),
new Option('username'),
new Option('password'),
new Option('dbname'),
]);

$this->options = $resolver->resolve($options);
}
}
23 changes: 23 additions & 0 deletions demo/index.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php
/*
* Copyright (c) 2022.
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
*/

use Wepesi\Demo\Database;

include __DIR__."/../vendor/autoload.php";
include __DIR__."/Database.php";

$database = new Database([
'dbname' => 'app',
]);
// Uncaught InvalidArgumentException: The required option "username" is missing.

// $database = new Database([
// 'host' => 'localhost',
// 'dbname' => 'app',
// 'username' => 'root',
// 'password' => 'root',
// ]);
var_dump($database->options);
84 changes: 84 additions & 0 deletions src/Option.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php
/*
* Copyright (c) 2022.
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
*/

namespace Wepesi\Resolver;

final class Option
{
/**
* @var string
*/
private string $name;

/**
* @var mixed
*/
private $defaultValue;

/**
* @var bool
*/
private bool $hasDefaultValue;

/**
* @var \Closure|null
*/
private ?\Closure $validator;

/**
* Option constructor.
* @param string $name
*/
public function __construct(string $name)
{
$this->name = $name;
}

public function getName(): string
{
$this->hasDefaultValue = false;
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;
}
}
122 changes: 122 additions & 0 deletions src/OptionsResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php
/*
* Copyright (c) 2022.
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
*/

namespace Wepesi\Resolver;

use Wepesi\Resolver\Traits\ExceptionTraits;

final class OptionsResolver
{
/**
* @var \ArrayObject
*/
private \ArrayObject $options;

use ExceptionTraits;

/**
* @param array $options
*/
public function __construct(array $options)
{
$this->options = new \ArrayObject();
foreach ($options as $option) {
$this->add($option);
}
}

/**
* @param array $options
* @return array
*/
public function resolve(array $options): array
{
try {
$checkDiff = $this->checkDiff($options);
if(isset($checkDiff['exception'])){
return $checkDiff;
}
/**
* @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;
} catch (\Exception $ex) {
return $this->exception($ex);
}
}

/**
* @param Option $option
* @return void
*/
private function add(Option $option): void
{
$this->options->offsetSet($option->getName(), $option);
}

/**
* @param array $options
* @return array
*/
private function checkDiff(array $options): array
{
try {
$defined = $this->options->getArrayCopy();
$diff = array_diff_key($options, $defined);
if (count($diff) > 0) {
$arr_diff = implode(', ', array_keys($diff));
$arr_defined = implode('", "', array_keys($defined));
$error_message = sprintf('The option(s) "%s" do(es) not exist. Defined options are: "%s".',$arr_diff ,$arr_defined);
throw new \InvalidArgumentException($error_message);
}
return [];
} catch (\Exception $ex) {
return $this->exception($ex);
}
}

/**
* @param $value
* @return string
*/
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);
}
}
15 changes: 15 additions & 0 deletions src/Traits/ExceptionTraits.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
/*
* Copyright (c) 2022.
* The OptionsResolver component helps you configure objects with option arrays. It supports default values, option constraints and lazy options.
*/

namespace Wepesi\Resolver\Traits;

trait ExceptionTraits
{
protected function exception($ex):array
{
return [get_class($ex) => $ex->getMessage()];
}
}

0 comments on commit 86a569a

Please sign in to comment.