Passed
Push — master ( 229462...d74785 )
by Andreas
02:20 queued 14s
created

XmlFileLoader::load()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 17
c 1
b 0
f 0
nc 5
nop 1
dl 0
loc 30
ccs 18
cts 18
cp 1
crap 4
rs 9.7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Migrations\Configuration\Loader;
6
7
use Doctrine\Migrations\Configuration\Configuration;
8
use Doctrine\Migrations\Configuration\Exception\FileNotFound;
9
use Doctrine\Migrations\Configuration\Exception\XmlNotValid;
10
use Doctrine\Migrations\Tools\BooleanStringFormatter;
11
use DOMDocument;
12
use SimpleXMLElement;
13
use const DIRECTORY_SEPARATOR;
14
use const LIBXML_NOCDATA;
15
use function assert;
16
use function file_exists;
17
use function file_get_contents;
18
use function libxml_clear_errors;
19
use function libxml_use_internal_errors;
20
use function simplexml_load_string;
21
use function strtr;
22
23
/**
24
 * @internal
25
 */
26
final class XmlFileLoader extends AbstractFileLoader
27
{
28
    /** @var ArrayLoader */
29
    private $arrayLoader;
30
31 12
    public function __construct()
32
    {
33 12
        $this->arrayLoader = new ArrayLoader();
34 12
    }
35
36
    /**
37
     * @param mixed|string $file
38
     */
39 10
    public function load($file) : Configuration
40
    {
41 10
        if (! file_exists($file)) {
42 1
            throw FileNotFound::new();
43
        }
44
45 9
        $this->validateXml($file);
46
47 6
        $rawXML = file_get_contents($file);
48 6
        assert($rawXML !== false);
49
50 6
        $root = simplexml_load_string($rawXML, SimpleXMLElement::class, LIBXML_NOCDATA);
51 6
        assert($root !== false);
52
53 6
        $config = $this->extractParameters($root, true);
54
55 6
        if (isset($config['all_or_nothing'])) {
56 2
            $config['all_or_nothing'] = BooleanStringFormatter::toBoolean(
57 2
                $config['all_or_nothing'],
58 2
                false
59
            );
60
        }
61 6
        if (isset($config['migrations_paths'])) {
62 3
            $config['migrations_paths'] = $this->getDirectoryRelativeToFile(
63 3
                $file,
64 3
                $config['migrations_paths']
65
            );
66
        }
67
68 6
        return $this->arrayLoader->load($config);
69
    }
70
71
    /**
72
     * @return mixed[]
73
     */
74 6
    private function extractParameters(SimpleXMLElement $root, bool $loopOverNodes) : array
75
    {
76 6
        $config = [];
77
78 6
        $itemsToCheck = $loopOverNodes ? $root->children() : $root->attributes();
79
80 6
        if (! ($itemsToCheck instanceof SimpleXMLElement)) {
0 ignored issues
show
introduced by
$itemsToCheck is always a sub-type of SimpleXMLElement.
Loading history...
81
            return $config;
82
        }
83 6
        foreach ($itemsToCheck as $node) {
84 6
            $nodeName = strtr($node->getName(), '-', '_');
85 6
            if ($nodeName === 'migrations_paths') {
86 3
                $config['migrations_paths'] = [];
87 3
                foreach ($node->{'path'} as $pathNode) {
88 3
                    $config['migrations_paths'][(string) $pathNode['namespace']] = (string) $pathNode;
89
                }
90 5
            } elseif ($nodeName === 'storage' && $node->{'table-storage'} instanceof SimpleXMLElement) {
91 2
                $config['table_storage'] = $this->extractParameters($node->{'table-storage'}, false);
92 5
            } elseif ($nodeName === 'migrations') {
93 2
                $config['migrations'] = [];
94 2
                foreach ($node->{'migration'} as $pathNode) {
95 2
                    $config['migrations'][] = (string) $pathNode;
96
                }
97
            } else {
98 5
                $config[$nodeName] = (string) $node;
99
            }
100
        }
101
102 6
        return $config;
103
    }
104
105 9
    private function validateXml(string $file) : void
106
    {
107
        try {
108 9
            libxml_use_internal_errors(true);
109
110 9
            $xml = new DOMDocument();
111
112 9
            if ($xml->load($file) === false) {
113 1
                throw XmlNotValid::malformed();
114
            }
115
116 8
            $xsdPath = __DIR__ . DIRECTORY_SEPARATOR . 'XML' . DIRECTORY_SEPARATOR . 'configuration.xsd';
117
118 8
            if ($xml->schemaValidate($xsdPath) === false) {
119 2
                throw XmlNotValid::failedValidation();
120
            }
121 6
        } finally {
122 9
            libxml_clear_errors();
123 9
            libxml_use_internal_errors(false);
124
        }
125 6
    }
126
}
127