forked from pawelantczak/php-sitemap-generator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SitemapGenerator.php
349 lines (343 loc) · 14.3 KB
/
SitemapGenerator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
<?php
/**
* @category class
* @package SitemapGenerator
* @author Paweł Antczak <[email protected]>
* @copyright 2009 Paweł Antczak
* @license http://www.gnu.org/licenses/gpl.html GPL V 2.0
* @version 1.2.0
* @see http://www.sitemaps.org/protocol.php
* @see http://en.wikipedia.org/wiki/Sitemaps
* @see http://en.wikipedia.org/wiki/Sitemap_index
*/
class SitemapGenerator {
/**
* Name of sitemap file
* @var string
* @access public
*/
public $sitemapFileName = "sitemap.xml";
/**
* Name of sitemap index file
* @var string
* @access public
*/
public $sitemapIndexFileName = "sitemap-index.xml";
/**
* Robots file name
* @var string
* @access public
*/
public $robotsFileName = "robots.txt";
/**
* Quantity of URLs per single sitemap file.
* According to specification max value is 50.000.
* If Your links are very long, sitemap file can be bigger than 10MB,
* in this case use smaller value.
* @var int
* @access public
*/
public $maxURLsPerSitemap = 50000;
/**
* If true, two sitemap files (.xml and .xml.gz) will be created and added to robots.txt.
* If true, .gz file will be submitted to search engines.
* If quantity of URLs will be bigger than 50.000, option will be ignored,
* all sitemap files except sitemap index will be compressed.
* @var bool
* @access public
*/
public $createGZipFile = false;
/**
* URL to Your site.
* Script will use it to send sitemaps to search engines.
* @var string
* @access private
*/
private $baseURL;
/**
* Base path. Relative to script location.
* Use this if Your sitemap and robots files should be stored in other
* directory then script.
* @var string
* @access private
*/
private $basePath;
/**
* Version of this class
* @var string
* @access private
*/
private $classVersion = "1.2.0";
/**
* Search engines URLs
* @var array of strings
* @access private
*/
private $searchEngines = array(
array("http://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=USERID&url=",
"http://search.yahooapis.com/SiteExplorerService/V1/ping?sitemap="),
"http://www.google.com/webmasters/tools/ping?sitemap=",
"http://submissions.ask.com/ping?sitemap=",
"http://www.bing.com/webmaster/ping.aspx?siteMap="
);
/**
* Array with urls
* @var array of strings
* @access private
*/
private $urls;
/**
* Array with sitemap
* @var array of strings
* @access private
*/
private $sitemaps;
/**
* Array with sitemap index
* @var array of strings
* @access private
*/
private $sitemapIndex;
/**
* Current sitemap full URL
* @var string
* @access private
*/
private $sitemapFullURL;
/**
* Constructor.
* @param string $baseURL You site URL, with / at the end.
* @param string|null $basePath Relative path where sitemap and robots should be stored.
*/
public function __construct($baseURL, $basePath = "") {
$this->baseURL = $baseURL;
$this->basePath = $basePath;
}
/**
* Use this to add many URL at one time.
* Each inside array can have 1 to 4 fields.
* @param array of arrays of strings $urlsArray
*/
public function addUrls($urlsArray) {
if (!is_array($urlsArray))
throw new InvalidArgumentException("Array as argument should be given.");
foreach ($urlsArray as $url) {
$this->addUrl(isset ($url[0]) ? $url[0] : null,
isset ($url[1]) ? $url[1] : null,
isset ($url[2]) ? $url[2] : null,
isset ($url[3]) ? $url[3] : null);
}
}
/**
* Use this to add single URL to sitemap.
* @param string $url URL
* @param string $lastModified When it was modified, use ISO 8601
* @param string $changeFrequency How often search engines should revisit this URL
* @param string $priority Priority of URL on You site
* @see http://en.wikipedia.org/wiki/ISO_8601
* @see http://php.net/manual/en/function.date.php
*/
public function addUrl($url, $lastModified = null, $changeFrequency = null, $priority = null) {
if ($url == null)
throw new InvalidArgumentException("URL is mandatory. At least one argument should be given.");
$urlLenght = extension_loaded('mbstring') ? mb_strlen($url) : strlen($url);
if ($urlLenght > 2048)
throw new InvalidArgumentException("URL lenght can't be bigger than 2048 characters.
Note, that precise url length check is guaranteed only using mb_string extension.
Make sure Your server allow to use mbstring extension.");
$tmp = array();
$tmp['loc'] = $url;
if (isset($lastModified)) $tmp['lastmod'] = $lastModified;
if (isset($changeFrequency)) $tmp['changefreq'] = $changeFrequency;
if (isset($priority)) $tmp['priority'] = $priority;
$this->urls[] = $tmp;
}
/**
* Create sitemap in memory.
*/
public function createSitemap() {
if (!isset($this->urls))
throw new BadMethodCallException("To create sitemap, call addUrl or addUrls function first.");
if ($this->maxURLsPerSitemap > 50000)
throw new InvalidArgumentException("More than 50,000 URLs per single sitemap is not allowed.");
$generatorInfo = '<!-- generator="SimpleSitemapGenerator/'.$this->classVersion.'" -->
<!-- sitemap-generator-url="http://www.antczak.org"
sitemap-generator-version="'.$this->classVersion.'" -->
<!-- generated-on="'.date('c').'" -->';
$sitemapHeader = '<?xml version="1.0" encoding="UTF-8"?>'.$generatorInfo.'
<urlset
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</urlset>';
$sitemapIndexHeader = '<?xml version="1.0" encoding="UTF-8"?>'.$generatorInfo.'
<sitemapindex
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
</sitemapindex>';
foreach(array_chunk($this->urls,$this->maxURLsPerSitemap) as $sitemap) {
$xml = new SimpleXMLElement($sitemapHeader);
foreach($sitemap as $url) {
$row = $xml->addChild('url');
$row->addChild('loc',htmlspecialchars($url['loc'],ENT_QUOTES,'UTF-8'));
if (isset($url['lastmod'])) $row->addChild('lastmod', $url['lastmod']);
if (isset($url['changefreq'])) $row->addChild('changefreq',$url['changefreq']);
if (isset($url['priority'])) $row->addChild('priority',$url['priority']);
}
if (strlen($xml->asXML()) > 10485760)
throw new LengthException("Sitemap size is more than 10MB (10,485,760),
please decrease maxURLsPerSitemap variable.");
$this->sitemaps[] = $xml->asXML();
}
if (sizeof($this->sitemaps) > 1000)
throw new LengthException("Sitemap index can contains 1000 single sitemaps.
Perhaps You trying to submit too many URLs.");
if (sizeof($this->sitemaps) > 1) {
for($i=0; $i<sizeof($this->sitemaps); $i++) {
$this->sitemaps[$i] = array(
str_replace(".xml", ($i+1).".xml.gz", $this->sitemapFileName),
$this->sitemaps[$i]
);
}
$xml = new SimpleXMLElement($sitemapIndexHeader);
foreach($this->sitemaps as $sitemap) {
$row = $xml->addChild('sitemap');
$row->addChild('loc',$this->baseURL.htmlentities($sitemap[0]));
$row->addChild('lastmod', date('c'));
}
$this->sitemapFullURL = $this->baseURL.$this->sitemapIndexFileName;
$this->sitemapIndex = array(
$this->sitemapIndexFileName,
$xml->asXML());
}
else {
if ($this->createGZipFile)
$this->sitemapFullURL = $this->baseURL.$this->sitemapFileName.".gz";
else
$this->sitemapFullURL = $this->baseURL.$this->sitemapFileName;
$this->sitemaps[0] = array(
$this->sitemapFileName,
$this->sitemaps[0]);
}
}
/**
* Returns created sitemaps as array of strings.
* Use it You want to work with sitemap without saving it as files.
* @return array of strings
* @access public
*/
public function toArray() {
if (isset($this->sitemapIndex))
return array_merge(array($this->sitemapIndex),$this->sitemaps);
else
return $this->sitemaps;
}
/**
* Will write sitemaps as files.
* @access public
*/
public function writeSitemap() {
if (!isset($this->sitemaps))
throw new BadMethodCallException("To write sitemap, call createSitemap function first.");
if (isset($this->sitemapIndex)) {
$this->_writeFile($this->sitemapIndex[1], $this->basePath, $this->sitemapIndex[0]);
foreach($this->sitemaps as $sitemap) {
$this->_writeGZipFile($sitemap[1], $this->basePath, $sitemap[0]);
}
}
else {
$this->_writeFile($this->sitemaps[0][1], $this->basePath, $this->sitemaps[0][0]);
if ($this->createGZipFile)
$this->_writeGZipFile($this->sitemaps[0][1], $this->basePath, $this->sitemaps[0][0].".gz");
}
}
/**
* If robots.txt file exist, will update information about newly created sitemaps.
* If there is no robots.txt will, create one and put into it information about sitemaps.
* @access public
*/
public function updateRobots() {
if (!isset($this->sitemaps))
throw new BadMethodCallException("To update robots.txt, call createSitemap function first.");
$sampleRobotsFile = "User-agent: *\nAllow: /";
if (file_exists($this->basePath.$this->robotsFileName)) {
$robotsFile = explode("\n", file_get_contents($this->basePath.$this->robotsFileName));
$robotsFileContent = "";
foreach($robotsFile as $key=>$value) {
if(substr($value, 0, 8) == 'Sitemap:') unset($robotsFile[$key]);
else $robotsFileContent .= $value."\n";
}
$robotsFileContent .= "Sitemap: $this->sitemapFullURL";
if ($this->createGZipFile && !isset($this->sitemapIndex))
$robotsFileContent .= "\nSitemap: ".$this->sitemapFullURL.".gz";
file_put_contents($this->basePath.$this->robotsFileName,$robotsFileContent);
}
else {
$sampleRobotsFile = $sampleRobotsFile."\n\nSitemap: ".$this->sitemapFullURL;
if ($this->createGZipFile && !isset($this->sitemapIndex))
$sampleRobotsFile .= "\nSitemap: ".$this->sitemapFullURL.".gz";
file_put_contents($this->basePath.$this->robotsFileName, $sampleRobotsFile);
}
}
/**
* Will inform search engines about newly created sitemaps.
* Google, Ask, Bing and Yahoo will be noticed.
* If You don't pass yahooAppId, Yahoo still will be informed,
* but this method can be used once per day. If You will do this often,
* message that limit was exceeded will be returned from Yahoo.
* @param string $yahooAppId Your site Yahoo appid.
* @return array of messages and http codes from each search engine
* @access public
*/
public function submitSitemap($yahooAppId = null) {
if (!isset($this->sitemaps))
throw new BadMethodCallException("To submit sitemap, call createSitemap function first.");
if (!extension_loaded('curl'))
throw new BadMethodCallException("cURL library is needed to do submission.");
$searchEngines = $this->searchEngines;
$searchEngines[0] = isset($yahooAppId) ? str_replace("USERID", $yahooAppId, $searchEngines[0][0]) : $searchEngines[0][1];
$result = array();
for($i=0;$i<sizeof($searchEngines);$i++) {
$submitSite = curl_init($searchEngines[$i].htmlspecialchars($this->sitemapFullURL,ENT_QUOTES,'UTF-8'));
curl_setopt($submitSite, CURLOPT_RETURNTRANSFER, true);
$responseContent = curl_exec($submitSite);
$response = curl_getinfo($submitSite);
$submitSiteShort = array_reverse(explode(".",parse_url($searchEngines[$i], PHP_URL_HOST)));
$result[] = array("site"=>$submitSiteShort[1].".".$submitSiteShort[0],
"fullsite"=>$searchEngines[$i].htmlspecialchars($this->sitemapFullURL, ENT_QUOTES,'UTF-8'),
"http_code"=>$response['http_code'],
"message"=>str_replace("\n", " ", strip_tags($responseContent)));
}
return $result;
}
/**
* Save file.
* @param string $content
* @param string $filePath
* @param string $fileName
* @return bool
* @access private
*/
private function _writeFile($content, $filePath, $fileName) {
$file = fopen($filePath.$fileName, 'w');
fwrite($file, $content);
return fclose($file);
}
/**
* Save GZipped file.
* @param string $content
* @param string $filePath
* @param string $fileName
* @return bool
* @access private
*/
private function _writeGZipFile($content, $filePath, $fileName) {
$file = gzopen($filePath.$fileName, 'w');
gzwrite($file, $content);
return gzclose($file);
}
}
?>