Completed
Pull Request — master (#8)
by
unknown
09:21
created

Generator::dateToW3C()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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