• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JBZoo / Utils / 29770052588

20 Jul 2026 06:55PM UTC coverage: 92.619% (-0.1%) from 92.722%
29770052588

push

github

web-flow
Merge pull request #57 from JBZoo/release/8.0

8.0.0 — PHP 8.3+ floor & lock-step major

15 of 16 new or added lines in 7 files covered. (93.75%)

106 existing lines in 13 files now uncovered.

1669 of 1802 relevant lines covered (92.62%)

41.2 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.0
/src/Xml.php
1
<?php
2

3
/**
4
 * JBZoo Toolbox - Utils.
5
 *
6
 * This file is part of the JBZoo Toolbox project.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT
11
 * @copyright  Copyright (C) JBZoo.com, All rights reserved.
12
 * @see        https://github.com/JBZoo/Utils
13
 */
14

15
declare(strict_types=1);
16

17
namespace JBZoo\Utils;
18

19
/**
20
 * @psalm-suppress UnusedClass
21
 */
22
final class Xml
23
{
24
    public const VERSION  = '1.0';
25
    public const ENCODING = 'UTF-8';
26

27
    /**
28
     * Escape string before save it as xml content.
29
     */
30
    public static function escape(float|int|string|null $rawXmlContent): string
31
    {
32
        $rawXmlContent = (string)$rawXmlContent;
18✔
33

34
        $rawXmlContent = (string)\preg_replace(
18✔
35
            '/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u',
18✔
36
            ' ',
18✔
37
            $rawXmlContent,
18✔
38
        );
18✔
39

40
        return \str_replace(
18✔
41
            ['&', '<', '>', '"', "'"],
18✔
42
            ['&amp;', '&lt;', '&gt;', '&quot;', '&apos;'],
18✔
43
            $rawXmlContent,
18✔
44
        );
18✔
45
    }
46

47
    /**
48
     * Create DOMDocument object from XML-string.
49
     */
50
    public static function createFromString(?string $source = null, bool $preserveWhiteSpace = false): \DOMDocument
51
    {
52
        $document = new \DOMDocument();
24✔
53

54
        $document->preserveWhiteSpace = $preserveWhiteSpace;
24✔
55

56
        if ($source !== '' && $source !== null) {
24✔
57
            $document->loadXML($source);
18✔
58
        }
59

60
        $document->xmlVersion   = self::VERSION;
24✔
61
        $document->encoding     = self::ENCODING;
24✔
62
        $document->formatOutput = true;
24✔
63

64
        return $document;
24✔
65
    }
66

67
    /**
68
     * Convert array to PHP DOMDocument object.
69
     * Format of input array
70
     * $source = [
71
     *     '_node'     => '#document',
72
     *     '_text'     => null,
73
     *     '_cdata'    => null,
74
     *     '_attrs'    => [],
75
     *     '_children' => [
76
     *         [
77
     *             '_node'     => 'parent',
78
     *             '_text'     => "Content of parent tag",
79
     *             '_cdata'    => null,
80
     *             '_attrs'    => ['parent-attribute' => 'value'],
81
     *             '_children' => [
82
     *                 [
83
     *                     '_node'     => 'child',
84
     *                     '_text'     => "Content of child tag",
85
     *                     '_cdata'    => null,
86
     *                     '_attrs'    => [],
87
     *                     '_children' => [],
88
     *                 ],
89
     *             ]
90
     *         ]
91
     *     ]
92
     * ];.
93
     *
94
     * Format of output
95
     *     <?xml version="1.0" encoding="UTF-8"?>
96
     *     <parent parent-attribute="value">Content of parent tag<child>Content of child tag</child></parent>
97
     *
98
     * @SuppressWarnings(PHPMD.NPathComplexity)
99
     * @suppress PhanPossiblyFalseTypeArgumentInternal
100
     */
101
    public static function array2Dom(
102
        array $xmlAsArray,
103
        ?\DOMElement $domElement = null,
104
        ?\DOMDocument $document = null,
105
    ): \DOMDocument {
106
        if ($document === null) {
24✔
107
            $document = self::createFromString();
24✔
108
        }
109

110
        $domElement ??= $document;
24✔
111

112
        if (\array_key_exists('_text', $xmlAsArray) && $xmlAsArray['_text'] !== null) {
24✔
113
            $domElement->appendChild(new \DOMText($xmlAsArray['_text']));
24✔
114
        }
115

116
        if (\array_key_exists('_cdata', $xmlAsArray) && $xmlAsArray['_cdata'] !== null) {
24✔
UNCOV
117
            $domElement->appendChild($document->createCDATASection($xmlAsArray['_cdata']));
×
118
        }
119

120
        if ($domElement instanceof \DOMElement && \array_key_exists('_attrs', $xmlAsArray)) {
24✔
121
            foreach ($xmlAsArray['_attrs'] as $name => $value) {
24✔
122
                $domElement->setAttribute($name, $value);
24✔
123
            }
124
        }
125

126
        if (\array_key_exists('_children', $xmlAsArray)) {
24✔
127
            foreach ($xmlAsArray['_children'] as $mixedElement) {
24✔
128
                if (\array_key_exists('_node', $mixedElement) && $mixedElement['_node'][0] !== '#') {
24✔
129
                    $newNode = $document->createElement($mixedElement['_node']);
24✔
130
                    $domElement->appendChild($newNode);
24✔
131

132
                    /** @phan-suppress-next-line PhanPossiblyFalseTypeArgument */
133
                    self::array2Dom($mixedElement, $newNode, $document);
24✔
134
                }
135
            }
136
        }
137

138
        return $document;
24✔
139
    }
140

141
    /**
142
     * Convert PHP \DOMDocument or \DOMNode object to simple array
143
     * Format of input XML (as string)
144
     *     <?xml version="1.0" encoding="UTF-8"?>
145
     *     <parent parent-attribute="value">Content of parent tag<child>Content of child tag</child></parent>.
146
     *
147
     * Format of output array
148
     * $result = [
149
     *     '_node'     => '#document',
150
     *     '_text'     => null,
151
     *     '_cdata'    => null,
152
     *     '_attrs'    => [],
153
     *     '_children' => [
154
     *         [
155
     *             '_node'     => 'parent',
156
     *             '_text'     => "Content of parent tag",
157
     *             '_cdata'    => null,
158
     *             '_attrs'    => ['parent-attribute' => 'value'],
159
     *             '_children' => [
160
     *                 [
161
     *                     '_node'     => 'child',
162
     *                     '_text'     => "Content of child tag",
163
     *                     '_cdata'    => null,
164
     *                     '_attrs'    => [],
165
     *                     '_children' => [],
166
     *                 ],
167
     *             ]
168
     *         ]
169
     *     ]
170
     * ];
171
     *
172
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
173
     */
174
    public static function dom2Array(\DOMNode $element): array
175
    {
176
        $result = [
18✔
177
            '_node'     => $element->nodeName,
18✔
178
            '_text'     => null,
18✔
179
            '_cdata'    => null,
18✔
180
            '_attrs'    => [],
18✔
181
            '_children' => [],
18✔
182
        ];
18✔
183

184
        if ($element->attributes !== null && $element->hasAttributes()) {
18✔
185
            /** @var \DOMAttr $domAttr */
186
            foreach ($element->attributes as $domAttr) {
18✔
187
                $result['_attrs'][$domAttr->name] = $domAttr->value;
18✔
188
            }
189
        }
190

191
        if ($element->hasChildNodes()) {
18✔
192
            $children = $element->childNodes;
18✔
193

194
            $child = $children->item(0);
18✔
195
            if ($children->length === 1 && $child !== null) {
18✔
196
                if ($child->nodeType === \XML_TEXT_NODE) {
18✔
197
                    $result['_text'] = $child->nodeValue;
18✔
198

199
                    return $result;
18✔
200
                }
201

202
                if ($child->nodeType === \XML_CDATA_SECTION_NODE) {
18✔
UNCOV
203
                    $result['_cdata'] = $child->nodeValue;
×
204

UNCOV
205
                    return $result;
×
206
                }
207
            }
208

209
            foreach ($children as $child2) {
18✔
210
                if ($child2->nodeType !== \XML_COMMENT_NODE) {
18✔
211
                    $result['_children'][] = self::dom2Array($child2);
18✔
212
                }
213
            }
214
        }
215

216
        return $result;
18✔
217
    }
218
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc