Completed
Push — master ( 7fe6b0...d8a5ea )
by Paweł
02:19
created

XMLWriter   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Test Coverage

Coverage 98.1%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 89
c 1
b 0
f 0
dl 0
loc 226
ccs 103
cts 105
cp 0.981
rs 9.44
wmc 37

14 Methods

Rating   Name   Duplication   Size   Complexity  
A addElement() 0 9 4
A getSitemapSize() 0 5 2
A closeSitemapIndex() 0 6 1
A openSitemap() 0 14 2
A addElementArrayNonAssoc() 0 4 2
A addSitemap() 0 9 2
A closeSitemap() 0 6 1
B addElementArrayAssoc() 0 27 10
A addElementArray() 0 10 4
A flushData() 0 3 1
A __construct() 0 8 2
A addUrl() 0 7 2
A endFile() 0 21 3
A openSitemapIndex() 0 9 1
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of Wszetko Sitemap.
7
 *
8
 * (c) Paweł Kłopotek-Główczewski <[email protected]>
9
 *
10
 * This source file is subject to the MIT license that is bundled
11
 * with this source code in the file LICENSE.
12
 */
13
14
namespace Wszetko\Sitemap\Drivers\XML;
15
16
use Exception;
17
use Wszetko\Sitemap\Sitemap;
18
19
/**
20
 * Class XMLWriter.
21
 *
22
 * @package Wszetko\Sitemap\Drivers\XML
23
 *
24
 * @method getXMLWriter(): \XMLWriter
25
 */
26
class XMLWriter extends AbstractXML
27
{
28
    /**
29
     * XMLWriter constructor.
30
     *
31
     * @param array $config
32
     *
33
     * @throws \InvalidArgumentException
34
     */
35 18
    public function __construct(array $config)
36
    {
37 18
        if (!isset($config['domain'])) {
38 2
            throw new \InvalidArgumentException('Domain is not set.');
39
        }
40
41 18
        $this->XMLWriter = new \XMLWriter();
42 18
        $this->setDomain($config['domain']);
43 18
    }
44
45
    /**
46
     * @param string $sitemap
47
     * @param array  $extensions
48
     */
49 4
    public function openSitemap(string $sitemap, array $extensions = []): void
50
    {
51 4
        $this->setCurrentSitemap($sitemap);
52 4
        $this->getXMLWriter()->openMemory();
53 4
        $this->getXMLWriter()->startDocument('1.0', 'UTF-8');
54 4
        $this->getXMLWriter()->setIndent(true);
55 4
        $this->getXMLWriter()->startElement('urlset');
56 4
        $this->getXMLWriter()->writeAttribute('xmlns', Sitemap::SCHEMA);
57
58 4
        foreach ($extensions as $extension => $urlset) {
59 2
            $this->getXMLWriter()->writeAttribute('xmlns:' . $extension, $urlset);
60
        }
61
62 4
        $this->flushData();
63 4
    }
64
65
    /**
66
     * @throws \Exception
67
     */
68 4
    public function closeSitemap(): void
69
    {
70 4
        $this->getXMLWriter()->endElement();
71 4
        $this->getXMLWriter()->endDocument();
72 4
        $this->flushData();
73 4
        $this->endFile();
74 4
    }
75
76
    /**
77
     * @return int
78
     */
79 6
    public function getSitemapSize(): int
80
    {
81 6
        clearstatcache(true, $this->getSitemapFileFullPath());
82
83 6
        return file_exists($this->getSitemapFileFullPath()) ? filesize($this->getSitemapFileFullPath()) : 0;
84
    }
85
86
    /**
87
     * @param array $element
88
     */
89 2
    public function addUrl(array $element): void
90
    {
91 2
        foreach ($element as $el => $val) {
92 2
            $this->addElement($el, $val);
93
        }
94
95 2
        $this->flushData();
96 2
    }
97
98
    /**
99
     * @param string $sitemap
100
     */
101 2
    public function openSitemapIndex(string $sitemap): void
102
    {
103 2
        $this->setCurrentSitemap($sitemap);
104 2
        $this->getXMLWriter()->openMemory();
105 2
        $this->getXMLWriter()->startDocument('1.0', 'UTF-8');
106 2
        $this->getXMLWriter()->setIndent(true);
107 2
        $this->getXMLWriter()->startElement('sitemapindex');
108 2
        $this->getXMLWriter()->writeAttribute('xmlns', Sitemap::SCHEMA);
109 2
        $this->flushData();
110 2
    }
111
112
    /**
113
     * @throws \Exception
114
     */
115 2
    public function closeSitemapIndex(): void
116
    {
117 2
        $this->getXMLWriter()->endElement();
118 2
        $this->getXMLWriter()->endDocument();
119 2
        $this->flushData();
120 2
        $this->endFile();
121 2
    }
122
123
    /**
124
     * @param string      $sitemap
125
     * @param null|string $lastmod
126
     */
127 2
    public function addSitemap(string $sitemap, string $lastmod = null): void
128
    {
129 2
        $this->getXMLWriter()->startElement('sitemap');
130 2
        $this->getXMLWriter()->writeElement('loc', $sitemap);
131 2
        if (isset($lastmod)) {
132 2
            $this->getXMLWriter()->writeElement('lastmod', $lastmod);
133
        }
134 2
        $this->getXMLWriter()->endElement();
135 2
        $this->flushData();
136 2
    }
137
138
    /**
139
     * Save from buffer to file.
140
     *
141
     * @return void
142
     */
143 6
    private function flushData(): void
144
    {
145 6
        file_put_contents($this->getSitemapFileFullPath(), $this->getXMLWriter()->flush(true), FILE_APPEND);
146 6
    }
147
148
    /**
149
     * Remove whitespace chars from end of file (Google don't like them).
150
     *
151
     * @throws Exception
152
     *
153
     * @return void
154
     */
155 6
    private function endFile(): void
156
    {
157 6
        $sitemapFile = fopen($this->getSitemapFileFullPath(), 'r+');
158
159 6
        fseek($sitemapFile, -1, SEEK_END);
0 ignored issues
show
Bug introduced by
It seems like $sitemapFile can also be of type false; however, parameter $handle of fseek() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

159
        fseek(/** @scrutinizer ignore-type */ $sitemapFile, -1, SEEK_END);
Loading history...
160 6
        $truncate = 0;
161 6
        $length = $this->getSitemapSize();
162 6
        $end = false;
163
164
        do {
165 6
            $s = fread($sitemapFile, 1);
0 ignored issues
show
Bug introduced by
It seems like $sitemapFile can also be of type false; however, parameter $handle of fread() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

165
            $s = fread(/** @scrutinizer ignore-type */ $sitemapFile, 1);
Loading history...
166 6
            if (ctype_space($s)) {
167 6
                ++$truncate;
168 6
                fseek($sitemapFile, -2, SEEK_CUR);
169
            } else {
170 6
                $end = true;
171
            }
172 6
        } while (!$end);
173
174 6
        ftruncate($sitemapFile, $length - $truncate);
0 ignored issues
show
Bug introduced by
It seems like $sitemapFile can also be of type false; however, parameter $handle of ftruncate() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
        ftruncate(/** @scrutinizer ignore-type */ $sitemapFile, $length - $truncate);
Loading history...
175 6
        fclose($sitemapFile);
0 ignored issues
show
Bug introduced by
It seems like $sitemapFile can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
        fclose(/** @scrutinizer ignore-type */ $sitemapFile);
Loading history...
176 6
    }
177
178
    /**
179
     * @param string      $element
180
     * @param             $value
181
     * @param null|string $namespace
182
     */
183 2
    private function addElement(string $element, $value, ?string $namespace = null): void
184
    {
185 2
        if (!is_array($value)) {
186 2
            $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $element, (string) $value);
187
        } else {
188 2
            if (isset($value['_namespace'])) {
189 2
                $this->addElement($value['_element'], $value[$value['_element']], $value['_namespace']);
190
            } else {
191 2
                $this->addElementArray($element, $value, $namespace);
192
            }
193
        }
194 2
    }
195
196
    /**
197
     * @param string      $element
198
     * @param             $value
199
     * @param null|string $namespace
200
     */
201 2
    private function addElementArray(string $element, $value, ?string $namespace = null): void
202
    {
203 2
        if (!$this->isAssoc($value)) {
204 2
            if (!empty($value)) {
205 2
                $this->addElementArrayNonAssoc($element, $value, $namespace);
206
            } else {
207 2
                $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $element);
208
            }
209
        } else {
210 2
            $this->addElementArrayAssoc($element, $value, $namespace);
211
        }
212 2
    }
213
214 2
    private function addElementArrayAssoc(string $element, $value, ?string $namespace = null): void
215
    {
216 2
        $this->getXMLWriter()->startElement(($namespace ? $namespace . ':' : '') . $element);
217 2
        if (isset($value['_attributes'])) {
218 2
            foreach ($value['_attributes'] as $attribute => $val) {
219 2
                $this->getXMLWriter()->writeAttribute($attribute, $val);
220
            }
221
222 2
            if (isset($value['_value'])) {
223 2
                if (is_array($value['_value'])) {
224
                    foreach ($value['_value'] as $el => $val) {
225
                        $this->addElement($el, $val);
226
                    }
227
                } else {
228 2
                    $this->getXMLWriter()->text((string) $value['_value']);
229
                }
230
            }
231
        } else {
232 2
            foreach ($value as $el => $val) {
233 2
                if (is_array($val)) {
234 2
                    $this->addElement($el, $val, $namespace);
235
                } else {
236 2
                    $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $el, (string) $val);
237
                }
238
            }
239
        }
240 2
        $this->getXMLWriter()->endElement();
241 2
    }
242
243
    /**
244
     * @param string      $element
245
     * @param             $value
246
     * @param null|string $namespace
247
     */
248 2
    private function addElementArrayNonAssoc(string $element, $value, ?string $namespace = null): void
249
    {
250 2
        foreach ($value as $val) {
251 2
            $this->addElement($element, $val, $namespace);
252
        }
253 2
    }
254
}
255