PageRenderTime 27ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/sitemaps.php

https://github.com/chemicaloliver/codeigniter-sitemaps
PHP | 453 lines | 289 code | 80 blank | 84 comment | 41 complexity | b4f0cbe74b1af196345dcbd9dda8f155 MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. if (!defined('BASEPATH')) exit('No direct script access allowed');
  3. /**
  4. * A class for generating XML sitemaps
  5. *
  6. * @author Oliver Smith <chemicaloli@gmail.com>
  7. * @author Philipp Dörner <pd@signalkraft.com>
  8. * @author Sadaoui "SAFAD" Abderrahim <SAFAD.Line@gmail.com>
  9. * @version 0.8
  10. * @access public
  11. * @package sitemaps
  12. */
  13. class Sitemaps
  14. {
  15. private $CI;
  16. private $items = array(); //array of webpages for sitemap
  17. private $item_keys =array('loc', 'lastmod','changefreq','priority');
  18. private $error_msg = array(); //errors
  19. private $ignore = array('Error'); //controllers not to auto probe
  20. private $excluded_methods = array('__construct', 'get_instance'); //method names not to include in autogeneration
  21. function __construct()
  22. {
  23. $this->CI = & get_instance();
  24. $this->CI->config->load('sitemaps');
  25. }
  26. /**
  27. * Adds a new item to the urlset
  28. *
  29. * @param array $new_item
  30. * @access public
  31. */
  32. function add_item($new_item = array())
  33. {
  34. //check that all the required keys are present
  35. foreach($this->item_keys as $key)
  36. {
  37. if(!array_key_exists($key, $new_item))
  38. {
  39. $this->set_error('Attempting to add page array with missing fields');
  40. return FALSE;
  41. }
  42. }
  43. $this->items[] = $new_item;
  44. return TRUE;
  45. }
  46. /**
  47. * Adds an array of items to the urlset
  48. *
  49. * @param array $new_items array of items
  50. * @access public
  51. */
  52. function add_item_array($new_items)
  53. {
  54. //check there are some items to add
  55. if(!count($new_items)>0)
  56. {
  57. $this->set_error('Attempting to add empty array of pages');
  58. return FALSE;
  59. }
  60. //check that all the required keys are present
  61. foreach($new_items as $new_item)
  62. {
  63. if(!$this->add_item($new_item))
  64. {
  65. return FALSE;
  66. }
  67. }
  68. return TRUE;
  69. }
  70. /**
  71. * Detects pages generated directly by CI controller
  72. * methods and adds them to the $this->items
  73. *
  74. * Must be called from a class not being probed
  75. *
  76. * @param array $exclude array of exluded classes
  77. * @return bool
  78. */
  79. function auto_detect($excluded = array('sitemap'))
  80. {
  81. $this->CI->load->helper('file');
  82. //get the filenames from the controller directory
  83. $files = get_filenames('application/controllers');
  84. if(count($files) < 1)
  85. {
  86. $this->set_error('No controller class files found for autodetection');
  87. return FALSE;
  88. }
  89. //add the exluded parameter to the existing ignored classes
  90. foreach ($excluded as $excluded_class)
  91. {
  92. $this->ignore[] = ucfirst($excluded_class);
  93. }
  94. //loop through the files in the controller dir
  95. for ($index = 0; $index < count($files); $index++)
  96. {
  97. //get the class names
  98. list($class, $ext) = explode('.', ucfirst(basename($files[$index])));
  99. //ignore all files that don't end .php
  100. if ($ext != 'php' || in_array($class, $this->ignore)) continue;
  101. //try to include the files
  102. try
  103. {
  104. include('application/controllers/' . $files[$index]);
  105. }
  106. catch (Exception $exc)
  107. {
  108. $this->set_error('Failed to include '.$files[$index]);
  109. continue;
  110. }
  111. //get the methods for the class (assuming method == page)
  112. foreach (get_class_methods($class) as $page)
  113. {
  114. //ignore methods specified in he exclude
  115. if (in_array($page, $this->excluded_methods)) continue;
  116. //setup and add page to item list
  117. $item = array(
  118. "loc" => site_url(lcfirst($class) . '/' . $page),
  119. "lastmod" => date("c", time()),
  120. "changefreq" => "hourly",
  121. "priority" => "0.8"
  122. );
  123. $this->add_item($item);
  124. }
  125. }
  126. return TRUE;
  127. }
  128. /**
  129. * Generates the sitemap XML data
  130. *
  131. * @param string $file_name (optional) if file name is supplied the XML data is saved in it otherwise returned as a string
  132. * @param bool $gzip (optional) compress sitemap, overwrites config item 'sitemaps_gzip'
  133. * @access public
  134. * @return string | bool
  135. */
  136. function build($file_name = null, $gzip = NULL)
  137. {
  138. $map = $this->CI->config->item('sitemaps_header') . "\n";
  139. foreach ($this->items as $item)
  140. {
  141. $item['loc'] = htmlentities($item['loc'], ENT_QUOTES);
  142. $map .= "\t<url>\n\t\t<loc>" . $item['loc'] . "</loc>\n";
  143. $attributes = array("lastmod", "changefreq", "priority");
  144. foreach ($attributes AS $attr)
  145. {
  146. if (isset($item[$attr]))
  147. {
  148. $map .= "\t\t<$attr>" . $item[$attr] . "</$attr>\n";
  149. }
  150. }
  151. $map .= "\t</url>\n\n";
  152. }
  153. unset($this->items);
  154. $map .= $this->CI->config->item('sitemaps_footer');
  155. if (!is_null($file_name))
  156. {
  157. $fh = fopen($file_name, 'w');
  158. if (!$fh)
  159. {
  160. $this->set_error('Could not open sitemaps file for writing: ' . $file_name);
  161. return FALSE;
  162. }
  163. if (!fwrite($fh, $map))
  164. {
  165. $this->set_error('Error writing to sitemaps file: ' . $file_name);
  166. return FALSE;
  167. }
  168. fclose($fh);
  169. if ($this->CI->config->item('sitemaps_filesize_error') && filesize($file_name) > 1024 * 1024 * 10)
  170. {
  171. $this->set_error('Your sitemap is bigger than 10MB, most search engines will not accept it.');
  172. return FALSE;
  173. }
  174. if ($gzip OR (is_null($gzip) && $this->CI->config->item('sitemaps_gzip')))
  175. {
  176. $gzdata = gzencode($map, 9);
  177. $file_gzip = str_replace("{file_name}", $file_name, $this->CI->config->item('sitemaps_gzip_path'));
  178. $fp = fopen($file_gzip, "w");
  179. if (!$fp)
  180. {
  181. $this->set_error('Unable to open gzipped file path for writing: ' . $fp);
  182. return FALSE;
  183. }
  184. if (!fwrite($fp, $gzdata))
  185. {
  186. $this->set_error('Error writing to gzipped sitemaps file: ' . $fp);
  187. return FALSE;
  188. }
  189. fclose($fp);
  190. // Delete the uncompressed sitemap
  191. if (!unlink($file_name))
  192. {
  193. $this->set_error('Unable to remove uncompressed sitemap: ' . $file_name);
  194. }
  195. return $file_gzip;
  196. }
  197. return $file_name;
  198. }
  199. else
  200. {
  201. return $map;
  202. }
  203. }
  204. /**
  205. * Generate a sitemap index file pointing to other sitemaps you previously built
  206. *
  207. * @param array $urls array of urls, each being an array with at least a loc index
  208. * @param string $file_name (optional) if file name is supplied the XML data is saved in it otherwise returned as a string
  209. * @param bool $gzip (optional) compress sitemap, overwrites config item 'sitemaps_gzip'
  210. * @access public
  211. * @return string | bool
  212. */
  213. function build_index($urls, $file_name = null, $gzip = null)
  214. {
  215. $index = $this->CI->config->item('sitemaps_index_header') . "\n";
  216. foreach ($urls as $url)
  217. {
  218. $url['loc'] = htmlentities($url['loc'], ENT_QUOTES);
  219. $index .= "\t<sitemap>\n\t\t<loc>" . $url['loc'] . "</loc>\n";
  220. if (isset($url['lastmod']))
  221. {
  222. $index .= "\t\t<lastmod>" . $url['lastmod'] . "</lastmod>\n";
  223. }
  224. $index .= "\t</sitemap>\n\n";
  225. }
  226. $index .= $this->CI->config->item('sitemaps_index_footer');
  227. if (!is_null($file_name))
  228. {
  229. $fh = fopen($file_name, 'w');
  230. if (!$fh)
  231. {
  232. $this->set_error('Could not open sitemaps index for writing: ' . $fh);
  233. return FALSE;
  234. }
  235. if (!fwrite($fh, $index))
  236. {
  237. $this->set_error('Could not write to sitemaps index: ' . $fh);
  238. return FALSE;
  239. }
  240. fclose($fh);
  241. if ($this->CI->config->item('sitemaps_filesize_error') && filesize($file_name) > 1024 * 1024 * 10)
  242. {
  243. $this->set_error('Your sitemap index is bigger than 10MB, most search engines will not accept it.');
  244. return FALSE;
  245. }
  246. if ($gzip OR (is_null($gzip) && $this->CI->config->item('sitemaps_index_gzip')))
  247. {
  248. $gzdata = gzencode($index, 9);
  249. $file_gzip = str_replace("{file_name}", $file_name, $this->CI->config->item('sitemaps_index_gzip_path'));
  250. $fp = fopen($file_gzip, "w");
  251. if (!$fp)
  252. {
  253. $this->set_error('Could not open gzipped sitemaps index for writing: ' . $fp);
  254. return FALSE;
  255. }
  256. if (!fwrite($fp, $gzdata))
  257. {
  258. $this->set_error('Could not write to gzipped sitemaps index: ' . $fp);
  259. return FALSE;
  260. }
  261. fclose($fp);
  262. // Delete the uncompressed sitemap index
  263. if (!unlink($file_name))
  264. {
  265. $this->set_error('Could not write to remove uncompressed sitemaps index: ' . $file_name);
  266. }
  267. return $file_gzip;
  268. }
  269. return $file_name;
  270. }
  271. else
  272. {
  273. return $index;
  274. }
  275. }
  276. /**
  277. * Notify search engines of your updates sitemap
  278. *
  279. * @param string $url_xml absolute URL of your sitemap, use Codeigniter's site_url()
  280. * @param array $search_engines array of search engines to ping, see config file for notes
  281. * @access public
  282. * @return array HTTP reponse codes
  283. */
  284. function ping($url_xml, $search_engines = NULL)
  285. {
  286. if (is_null($search_engines))
  287. {
  288. $search_engines = $this->CI->config->item('sitemaps_search_engines');
  289. }
  290. $statuses = array();
  291. foreach ($search_engines AS $engine)
  292. {
  293. $status = 0;
  294. if ($fp = @fsockopen($engine['host'], 80))
  295. {
  296. $engine['url'] = empty($engine['url']) ? "/ping?sitemap=" : $engine['url'];
  297. $req = 'GET ' . $engine['url'] .
  298. urlencode($url_xml) . " HTTP/1.1\r\n" .
  299. "Host: " . $engine['host'] . "\r\n" .
  300. $this->CI->config->item('sitemaps_user_agent') .
  301. "Connection: Close\r\n\r\n";
  302. fwrite($fp, $req);
  303. while (!feof($fp))
  304. {
  305. if (@preg_match('~^HTTP/\d\.\d (\d+)~i', fgets($fp, 128), $m))
  306. {
  307. $status = intval($m[1]);
  308. break;
  309. }
  310. }
  311. fclose($fp);
  312. }
  313. $statuses[] = array("host" => $engine['host'], "status" => $status, "request" => $req);
  314. }
  315. if ($this->CI->config->item('sitemaps_log_http_responses') OR $this->CI->config->item('sitemaps_debug'))
  316. {
  317. foreach ($statuses AS $reponse)
  318. {
  319. $message = "Sitemaps: " . $reponse['host'] . " responded with HTTP status " . $reponse['status'];
  320. if ($this->CI->config->item('sitemaps_log_http_responses'))
  321. {
  322. $level = $reponse['status'] == 200 ? 'debug' : 'error';
  323. log_message($level, $message);
  324. }
  325. if ($this->CI->config->item('sitemaps_debug'))
  326. {
  327. echo "<p>" . $message . " after request:</p>\n<pre>" . $reponse['request'] . "</pre>\n\n";
  328. }
  329. }
  330. }
  331. return $statuses;
  332. }
  333. /**
  334. * Show error messages
  335. *
  336. * @access public
  337. * @param string
  338. * @return string
  339. */
  340. function display_errors($open = '<p>', $close = '</p>')
  341. {
  342. $str = '';
  343. foreach ($this->error_msg as $val)
  344. {
  345. $str .= $open . $val . $close;
  346. }
  347. return $str;
  348. }
  349. /**
  350. * Set error message
  351. *
  352. * @access public
  353. * @param string
  354. * @return void
  355. */
  356. function set_error($msg)
  357. {
  358. $CI = & get_instance();
  359. if (is_array($msg))
  360. {
  361. foreach ($msg as $val)
  362. {
  363. $this->error_msg[] = $val;
  364. log_message('error', $val);
  365. }
  366. }
  367. else
  368. {
  369. $this->error_msg[] = $msg;
  370. log_message('error', $msg);
  371. }
  372. }
  373. }