Completed
Push — master ( 4b16c6...b82f61 )
by Paweł
01:55
created

XMLWriter   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 314
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 314
ccs 0
cts 178
cp 0
rs 8.8
c 0
b 0
f 0
wmc 45
lcom 1
cbo 1

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A getDomain() 0 4 1
A setDomain() 0 4 1
A getCurrentSitemap() 0 4 1
A setCurrentSitemap() 0 4 1
A openSitemap() 0 15 2
A getXMLWriter() 0 4 1
A flushData() 0 4 1
A getSitemapFileFullPath() 0 4 1
A getWorkDir() 0 4 1
A setWorkDir() 0 4 1
A closeSitemap() 0 7 1
A endFile() 0 26 4
A getSitemapSize() 0 6 2
A addUrl() 0 8 2
A addElement() 0 12 4
C addElementArray() 0 36 13
A addElementArrayNonAssoc() 0 6 2
A openSitemapIndex() 0 10 1
A closeSitemapIndex() 0 7 1
A addSitemap() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like XMLWriter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use XMLWriter, and based on these observations, apply Extract Interface, too.

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->getXMLWriter()->startElement(($namespace ? $namespace . ':' : '') . $element);
251
            if (isset($value['_attributes'])) {
252
                foreach ($value['_attributes'] as $attribute => $val) {
253
                    $this->getXMLWriter()->writeAttribute($attribute, $val);
254
                }
255
256
                if (isset($value['_value'])) {
257
                    if (is_array($value['_value'])) {
258
                        foreach ($value['_value'] as $el => $val) {
259
                            $this->addElement($el, $val);
260
                        }
261
                    } else {
262
                        $this->getXMLWriter()->text((string) $value['_value']);
263
                    }
264
                }
265
            } else {
266
                foreach ($value as $el => $val) {
267
                    if (is_array($val)) {
268
                        $this->addElement($el, $val, $namespace);
269
                    } else {
270
                        $this->getXMLWriter()->writeElement(($namespace ? $namespace . ':' : '') . $el, (string) $val);
271
                    }
272
                }
273
            }
274
            $this->getXMLWriter()->endElement();
275
        }
276
    }
277
278
    /**
279
     * @param string      $element
280
     * @param             $value
281
     * @param string|null $namespace
282
     */
283
    private function addElementArrayNonAssoc(string $element, $value, ?string $namespace = null): void
284
    {
285
        foreach ($value as $val) {
286
            $this->addElement($element, $val, $namespace);
287
        }
288
    }
289
290
    /**
291
     * @param string $sitemap
292
     */
293
    public function openSitemapIndex(string $sitemap): void
294
    {
295
        $this->setCurrentSitemap($sitemap);
296
        $this->getXMLWriter()->openMemory();
297
        $this->getXMLWriter()->startDocument('1.0', 'UTF-8');
298
        $this->getXMLWriter()->setIndent(true);
299
        $this->getXMLWriter()->startElement('sitemapindex');
300
        $this->getXMLWriter()->writeAttribute('xmlns', Sitemap::SCHEMA);
301
        $this->flushData();
302
    }
303
304
    /**
305
     * @throws \Exception
306
     */
307
    public function closeSitemapIndex(): void
308
    {
309
        $this->getXMLWriter()->endElement();
310
        $this->getXMLWriter()->endDocument();
311
        $this->flushData();
312
        $this->endFile();
313
    }
314
315
    /**
316
     * @param string $sitemap
317
     * @param string|null $lastmod
318
     */
319
    public function addSitemap(string $sitemap, string $lastmod = null): void
320
    {
321
        $this->getXMLWriter()->startElement('sitemap');
322
        $this->getXMLWriter()->writeElement('loc', $sitemap);
323
        if (isset($lastmod)) {
324
            $this->getXMLWriter()->writeElement('lastmod', $lastmod);
325
        }
326
        $this->getXMLWriter()->endElement();
327
        $this->flushData();
328
    }
329
}
330