Passed
Push — master ( 793471...608ba9 )
by Paweł
01:59
created

XMLWriter::setDomain()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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