Completed
Push — develop ( 6b95ec...94a0bd )
by Serge
05:06
created

Sitemap::generateUrls()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 6

Importance

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