EnvironmentAdapter::extractStatementAttributes()   A
last analyzed

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
	 * @throws ReflectionException
27
	 */
28
	public function load(string $file): array
29
	{
30
		/** @var array<string, mixed> $data */
31
		$data = (array)Neon::decode(FileSystem::read($file));
32
		return $this->process($data);
33
	}
34
35
	/**
36
	 * @param array<string,mixed> $data
37
	 * @return array<string, array<string, array<string, mixed>>>
38
	 * @throws ReflectionException
39
	 */
40
	protected function process(array $data): array
41
	{
42
		$envs = [];
43
44
		foreach ($data as $name => $entity) {
45
			if (!$entity instanceof Entity) {
46
				throw new InvalidConfigurationException(
47
					"Invalid argument type ({$name}). Expected Entity, got " . gettype($entity)
48
				);
49
			}
50
51
			$name = strtoupper($name);
52
			$var = strtolower($name);
53
			$type = $this->getEntityType($entity);
54
			$attributes = $this->getAttributes($entity->attributes);
55
56
			if ($entity->attributes['hidden'] ?? $entity->attributes[1] ?? false) {
57
				$envs[$var] = new Statement("Mallgroup\Environment::$type", $this->extractStatementAttributes($name, $type, $attributes));
58
			} elseif ($type === 'array') {
59
				$envs[$var] = Environment::array($name, $attributes['separator'] ?: '|', $attributes['cast']);
60
			} else {
61
				$envs[$var] = (new Environment($name, $attributes['default'] ?? ''))->get($type);
62
			}
63
		}
64
		return ['parameters' => ['env' => $envs]];
65
	}
66
67
	/**
68
	 * @param string $name
69
	 * @param string $type
70
	 * @param array{separator: string, cast: string, default: string} $arguments
71
	 * @return array<string, string>
72
	 * @throws ReflectionException
73
	 */
74
	protected function extractStatementAttributes(string $name, string $type, array $arguments): array {
75
		$return = [];
76
		$arguments['name'] = $name;
77
78
		$reflection = new ReflectionMethod(Environment::class, $type);
79
		foreach($reflection->getParameters() as $parameter) {
80
			$name = $parameter->getName();
81
			if (!isset($arguments[$name])) {
82
				if (!$parameter->isOptional()) {
83
					throw new InvalidArgumentException('Required argument ' . $name . ' not found.');
84
				}
85
				continue;
86
			}
87
			$return[$name] = $arguments[$name];
88
		}
89
90
		return $return;
91
	}
92
93
	protected function getEntityType(Entity $entity): string
94
	{
95
		$type = substr((string)$entity->value, 2);
96
		return match ($type) {
97
			Environment::INTEGER, Environment::INT, Environment::BOOLEAN, Environment::BOOL, Environment::FLOAT, 'array' => $type,
98
			default => Environment::STRING
99
		};
100
	}
101
102
	/**
103
	 * @param array<int|string, mixed> $arguments
104
	 * @return array{separator: string, cast: string, default: string}
105
	 */
106
	protected function getAttributes(array $arguments): array
107
	{
108
		return array_filter([
109
			'separator' => (string)($arguments['separator'] ?? $arguments[0] ?? '|'),
110
			'cast' => (string)($arguments['cast'] ?? $arguments[2] ?? Environment::STRING),
111
			'default' => (string)($arguments['default'] ?? $arguments[0] ?? ''),
112
		], static fn($item) => $item !== '');
113
	}
114
115
	/**
116
	 * Generates configuration in PHP format.
117
	 * @param mixed[] $data
118
	 */
119
	public function dump(array $data): string
120
	{
121
		$data = (array)($data['parameters']['env'] ?? []);
122
		array_walk_recursive(
123
			$data,
124
			static function (mixed &$val): void {
125
				if ($val instanceof Statement && $entity = $val->getEntity()) {
126
					$arguments = [
127
						'hidden' => true,
128
					];
129
					/** @var array<"cast"|"default"|"separator", string> $args */
130
					$args = $val->arguments;
131
132
					if (is_array($entity) && $entity[0] === Environment::class) {
133
						$type = (string)($entity[1] ?? 'string');
134
135
						if ($type === 'array') {
136
							$arguments['separator'] = $args['separator'];
137
							$arguments['cast'] = $args['cast'];
138
						} else {
139
							$arguments['default'] = $args['default'];
140
						}
141
						$entity = '::' . $type;
142
					}
143
144
					$val = new Entity($entity, $arguments);
145
				}
146
			},
147
		);
148
		return "# generated by Nette\n\n" . Neon::encode($data, Neon::BLOCK);
149
	}
150
}
151