Passed
Push — develop ( f21533...50b624 )
by Serge
02:00
created

Sitemap::sortUrlsByPriority()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.3906

Importance

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