Passed
Push — master ( acb60a...9ce0e5 )
by Serge
02:05
created

Sitemap::sortUrlsByPriority()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.1158

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 20
ccs 10
cts 12
cp 0.8333
rs 8.8571
cc 5
eloc 11
nc 1
nop 0
crap 5.1158
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 Sitemap 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
    /**
46
     * @var mixed renderedUrls
47
     * @access private
48
     */
49
    private $renderedUrls = [];
50
51
    /** @var int Cache expiration time */
52
    public $cacheExpire = 86400;
53
54
    /** @var string Cache key */
55
    public $cacheKey = 'sitemap';
56
57
    /** @var boolean Use php's gzip compressing. */
58
    public $enableGzip = false;
59
60
    /** @var array Model list for sitemap */
61
    public $models = [];
62
63
    /** @var array Url list for sitemap */
64
    public $urls = [];
65
66
    /** @var int */
67
    public $maxSectionUrl = 20000;
68
69
    /** @var bool Sort urls by priority. Top priority urls first */
70
    public $sortByPriority = false;
71
    /**
72
     * Build site map.
73
     * @return array
74
     */
75 5
    public function render()
76
    {
77 5
        $result = Yii::$app->cache->get($this->cacheKey);
78 5
        if ($result) {
79 1
            return $result;
80
        }
81 5
        $this->generateUrls();
82 5
        if ($this->sortByPriority) {
83 1
            $this->sortUrlsByPriority();
84 1
        }
85 5
        $parts = ceil(count($this->renderedUrls) / $this->maxSectionUrl);
86 5
        if ($parts > 1) {
87 1
            $xml = new XMLWriter();
88 1
            $xml->openMemory();
89 1
            $xml->startDocument('1.0', 'UTF-8');
90 1
            $xml->startElement('sitemapindex');
91 1
            $xml->writeAttribute('xmlns', $this->schemas['xmlns']);
92 1
            for ($i = 1; $i <= $parts; $i++) {
93 1
                $xml->startElement('sitemap');
94 1
                $xml->writeElement('loc', Url::to(['/sitemap/default/index', 'id' =>$i], true));
95 1
                $xml->writeElement('lastmod', static::dateToW3C(time()));
96 1
                $xml->endElement();
97 1
                $result[$i]['file'] = Url::to(['/sitemap/default/index', 'id' =>$i], false);
98 1
            }
99 1
            $xml->endElement();
100 1
            $result[0]['xml'] = $xml->outputMemory();
101 1
            $result[0]['file'] = Url::to(['/sitemap/default/index']);
102 1
        }
103 5
        $urlItem = 0;
104 5
        for ($i = 1; $i <= $parts; $i++) {
105 5
            $xml = new XMLWriter();
106 5
            $xml->openMemory();
107 5
            $xml->startDocument('1.0', 'UTF-8');
108 5
            $xml->startElement('urlset');
109 5
            foreach ($this->schemas as $attr => $schemaUrl) {
110 5
                $xml->writeAttribute($attr, $schemaUrl);
111 5
            }
112 5
            for (; ($urlItem < $i * $this->maxSectionUrl) && ($urlItem < count($this->renderedUrls)); $urlItem++) {
113 5
                $xml->startElement('url');
114 5
                foreach ($this->renderedUrls[$urlItem] as $urlKey => $urlValue) {
115 5
                    if (is_array($urlValue)) {
116
                        switch ($urlKey) {
117 1
                            case 'news':
118 1
                                $namespace = 'news:';
119 1
                                $xml->startElement($namespace.$urlKey);
120 1
                                static::hashToXML($urlValue, $xml, $namespace);
121 1
                                $xml->endElement();
122 1
                                break;
123 1
                            case 'images':
124 1
                                $namespace = 'image:';
125 1
                                foreach ($urlValue as $image) {
126 1
                                    $xml->startElement($namespace.'image');
127 1
                                    static::hashToXML($image, $xml, $namespace);
128 1
                                    $xml->endElement();
129 1
                                }
130 1
                                break;
131
                        }
132 1
                    } else {
133 5
                        $xml->writeElement($urlKey, $urlValue);
134
                    }
135 5
                }
136 5
                $xml->endElement();
137 5
            }
138
139 5
            $xml->endElement(); // urlset
140 5
            $xml->endElement(); // document
141 5
            $result[$i]['xml'] = $xml->outputMemory();
142 5
        }
143
144 5
        if ($parts == 1) {
145 4
            $result[0] = $result[1];
146 4
            unset($result[1]);
147 4
        }
148 5
        Yii::$app->cache->set($this->cacheKey, $result, $this->cacheExpire);
149 5
        return $result;
150
    }
151
152
    /**
153
     * Generate url's array from properties $url and $models
154
     *
155
     * @access protected
156
     * @return array
157
     */
158 5
    protected function generateUrls()
159
    {
160 5
        $this->renderedUrls = $this->urls;
161
162 5
        foreach ($this->models as $modelName) {
163
            /** @var behaviors\SitemapBehavior $model */
164 1
            if (is_array($modelName)) {
165 1
                $model = new $modelName['class'];
166 1
                if (isset($modelName['behaviors'])) {
167 1
                    $model->attachBehaviors($modelName['behaviors']);
168 1
                }
169 1
            } else {
170 1
                $model = new $modelName;
171
            }
172 1
            $this->renderedUrls = array_merge($this->renderedUrls, $model->generateSiteMap());
173 5
        }
174
        $this->renderedUrls = array_map(function($item) {
175 5
            $item['loc'] = Url::to($item['loc'], true);
176 5
            if (isset($item['lastmod'])) {
177 1
                $item['lastmod'] = Sitemap::dateToW3C($item['lastmod']);
178 1
            }
179 5
            if (isset($item['images'])) {
180
                $item['images'] = array_map(function($image) {
181 1
                    $image['loc'] = Url::to($image['loc'], true);
182 1
                    return $image;
183 1
                }, $item['images']);
184 1
            }
185 5
            return $item;
186 5
        }, $this->renderedUrls);
187 5
    }
188
189
    /**
190
     * Convert associative arrays to XML
191
     *
192
     * @param array $hash
193
     * @param XMLWriter $xml
194
     * @param string $namespace
195
     * @static
196
     * @access protected
197
     * @return XMLWriter
198
     */
199 1
    protected static function hashToXML($hash, $xml, $namespace = '')
200
    {
201 1
        foreach ($hash as $key => $value) {
202 1
            $xml->startElement($namespace.$key);
203 1
            if (is_array($value)) {
204 1
                static::hashToXML($value, $xml, $namespace);
205 1
            } else {
206 1
                $xml->text($value);
207
            }
208 1
            $xml->endElement();
209 1
        }
210 1
        return $xml;
211
    }
212
    /**
213
     * Convert date to W3C format
214
     *
215
     * @param mixed $date
216
     * @static
217
     * @access protected
218
     * @return string
219
     */
220 3
    public static function dateToW3C($date)
221
    {
222 3
        if (is_int($date)) {
223 2
            return date(DATE_W3C, $date);
224
        } else {
225 2
            return date(DATE_W3C, strtotime($date));
226
        }
227
    }
228
229
    /**
230
     * @return mixed
231
     */
232
    protected function sortUrlsByPriority()
233
    {
234 1
        usort($this->renderedUrls, function ($urlA, $urlB) {
235 1
            if (!isset($urlA['priority'])) {
236
                return 1;
237
            }
238
239 1
            if (!isset($urlB['priority'])) {
240 1
                return -1;
241
            }
242
243 1
            $a = $urlA['priority'];
244 1
            $b = $urlB['priority'];
245 1
            if ($a == $b) {
246
                return 0;
247
            }
248
249 1
            return ($a < $b) ? 1 : -1;
250 1
        });
251 1
    }
252
}
253