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