Passed
Push — master ( 0a1f9b...331454 )
by Radovan
02:05
created

EnvironmentAdapter::extractStatementAttributes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nc 4
nop 3
dl 0
loc 17
rs 9.9
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Mallgroup\DI\Config\Adapters;
5
6
use Mallgroup\Environment;
7
use Nette\DI\Config\Adapter;
8
use Nette\DI\Definitions\Statement;
9
use Nette\DI\InvalidConfigurationException;
10
use Nette\InvalidArgumentException;
11
use Nette\Neon\Entity;
12
use Nette\Neon\Neon;
13
use Nette\Utils\FileSystem;
14
use ReflectionException;
15
use ReflectionMethod;
16
use function array_walk_recursive;
17
use function gettype;
18
use function strtoupper;
19
use function substr;
20
21
class EnvironmentAdapter implements Adapter
22
{
23
	/**
24
	 * Reads configuration from PHP file.
25
	 * @return array<string, array<string, array<string, mixed>>>
26
	 */
27
	public function load(string $file): array
28
	{
29
		/** @var array<string, mixed> $data */
30
		$data = (array)Neon::decode(FileSystem::read($file));
31
		return $this->process($data);
32
	}
33
34
	/**
35
	 * @param array<string,mixed> $data
36
	 * @return array<string, array<string, array<string, mixed>>>
37
	 * @throws ReflectionException
38
	 */
39
	protected function process(array $data): array
40
	{
41
		$envs = [];
42
43
		foreach ($data as $name => $entity) {
44
			if (!$entity instanceof Entity) {
45
				throw new InvalidConfigurationException(
46
					"Invalid argument type ({$name}). Expected Entity, got " . gettype($entity)
47
				);
48
			}
49
50
			$name = strtoupper($name);
51
			$var = strtolower($name);
52
			$type = $this->getEntityType($entity);
53
			$attributes = $this->getAttributes($entity->attributes);
54
55
			if ($entity->attributes['hidden'] ?? $entity->attributes[1] ?? false) {
56
				$envs[$var] = new Statement("Mallgroup\Environment::$type", $this->extractStatementAttributes($name, $type, $attributes));
57
			} elseif ($type === 'array') {
58
				$envs[$var] = Environment::array($name, $attributes['separator'] ?: '|', $attributes['cast']);
59
			} else {
60
				$envs[$var] = (new Environment($name, $attributes['default'] ?? ''))->get($type);
61
			}
62
		}
63
		return ['parameters' => ['env' => $envs]];
64
	}
65
66
	/**
67
	 * @param string $name
68
	 * @param string $type
69
	 * @param array<"cast"|"default"|"separator", string> $arguments
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<"cast"|"default"|"separator", string> at position 2 could not be parsed: Unknown type name '"cast"' at position 2 in array<"cast"|"default"|"separator", string>.
Loading history...
70
	 * @return array<string, string>
71
	 * @throws ReflectionException
72
	 */
73
	protected function extractStatementAttributes(string $name, string $type, array $arguments): array {
74
		$return = [];
75
		$arguments['name'] = $name;
76
77
		$reflection = new ReflectionMethod(Environment::class, $type);
78
		foreach($reflection->getParameters() as $parameter) {
79
			$name = $parameter->getName();
80
			if (!isset($arguments[$name])) {
81
				if (!$parameter->isOptional()) {
82
					throw new InvalidArgumentException('Required argument ' . $name . ' not found.');
83
				}
84
				continue;
85
			}
86
			$return[$name] = $arguments[$name];
87
		}
88
89
		return $return;
90
	}
91
92
	protected function getEntityType(Entity $entity): string
93
	{
94
		$type = substr((string)$entity->value, 2);
95
		return match ($type) {
96
			Environment::INTEGER, Environment::INT, Environment::BOOLEAN, Environment::BOOL, Environment::FLOAT, 'array' => $type,
97
			default => Environment::STRING
98
		};
99
	}
100
101
	/**
102
	 * @param array<int|string, mixed> $arguments
103
	 * @return array<"cast"|"default"|"separator", string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<"cast"|"default"|"separator", string> at position 2 could not be parsed: Unknown type name '"cast"' at position 2 in array<"cast"|"default"|"separator", string>.
Loading history...
104
	 */
105
	protected function getAttributes(array $arguments): array
106
	{
107
		return array_filter([
108
			'separator' => (string)($arguments['separator'] ?? $arguments[0] ?? '|'),
109
			'cast' => (string)($arguments['cast'] ?? $arguments[2] ?? Environment::STRING),
110
			'default' => (string)($arguments['default'] ?? $arguments[0] ?? ''),
111
		], static fn($item) => $item !== '');
112
	}
113
114
	/**
115
	 * Generates configuration in PHP format.
116
	 * @param mixed[] $data
117
	 */
118
	public function dump(array $data): string
119
	{
120
		$data = (array)($data['parameters']['env'] ?? []);
121
		array_walk_recursive(
122
			$data,
123
			static function (mixed &$val): void {
124
				if ($val instanceof Statement && $entity = $val->getEntity()) {
125
					$arguments = [
126
						'hidden' => true,
127
					];
128
					/** @var array<"cast"|"default"|"separator", string> $args */
129
					$args = $val->arguments;
130
131
					if (is_array($entity) && $entity[0] === Environment::class) {
132
						$type = (string)($entity[1] ?? 'string');
133
134
						if ($type === 'array') {
135
							$arguments['separator'] = $args['separator'];
136
							$arguments['cast'] = $args['cast'];
137
						} else {
138
							$arguments['default'] = $args['default'];
139
						}
140
						$entity = '::' . $type;
141
					}
142
143
					$val = new Entity($entity, $arguments);
144
				}
145
			},
146
		);
147
		return "# generated by Nette\n\n" . Neon::encode($data, Neon::BLOCK);
148
	}
149
}
150