1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\DI\Config;
9:
10: use Nette;
11: use Nette\Utils\Validators;
12:
13:
14: 15: 16:
17: class Loader
18: {
19: use Nette\SmartObject;
20:
21:
22: const INCLUDES_KEY = 'includes';
23:
24: private $adapters = [
25: 'php' => Adapters\PhpAdapter::class,
26: 'ini' => Adapters\IniAdapter::class,
27: 'neon' => Adapters\NeonAdapter::class,
28: ];
29:
30: private $dependencies = [];
31:
32: private $loadedFiles = [];
33:
34:
35: 36: 37: 38: 39: 40:
41: public function load($file, $section = null)
42: {
43: if (!is_file($file) || !is_readable($file)) {
44: throw new Nette\FileNotFoundException("File '$file' is missing or is not readable.");
45: }
46:
47: if (isset($this->loadedFiles[$file])) {
48: throw new Nette\InvalidStateException("Recursive included file '$file'");
49: }
50: $this->loadedFiles[$file] = true;
51:
52: $this->dependencies[] = $file;
53: $data = $this->getAdapter($file)->load($file);
54:
55: if ($section) {
56: if (isset($data[self::INCLUDES_KEY])) {
57: throw new Nette\InvalidStateException("Section 'includes' must be placed under some top section in file '$file'.");
58: }
59: $data = $this->getSection($data, $section, $file);
60: }
61:
62:
63: $merged = [];
64: if (isset($data[self::INCLUDES_KEY])) {
65: Validators::assert($data[self::INCLUDES_KEY], 'list', "section 'includes' in file '$file'");
66: foreach ($data[self::INCLUDES_KEY] as $include) {
67: if (!preg_match('#([a-z]+:)?[/\\\\]#Ai', $include)) {
68: $include = dirname($file) . '/' . $include;
69: }
70: $merged = Helpers::merge($this->load($include), $merged);
71: }
72: }
73: unset($data[self::INCLUDES_KEY], $this->loadedFiles[$file]);
74:
75:
76: return Helpers::merge($data, $merged);
77: }
78:
79:
80: 81: 82: 83: 84: 85:
86: public function save($data, $file)
87: {
88: if (file_put_contents($file, $this->getAdapter($file)->dump($data)) === false) {
89: throw new Nette\IOException("Cannot write file '$file'.");
90: }
91: }
92:
93:
94: 95: 96: 97:
98: public function getDependencies()
99: {
100: return array_unique($this->dependencies);
101: }
102:
103:
104: 105: 106: 107: 108: 109:
110: public function addAdapter($extension, $adapter)
111: {
112: $this->adapters[strtolower($extension)] = $adapter;
113: return $this;
114: }
115:
116:
117:
118: private function getAdapter($file)
119: {
120: $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
121: if (!isset($this->adapters[$extension])) {
122: throw new Nette\InvalidArgumentException("Unknown file extension '$file'.");
123: }
124: return is_object($this->adapters[$extension]) ? $this->adapters[$extension] : new $this->adapters[$extension];
125: }
126:
127:
128: private function getSection(array $data, $key, $file)
129: {
130: Validators::assertField($data, $key, 'array|null', "section '%' in file '$file'");
131: $item = $data[$key];
132: if ($parent = Helpers::takeParent($item)) {
133: $item = Helpers::merge($item, $this->getSection($data, $parent, $file));
134: }
135: return $item;
136: }
137: }
138: