AbstractConfigRepository::resolveDirectory()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 17
rs 10
1
<?php
2
3
namespace FigTree\Config;
4
5
use FigTree\Exceptions\{
6
	InvalidPathException,
7
	InvalidDirectoryException,
8
	UnreadablePathException,
9
};
10
use FigTree\Config\Contracts\{
11
	ConfigRepositoryInterface,
12
	ConfigFactoryInterface,
13
	ConfigInterface,
14
};
15
use FigTree\Config\Exceptions\InvalidConfigFilePathException;
16
17
abstract class AbstractConfigRepository implements ConfigRepositoryInterface
18
{
19
	protected ConfigFactoryInterface $factory;
20
21
	/**
22
	 * Config file directories.
23
	 *
24
	 * @var array
25
	 */
26
	protected $directories = [];
27
28
	/**
29
	 * Cached Config objects.
30
	 *
31
	 * @var array
32
	 */
33
	protected $configs = [];
34
35
	/**
36
	 * Get the directories to search for a Config file.
37
	 *
38
	 * @return array
39
	 */
40
	public function getDirectories(): array
41
	{
42
		return $this->directories;
43
	}
44
45
	/**
46
	 * Add a directory to search for a Config file.
47
	 *
48
	 * @param string $directory
49
	 *
50
	 * @return $this
51
	 */
52
	public function addDirectory(string $directory): ConfigRepositoryInterface
53
	{
54
		$dir = $this->resolveDirectory($directory);
55
56
		$this->directories[] = $dir;
57
58
		return $this;
59
	}
60
61
	/**
62
	 * Search for a Config file from the list of applicable directories.
63
	 * Returns null if it could not be found.
64
	 *
65
	 * @param string $fileName
66
	 *
67
	 * @return \FigTree\Config\Contracts\ConfigInterface|null
68
	 */
69
	public function get(string $fileName): ?ConfigInterface
70
	{
71
		if (key_exists($fileName, $this->configs)) {
72
			return $this->configs[$fileName];
73
		}
74
75
		$paths = $this->search($fileName);
76
77
		if (empty($paths)) {
78
			return null;
79
		}
80
81
		return $this->configs[$fileName] = $this->factory->create($paths);
82
	}
83
84
	/**
85
	 * Find all instances of the given file within configured directories.
86
	 *
87
	 * @param string $fileName
88
	 *
89
	 * @return array
90
	 */
91
	protected function search(string $fileName): array
92
	{
93
		$paths = [];
94
95
		foreach ($this->directories as $directory) {
96
			$fullPath = $this->resolveFile($directory, $fileName . '.php');
97
98
			if (!empty($fullPath)) {
99
				$paths[] = $fullPath;
100
			}
101
		}
102
103
		return $paths;
104
	}
105
106
	/**
107
	 * Resolve a given directory to an absolute path.
108
	 *
109
	 * @param string $directory
110
	 *
111
	 * @return string
112
	 *
113
	 * @throws \FigTree\Exceptions\InvalidDirectoryException
114
	 * @throws \FigTree\Exceptions\InvalidPathException
115
	 * @throws \FigTree\Exceptions\UnreadablePathException
116
	 */
117
	protected function resolveDirectory(string $directory): string
118
	{
119
		$dir = realpath($directory);
120
121
		if (empty($dir)) {
122
			throw new InvalidPathException($directory);
123
		}
124
125
		if (!is_dir($dir)) {
126
			throw new InvalidDirectoryException($directory);
127
		}
128
129
		if (!is_readable($dir)) {
130
			throw new UnreadablePathException(sprintf('Directory %s is not readable.', $dir));
131
		}
132
133
		return $dir;
134
	}
135
136
	/**
137
	 * Resolve a file in a given directory to an absolute path,
138
	 * additionally ensuring the file exists within that directory.
139
	 *
140
	 * @param string $directory
141
	 * @param string $file
142
	 *
143
	 * @return string|null
144
	 *
145
	 * @throws \FigTree\Config\Exceptions\InvalidConfigFilePathException
146
	 */
147
	protected function resolveFile(string $directory, string $file): ?string
148
	{
149
		$prefix = $directory . DIRECTORY_SEPARATOR;
150
151
		$path = realpath($prefix . $file);
152
153
		if (empty($path) || !is_file($path) || !is_readable($path)) {
154
			return null;
155
		}
156
157
		if (!str_starts_with($path, $prefix)) {
158
			throw new InvalidConfigFilePathException($file);
159
		}
160
161
		return $path;
162
	}
163
}
164