Passed
Branch master (576d7a)
by Paweł
01:36
created

XMLWriter::addElementArray()   B

Complexity

Conditions 11
Paths 10

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
347
    {
348
        $begin = '';
349
350
        if ($namespace) {
351
            $begin = $namespace . ':';
352
        }
353
354
        if (!is_array($value)) {
355
            $this->getXMLWriter()->writeElement($begin . $element, (string)$value);
356
        } else {
357
            if (isset($value['_namespace'])) {
358
                $this->addElementNS($value);
0 ignored issues
show
Bug introduced by
The call to Wszetko\Sitemap\Drivers\...LWriter::addElementNS() has too few arguments starting with value. ( Ignorable by Annotation )

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

358
                $this->/** @scrutinizer ignore-call */ 
359
                       addElementNS($value);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug introduced by
$value of type array is incompatible with the type string expected by parameter $element of Wszetko\Sitemap\Drivers\...LWriter::addElementNS(). ( Ignorable by Annotation )

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

358
                $this->addElementNS(/** @scrutinizer ignore-type */ $value);
Loading history...
359
            } else {
360
                if ($this->isAssoc($value)) {
361
                    $this->startElement($element, $namespace, null);
362
363
                    if (isset($value['_attributes'])) {
364
                        foreach ($value['_attributes'] as $attribute => $val) {
365
                            $this->getXMLWriter()->writeAttribute($attribute, $val);
366
                        }
367
368
                        if (isset($value['_value'])) {
369
                            if (is_array($value['_value'])) {
370
                                foreach ($value['_value'] as $el => $val) {
371
                                    $this->addElement($el, $val);
372
                                }
373
                            } else {
374
                                $this->getXMLWriter()->text((string)$value['_value']);
375
                            }
376
                        }
377
                    } else {
378
                        foreach ($value as $el => $val) {
379
                            if (is_array($val)) {
380
                                $this->addElement($el, $val, $namespace);
381
                            } else {
382
                                $this->getXMLWriter()->writeElement($begin . $el, $val);
383
                            }
384
                        }
385
                    }
386
387
                    $this->getXMLWriter()->endElement();
388
                } else {
389
                    foreach ($value as $val) {
390
                        if (is_array($val)) {
391
                            $this->addElement($element, $val, $namespace);
392
                        } else {
393
                            $this->getXMLWriter()->writeElement($begin . $element, $val);
394
                        }
395
                    }
396
                }
397
            }
398
        }
399
    }
400
}
401