Completed
Push — master ( b82f61...793471 )
by Paweł
02:36
created

XMLWriter::addElementArrayAssoc()   B

Complexity

Conditions 10
Paths 9

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
cc 10
nc 9
nop 3
dl 0
loc 28
ccs 0
cts 27
cp 0
crap 110
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Wszetko\Sitemap\Drivers\XML;
5
6
use Exception;
7
use Wszetko\Sitemap\Interfaces\XML;
8
use Wszetko\Sitemap\Sitemap;
9
use Wszetko\Sitemap\Traits\IsAssoc;
10
11
/**
12
 * Class XMLWriter
13
 *
14
 * @package Wszetko\Sitemap\Drivers\XML
15
 */
16
class XMLWriter implements XML
17
{
18
    use IsAssoc;
19
20
    /**
21
     * @var \XMLWriter
22
     */
23
    private $XMLWriter;
24
25
    /**
26
     * @var string
27
     */
28
    private $currentSitemap;
29
30
    /**
31
     * @var string
32
     */
33
    private $workDir;
34
35
    /**
36
     * @var string
37
     */
38
    private $domain;
39
40
    /**
41
     * XMLWriter constructor.
42
     *
43
     * @param array $config
44
     *
45
     * @throws \Exception
46
     */
47
    public function __construct(array $config)
48
    {
49
        $this->XMLWriter = new \XMLWriter();
50
51
        if (!isset($config['domain'])) {
52
            throw new Exception('Domain is not set.');
53
        }
54
55
        $this->setDomain($config['domain']);
56
    }
57
58
    /**
59
     * @return string
60
     */
61
    public function getDomain(): string
62
    {
63
        return $this->domain;
64
    }
65
66
    /**
67
     * @param string $domain
68
     */
69
    public function setDomain(string $domain): void
70
    {
71
        $this->domain = $domain;
72
    }
73
74
    /**
75
     * @return string
76
     */
77
    public function getCurrentSitemap(): string
78
    {
79
        return $this->currentSitemap;
80
    }
81
82
    /**
83
     * @param string $currentSitemap
84
     */
85
    public function setCurrentSitemap(string $currentSitemap): void
86
    {
87
        $this->currentSitemap = $currentSitemap;
88
    }
89
90
    /**
91
     * @param string $sitemap
92
     * @param array  $extensions
93
     */
94
    public function openSitemap(string $sitemap, array $extensions = []): void
95
    {
96
        $this->setCurrentSitemap($sitemap);
97
        $this->getXMLWriter()->openMemory();
98
        $this->getXMLWriter()->startDocument('1.0', 'UTF-8');
99
        $this->getXMLWriter()->setIndent(true);
100
        $this->getXMLWriter()->startElement('urlset');
101
        $this->getXMLWriter()->writeAttribute('xmlns', Sitemap::SCHEMA);
102
103
        foreach ($extensions as $extension => $urlset) {
104
            $this->getXMLWriter()->writeAttribute('xmlns:' . $extension, $urlset);
105
        }
106
107
        $this->flushData();
108
    }
109
110
    /**
111
     * @return \XMLWriter
112
     */
113
    private function getXMLWriter(): \XMLWriter
114
    {
115
        return $this->XMLWriter;
116
    }
117
118
    /**
119
     * Save from buffer to file
120
     *
121
     * @return void
122
     */
123
    private function flushData(): void
124
    {
125
        file_put_contents($this->getSitemapFileFullPath(), $this->getXMLWriter()->flush(true), FILE_APPEND);
126
    }
127
128
    /**
129
     * @return string
130
     */
131
    private function getSitemapFileFullPath(): string
132
    {
133
        return $this->getWorkDir() . DIRECTORY_SEPARATOR . $this->currentSitemap;
134
    }
135
136
    /**
137
     * @return string
138
     */
139
    public function getWorkDir(): string
140
    {
141
        return $this->workDir;
142
    }
143
144
    /**
145
     * @param string $dir
146
     */
147
    public function setWorkDir(string $dir): void
148
    {
149
        $this->workDir = $dir;
150
    }
151
152
    /**
153
     * @throws \Exception
154
     */
155
    public function closeSitemap(): void
156
    {
157
        $this->getXMLWriter()->endElement();
158
        $this->getXMLWriter()->endDocument();
159
        $this->flushData();
160
        $this->endFile();
161
    }
162
163
    /**
164
     * Remove whitespace chars from end of file (Google don't like them)
165
     *
166
     * @return void
167
     * @throws Exception
168
     */
169
    private function endFile(): void
170
    {
171
        $sitemapFile = fopen($this->getSitemapFileFullPath(), 'r+');
172
173
        if ($sitemapFile === false) {
174
            throw new Exception("Unable to open file.");
175
        }
176
177
        fseek($sitemapFile, -1, SEEK_END);
178
        $truncate = 0;
179
        $length = $this->getSitemapSize();
180
        $end = false;
181
182
        do {
183
            $s = fread($sitemapFile, 1);
184
            if (ctype_space($s)) {
185
                $truncate++;
186
                fseek($sitemapFile, -2, SEEK_CUR);
187
            } else {
188
                $end = true;
189
            }
190
        } while (!$end);
191
192
        ftruncate($sitemapFile, $length - $truncate);
193
        fclose($sitemapFile);
194
    }
195
196
    /**
197
     * @return int
198
     */
199
    public function getSitemapSize(): int
200
    {
201
        clearstatcache(true, $this->getSitemapFileFullPath());
202
203
        return file_exists($this->getSitemapFileFullPath()) ? filesize($this->getSitemapFileFullPath()) : 0;
204
    }
205
206
    /**
207
     * @param array $element
208
     */
209
    public function addUrl(array $element): void
210
    {
211
        foreach ($element as $el => $val) {
212
            $this->addElement($el, $val);
213
        }
214
215
        $this->flushData();
216
    }
217
218
    /**
219
     * @param string      $element
220
     * @param             $value
221
     * @param string|null $namespace
222
     */
223
    private function addElement(string $element, $value, ?string $namespace = null): void
224
    {
225
        if (!is_array($value)) {
226
            $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $element, (string) $value);
227
        } else {
228
            if (isset($value['_namespace'])) {
229
                $this->addElement($value['_element'], $value[$value['_element']], $value['_namespace']);
230
            } else {
231
                $this->addElementArray($element, $value, $namespace);
232
            }
233
        }
234
    }
235
236
    /**
237
     * @param string      $element
238
     * @param             $value
239
     * @param string|null $namespace
240
     */
241
    private function addElementArray(string $element, $value, ?string $namespace = null): void
242
    {
243
        if (!$this->isAssoc($value)) {
244
            if (!empty($value)) {
245
                $this->addElementArrayNonAssoc($element, $value, $namespace);
246
            } else {
247
                $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $element);
248
            }
249
        } else {
250
            $this->addElementArrayAssoc($element, $value, $namespace);
251
        }
252
    }
253
254
    private function addElementArrayAssoc(string $element, $value, ?string $namespace = null): void
255
    {
256
        $this->getXMLWriter()->startElement(($namespace ? $namespace . ':' : '') . $element);
257
        if (isset($value['_attributes'])) {
258
            foreach ($value['_attributes'] as $attribute => $val) {
259
                $this->getXMLWriter()->writeAttribute($attribute, $val);
260
            }
261
262
            if (isset($value['_value'])) {
263
                if (is_array($value['_value'])) {
264
                    foreach ($value['_value'] as $el => $val) {
265
                        $this->addElement($el, $val);
266
                    }
267
                } else {
268
                    $this->getXMLWriter()->text((string) $value['_value']);
269
                }
270
            }
271
        } else {
272
            foreach ($value as $el => $val) {
273
                if (is_array($val)) {
274
                    $this->addElement($el, $val, $namespace);
275
                } else {
276
                    $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $el, (string) $val);
277
                }
278
            }
279
        }
280
        $this->getXMLWriter()->endElement();
281
    }
282
283
    /**
284
     * @param string      $element
285
     * @param             $value
286
     * @param string|null $namespace
287
     */
288
    private function addElementArrayNonAssoc(string $element, $value, ?string $namespace = null): void
289
    {
290
        foreach ($value as $val) {
291
            $this->addElement($element, $val, $namespace);
292
        }
293
    }
294
295
    /**
296
     * @param string $sitemap
297
     */
298
    public function openSitemapIndex(string $sitemap): void
299
    {
300
        $this->setCurrentSitemap($sitemap);
301
        $this->getXMLWriter()->openMemory();
302
        $this->getXMLWriter()->startDocument('1.0', 'UTF-8');
303
        $this->getXMLWriter()->setIndent(true);
304
        $this->getXMLWriter()->startElement('sitemapindex');
305
        $this->getXMLWriter()->writeAttribute('xmlns', Sitemap::SCHEMA);
306
        $this->flushData();
307
    }
308
309
    /**
310
     * @throws \Exception
311
     */
312
    public function closeSitemapIndex(): void
313
    {
314
        $this->getXMLWriter()->endElement();
315
        $this->getXMLWriter()->endDocument();
316
        $this->flushData();
317
        $this->endFile();
318
    }
319
320
    /**
321
     * @param string $sitemap
322
     * @param string|null $lastmod
323
     */
324
    public function addSitemap(string $sitemap, string $lastmod = null): void
325
    {
326
        $this->getXMLWriter()->startElement('sitemap');
327
        $this->getXMLWriter()->writeElement('loc', $sitemap);
328
        if (isset($lastmod)) {
329
            $this->getXMLWriter()->writeElement('lastmod', $lastmod);
330
        }
331
        $this->getXMLWriter()->endElement();
332
        $this->flushData();
333
    }
334
}
335