Completed
Pull Request — master (#79)
by Jan Philipp
02:48
created

XmlConfigFileLoader::extractNodes()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4222
c 0
b 0
f 0
cc 5
nc 6
nop 2
1
<?php declare(strict_types=1);
2
3
4
namespace Shopware\Psh\Config;
5
6
use DOMElement;
7
use DOMXPath;
8
use Symfony\Component\Config\Util\XmlUtils;
9
10
/**
11
 * Load the config data from an xml file
12
 */
13
class XmlConfigFileLoader extends ConfigFileLoader
14
{
15
    const NODE_HEADER = 'header';
16
17
    const NODE_PLACEHOLDER = 'placeholder';
18
19
    const NODE_PLACEHOLDER_DYNAMIC = 'dynamic';
20
21
    const NODE_PLACEHOLDER_CONST = 'const';
22
23
    const NODE_PATH = 'path';
24
25
    const NODE_ENVIRONMENT = 'environment';
26
27
    const NODE_TEMPLATE = 'template';
28
29
    const NODE_TEMPLATE_SOURCE = 'source';
30
31
    const NODE_TEMPLATE_DESTINATION = 'destination';
32
33
    /**
34
     * @var ConfigBuilder
35
     */
36
    private $configBuilder;
37
38
    /**
39
     * @var string
40
     */
41
    private $applicationRootDirectory;
42
43
    /**
44
     * @param ConfigBuilder $configBuilder
45
     * @param string $applicationRootDirectory
46
     */
47
    public function __construct(ConfigBuilder $configBuilder, string $applicationRootDirectory)
48
    {
49
        $this->configBuilder = $configBuilder;
50
        $this->applicationRootDirectory = $applicationRootDirectory;
51
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56
    public function isSupported(string $file): bool
57
    {
58
        return in_array(pathinfo($file, PATHINFO_BASENAME), ['.psh.xml', '.psh.xml.dist', '.psh.xml.override'], true);
59
    }
60
61
    /**
62
     * @inheritdoc
63
     */
64
    public function load(string $file, array $params): Config
65
    {
66
        $pshConfigNode = $this->loadXmlRoot($file);
67
        $this->configBuilder->start();
68
69
        try {
70
            $this->configBuilder->setHeader(
71
                $this->extractNode(self::NODE_HEADER, $pshConfigNode)->nodeValue
72
            );
73
        } catch (\InvalidArgumentException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
74
        }
75
76
        $this->setConfigData($file, $pshConfigNode);
77
78
        $environments = $this->extractNodes(self::NODE_ENVIRONMENT, $pshConfigNode);
79
80
        foreach ($environments as $node) {
81
            $this->configBuilder->start($node->getAttribute('name'));
82
            $this->setConfigData($file, $node);
83
        }
84
85
        return $this->configBuilder
86
            ->create($params);
87
    }
88
89
    /**
90
     * @param string $file
91
     * @param array $pshConfigNode
92
     */
93
    private function setConfigData(string $file, DOMElement $pshConfigNode)
94
    {
95
        $this->configBuilder->setCommandPaths(
96
            $this->extractCommandPaths($file, $pshConfigNode)
97
        );
98
99
        try {
100
            $this->extractPlaceholders(
101
                $this->extractNode(self::NODE_PLACEHOLDER, $pshConfigNode)
102
            );
103
        } catch (\InvalidArgumentException $e) {
104
            // nth
105
        }
106
107
        $this->configBuilder->setTemplates(
108
            $this->extractTemplates($file, $pshConfigNode)
109
        );
110
    }
111
112
    /**
113
     * @param string $key
114
     * @param DOMElement $parent
115
     * @return DOMElement[]
116
     */
117
    private function extractNodes(string $key, DOMElement $parent): array
118
    {
119
        $nodes = [];
120
121
        foreach ($parent->childNodes as $childNode) {
122
            if ($childNode instanceof DOMElement && $childNode->localName === $key) {
123
                $nodes[] = $childNode;
124
            }
125
        }
126
127
        if (0 === count($nodes)) {
128
            return [];
129
        }
130
131
        return $nodes;
132
    }
133
134
    /**
135
     * @param string $key
136
     * @param DOMElement $parent
137
     * @return DOMElement
138
     */
139
    private function extractNode(string $key, DOMElement $parent): DOMElement
140
    {
141
        $nodes = $this->extractNodes($key, $parent);
142
143
        if (count($nodes) === 0) {
144
            throw new \InvalidArgumentException('No node found for ' . $key);
145
        }
146
147
        return $nodes[0];
148
    }
149
150
    /**
151
     * @param string $file
152
     * @param $pshConfigNode
153
     * @return array
154
     */
155
    private function extractCommandPaths(string $file, DOMElement $pshConfigNode): array
156
    {
157
        $pathNodes = $this->extractNodes(self::NODE_PATH, $pshConfigNode);
158
159
        return array_map(function (DOMElement $path) use ($file) {
160
            return $this->fixPath($this->applicationRootDirectory, $path->nodeValue, $file);
161
        }, $pathNodes);
162
    }
163
164
    /**
165
     * @param string $file
166
     * @param array $pshConfigNodes
167
     * @return array
168
     */
169
    private function extractTemplates(string $file, DOMElement $pshConfigNodes): array
170
    {
171
        $templates = $this->extractNodes(self::NODE_TEMPLATE, $pshConfigNodes);
172
173
        return array_map(function (DOMElement $template) use ($file) {
174
            return [
175
                'source' => $this->fixPath(
176
                    $this->applicationRootDirectory,
177
                    $template->getAttribute(self::NODE_TEMPLATE_SOURCE),
178
                    $file
179
                ),
180
                'destination' => $this->makeAbsolutePath(
181
                    $file,
182
                    $template->getAttribute(self::NODE_TEMPLATE_DESTINATION)
183
                )
184
            ];
185
        }, $templates);
186
    }
187
188
    /**
189
     * @param DOMElement $placeholder
190
     */
191
    private function extractPlaceholders(DOMElement $placeholder)
192
    {
193
        foreach ($this->extractNodes(self::NODE_PLACEHOLDER_DYNAMIC, $placeholder) as $dynamic) {
194
            $this->configBuilder->setDynamicVariable($dynamic->getAttribute('name'), $dynamic->nodeValue);
195
        }
196
197
        foreach ($this->extractNodes(self::NODE_PLACEHOLDER_CONST, $placeholder) as $dynamic) {
198
            $this->configBuilder->setConstVariable($dynamic->getAttribute('name'), $dynamic->nodeValue);
199
        }
200
    }
201
202
    /**
203
     * @param string $file
204
     * @return DOMElement
205
     */
206
    private function loadXmlRoot(string $file): DOMElement
207
    {
208
        $xml = XmlUtils::loadFile($file);
209
        $xPath = new DOMXPath($xml);
210
211
        /** @var \DOMNodeList $pshConfigNodes */
212
        $pshConfigNodes = $xPath->query('//psh');
213
214
        if (false === $pshConfigNodes) {
215
            throw new \RuntimeException('Invalid file at ' . $file . ' found');
216
        }
217
218
        return $pshConfigNodes[0];
219
    }
220
}
221