Completed
Pull Request — master (#8)
by
unknown
03:18
created

Generator   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 31
c 1
b 0
f 1
lcom 1
cbo 3
dl 0
loc 218
rs 9.8

5 Methods

Rating   Name   Duplication   Size   Complexity  
C render() 0 71 14
C generateUrls() 0 36 7
A hashToXML() 0 13 3
A dateToW3C() 0 8 2
B sortUrlsByPriority() 0 20 5
1
<?php
2
/**
3
 * Yii2 module for automatically generating XML Sitemap.
4
 *
5
 * @link https://github.com/himiklab/yii2-sitemap-module
6
 * @author Serge Larin <[email protected]>
7
 * @author HimikLab
8
 * @copyright 2015 Assayer Pro Company
9
 * @copyright Copyright (c) 2014 HimikLab
10
 * @license http://opensource.org/licenses/MIT MIT
11
 *
12
 * based on https://github.com/himiklab/yii2-sitemap-module
13
 */
14
15
namespace assayerpro\sitemap;
16
17
use Yii;
18
use XMLWriter;
19
use yii\base\InvalidConfigException;
20
use yii\caching\Cache;
21
use yii\helpers\Url;
22
23
/**
24
 * Yii2 module for automatically generating XML Sitemap.
25
 *
26
 * @author Serge Larin <[email protected]>
27
 * @author HimikLab
28
 * @package assayerpro\sitemap
29
 */
30
class Generator extends \yii\base\Component
31
{
32
    const ALWAYS = 'always';
33
    const HOURLY = 'hourly';
34
    const DAILY = 'daily';
35
    const WEEKLY = 'weekly';
36
    const MONTHLY = 'monthly';
37
    const YEARLY = 'yearly';
38
    const NEVER = 'never';
39
    private $schemas = [
40
        'xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9',
41
        'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1',
42
        'xmlns:news' => 'http://www.google.com/schemas/sitemap-news/0.9',
43
    ];
44
45
    /** @var int Cache expiration time */
46
    public $cacheExpire = 86400;
47
48
    /** @var string Cache key */
49
    public $cacheKey = 'sitemap';
50
51
    /** @var boolean Use php's gzip compressing. */
52
    public $enableGzip = false;
53
54
    /** @var array Model list for sitemap */
55
    public $models = [];
56
57
    /** @var array Url list for sitemap */
58
    public $urls = [];
59
60
    /** @var int */
61
    public $maxSectionUrl = 20000;
62
63
    public $sortByPriority = true;
64
65
    /**
66
     * Build site map.
67
     * @return array
68
     */
69
    public function render()
70
    {
71
        $result = Yii::$app->cache->get($this->cacheKey);
72
        if ($result) {
73
            return $result;
74
        }
75
        $urls = $this->generateUrls();
76
        $parts = ceil(count($urls) / $this->maxSectionUrl);
77
        if ($parts > 1) {
78
            $xml = new XMLWriter();
79
            $xml->openMemory();
80
            $xml->startDocument('1.0', 'UTF-8');
81
            $xml->startElement('sitemapindex');
82
            $xml->writeAttribute('xmlns', $this->schemas['xmlns']);
83
            for ($i = 1; $i <= $parts; $i++) {
84
                $xml->startElement('sitemap');
85
                $xml->writeElement('loc', Url::to([$route, 'id' =>$i], true));
0 ignored issues
show
Bug introduced by
The variable $route does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
86
                $xml->writeElement('lastmod', static::dateToW3C(time()));
87
                $xml->endElement();
88
            }
89
            $xml->endElement();
90
            $result[0]['xml'] = $xml->outputMemory();
91
        }
92
        $urlItem = 0;
93
        for ($i = 1; $i <= $parts; $i++) {
94
            $xml = new XMLWriter();
95
            $xml->openMemory();
96
            $xml->startDocument('1.0', 'UTF-8');
97
            $xml->startElement('urlset');
98
            foreach ($this->schemas as $attr => $schemaUrl) {
99
                $xml->writeAttribute($attr, $schemaUrl);
100
            }
101
            for (; ($urlItem < $i * $this->maxSectionUrl) && ($urlItem < count($urls)); $urlItem++) {
102
                $xml->startElement('url');
103
                foreach ($urls[$urlItem] as $urlKey => $urlValue) {
104
                    if (is_array($urlValue)) {
105
                        switch ($urlKey) {
106
                            case 'news':
107
                                $namespace = 'news:';
108
                                $xml->startElement($namespace.$urlKey);
109
                                static::hashToXML($urlValue, $xml, $namespace);
110
                                $xml->endElement();
111
                                break;
112
                            case 'images':
113
                                $namespace = 'image:';
114
                                foreach ($urlValue as $image) {
115
                                    $xml->startElement($namespace.'image');
116
                                    static::hashToXML($image, $xml, $namespace);
117
                                    $xml->endElement();
118
                                }
119
                                break;
120
                        }
121
                    } else {
122
                        $xml->writeElement($urlKey, $urlValue);
123
                    }
124
                }
125
                $xml->endElement();
126
            }
127
128
            $xml->endElement(); // urlset
129
            $xml->endElement(); // document
130
            $result[$i]['xml'] = $xml->outputMemory();
131
        }
132
133
        if ($parts == 1) {
134
            $result[0] = $result[1];
135
            unset($result[1]);
136
        }
137
        Yii::$app->cache->set($this->cacheKey, $result, $this->cacheExpire);
138
        return $result;
139
    }
140
141
    /**
142
     * Generate url's array from properties $url and $models
143
     *
144
     * @access protected
145
     * @return array
146
     */
147
    protected function generateUrls()
148
    {
149
        $urls = $this->urls;
150
151
        foreach ($this->models as $modelName) {
152
            /** @var behaviors\SitemapBehavior $model */
153
            if (is_array($modelName)) {
154
                $model = new $modelName['class'];
155
                if (isset($modelName['behaviors'])) {
156
                    $model->attachBehaviors($modelName['behaviors']);
157
                }
158
            } else {
159
                $model = new $modelName;
160
            }
161
            $urls = array_merge($urls, $model->generateSiteMap());
162
        }
163
        $urls = array_map(function($item) {
164
            $item['loc'] = Url::to($item['loc'], true);
165
            if (isset($item['lastmod'])) {
166
                $item['lastmod'] = Generator::dateToW3C($item['lastmod']);
167
            }
168
            if (isset($item['images'])) {
169
                $item['images'] = array_map(function($image) {
170
                    $image['loc'] = Url::to($image['loc'], true);
171
                    return $image;
172
                }, $item['images']);
173
            }
174
            return $item;
175
        }, $urls);
176
177
        if ($this->sortByPriority) {
178
            $this->sortUrlsByPriority($urls);
179
        }
180
181
        return $urls;
182
    }
183
184
    /**
185
     * Convert associative arrays to XML
186
     *
187
     * @param array $hash
188
     * @param XMLWriter $xml
189
     * @param string $namespace
190
     * @static
191
     * @access protected
192
     * @return XMLWriter
193
     */
194
    protected static function hashToXML($hash, $xml, $namespace = '')
195
    {
196
        foreach ($hash as $key => $value) {
197
            $xml->startElement($namespace.$key);
198
            if (is_array($value)) {
199
                static::hashToXML($value, $xml, $namespace);
200
            } else {
201
                $xml->text($value);
202
            }
203
            $xml->endElement();
204
        }
205
        return $xml;
206
    }
207
    /**
208
     * Convert date to W3C format
209
     *
210
     * @param mixed $date
211
     * @static
212
     * @access protected
213
     * @return string
214
     */
215
    public static function dateToW3C($date)
216
    {
217
        if (is_int($date)) {
218
            return date(DATE_W3C, $date);
219
        } else {
220
            return date(DATE_W3C, strtotime($date));
221
        }
222
    }
223
224
    /**
225
     * @return mixed
226
     */
227
    protected function sortUrlsByPriority(&$urls)
228
    {
229
        usort($urls, function ($urlA, $urlB) {
230
            if (!isset($urlA['priority'])) {
231
                return 1;
232
            }
233
234
            if (!isset($urlB['priority'])) {
235
                return -1;
236
            }
237
238
            $a = $urlA['priority'];
239
            $b = $urlB['priority'];
240
            if ($a == $b) {
241
                return 0;
242
            }
243
244
            return ($a < $b) ? 1 : -1;
245
        });
246
    }
247
}
248