PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/misc/others/geoipUpdateRows.php

https://github.com/CodeYellowBV/piwik
PHP | 233 lines | 196 code | 25 blank | 12 comment | 34 complexity | f35ef4770cebf00f9cf6b733aef3b14a MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. use Piwik\Common;
  3. use Piwik\Config;
  4. use Piwik\Db;
  5. use Piwik\FrontController;
  6. use Piwik\IP;
  7. use Piwik\Log;
  8. use Piwik\Piwik;
  9. use Piwik\Plugins\UserCountry\LocationProvider\GeoIp\Pecl;
  10. use Piwik\Plugins\UserCountry\LocationProvider;
  11. use Piwik\Plugins\UserCountry\LocationProvider\GeoIp\Php;
  12. require_once './cli-script-bootstrap.php';
  13. ini_set("memory_limit", "512M");
  14. $query = "SELECT count(*) FROM " . Common::prefixTable('log_visit');
  15. $count = Db::fetchOne($query);
  16. // when script run via browser, check for Super User & output html page to do conversion via AJAX
  17. if (!Common::isPhpCliMode()) {
  18. try {
  19. Piwik::checkUserHasSuperUserAccess();
  20. } catch (Exception $e) {
  21. Log::error('[error] You must be logged in as Super User to run this script. Please login in to Piwik and refresh this page.');
  22. exit;
  23. }
  24. // the 'start' query param will be supplied by the AJAX requests, so if it's not there, the
  25. // user is viewing the page in the browser.
  26. if (Common::getRequestVar('start', false) === false) {
  27. // output HTML page that runs update via AJAX
  28. ?>
  29. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  30. <html>
  31. <head>
  32. <script type="text/javascript" src="../../libs/jquery/jquery.js"></script>
  33. <script type="text/javascript">
  34. (function ($) {
  35. var count = <?php echo $count; ?>;
  36. var doIteration = function (start) {
  37. if (start >= count) {
  38. return;
  39. }
  40. var end = Math.min(start + 100, count);
  41. $.ajax({
  42. type: 'POST',
  43. url: 'geoipUpdateRows.php',
  44. data: {
  45. start: start,
  46. end: end
  47. },
  48. async: true,
  49. error: function (xhr, status, error) {
  50. $('body')
  51. .append(xhr.responseText)
  52. .append('<div style="color:red"><strong>An error occured!</strong></div>');
  53. },
  54. success: function (response) {
  55. doIteration(end);
  56. $('body').append(response);
  57. var body = $('body')[0];
  58. body.scrollTop = body.scrollHeight;
  59. }
  60. });
  61. };
  62. doIteration(0);
  63. }(jQuery));
  64. </script>
  65. </head>
  66. <body>
  67. </body>
  68. </html>
  69. <?php
  70. exit;
  71. } else {
  72. $start = Common::getRequestVar('start', 0, 'int');
  73. $end = min($count, Common::getRequestVar('end', $count, 'int'));
  74. $limit = $end - $start;
  75. }
  76. } else // command line
  77. {
  78. $start = 0;
  79. $end = $count;
  80. $limit = 1000;
  81. }
  82. function geoipUpdateError($message)
  83. {
  84. Log::error($message);
  85. if (!Common::isPhpCliMode()) {
  86. @header('HTTP/1.1 500 Internal Server Error', $replace = true, $responseCode = 500);
  87. }
  88. exit;
  89. }
  90. // only display notes if on command line (where start will == 0 for that part of script) or on
  91. // first AJAX call by browser
  92. $displayNotes = $start == 0;
  93. // try getting the pecl location provider
  94. $provider = new Pecl();
  95. if (!$provider->isAvailable()) {
  96. if ($displayNotes) {
  97. Log::info("[note] The GeoIP PECL extension is not installed.");
  98. }
  99. $provider = null;
  100. } else {
  101. $workingOrError = $provider->isWorking();
  102. if ($workingOrError !== true) {
  103. if ($displayNotes) {
  104. Log::info("[note] The GeoIP PECL extension is broken: $workingOrError");
  105. }
  106. if (Common::isPhpCliMode()) {
  107. Log::info("[note] Make sure your command line PHP is configured to use the PECL extension.");
  108. }
  109. $provider = null;
  110. }
  111. }
  112. // use php api if pecl extension cannot be used
  113. if (is_null($provider)) {
  114. if ($displayNotes) {
  115. Log::info("[note] Falling back to PHP API. This may become too slow for you. If so, you can read this link on how to install the PECL extension: http://piwik.org/faq/how-to/#faq_164");
  116. }
  117. $provider = new Php();
  118. if (!$provider->isAvailable()) {
  119. if ($displayNotes) {
  120. Log::info("[note] The GeoIP PHP API is not available. This means you do not have a GeoIP location database in your ./misc directory. The database must be named either GeoIP.dat or GeoIPCity.dat based on the type of database it is.");
  121. }
  122. $provider = null;
  123. } else {
  124. $workingOrError = $provider->isWorking();
  125. if ($workingOrError !== true) {
  126. if ($displayNotes) {
  127. Log::info("[note] The GeoIP PHP API is broken: $workingOrError");
  128. }
  129. $provider = null;
  130. }
  131. }
  132. }
  133. if (is_null($provider)) {
  134. geoipUpdateError("\n[error] There is no location provider that can be used with this script. Only the GeoIP PECL module or the GeoIP PHP API can be used at present. Please install and configure one of these first.");
  135. }
  136. $info = $provider->getInfo();
  137. if ($displayNotes) {
  138. Log::info("[note] Found working provider: {$info['id']}");
  139. }
  140. // perform update
  141. $logVisitFieldsToUpdate = array('location_country' => LocationProvider::COUNTRY_CODE_KEY,
  142. 'location_region' => LocationProvider::REGION_CODE_KEY,
  143. 'location_city' => LocationProvider::CITY_NAME_KEY,
  144. 'location_latitude' => LocationProvider::LATITUDE_KEY,
  145. 'location_longitude' => LocationProvider::LONGITUDE_KEY);
  146. if ($displayNotes) {
  147. Log::info("\n$count rows to process in " . Common::prefixTable('log_visit')
  148. . " and " . Common::prefixTable('log_conversion') . "...");
  149. }
  150. flush();
  151. for (; $start < $end; $start += $limit) {
  152. $rows = Db::fetchAll("SELECT idvisit, location_ip, " . implode(',', array_keys($logVisitFieldsToUpdate)) . "
  153. FROM " . Common::prefixTable('log_visit') . "
  154. LIMIT $start, $limit");
  155. if (!count($rows)) {
  156. continue;
  157. }
  158. foreach ($rows as $i => $row) {
  159. $fieldsToSet = array();
  160. foreach ($logVisitFieldsToUpdate as $field => $ignore) {
  161. if (empty($fieldsToSet[$field])) {
  162. $fieldsToSet[] = $field;
  163. }
  164. }
  165. // skip if it already has a location
  166. if (empty($fieldsToSet)) {
  167. continue;
  168. }
  169. $ip = IP::N2P($row['location_ip']);
  170. $location = $provider->getLocation(array('ip' => $ip));
  171. if (!empty($location[LocationProvider::COUNTRY_CODE_KEY])) {
  172. $location[LocationProvider::COUNTRY_CODE_KEY] =
  173. strtolower($location[LocationProvider::COUNTRY_CODE_KEY]);
  174. }
  175. $row['location_country'] = strtolower($row['location_country']);
  176. $columnsToSet = array();
  177. $bind = array();
  178. foreach ($logVisitFieldsToUpdate as $column => $locationKey) {
  179. if (!empty($location[$locationKey])
  180. && $location[$locationKey] != $row[$column]
  181. ) {
  182. $columnsToSet[] = $column . ' = ?';
  183. $bind[] = $location[$locationKey];
  184. }
  185. }
  186. if (empty($columnsToSet)) {
  187. continue;
  188. }
  189. $bind[] = $row['idvisit'];
  190. // update log_visit
  191. $sql = "UPDATE " . Common::prefixTable('log_visit') . "
  192. SET " . implode(', ', $columnsToSet) . "
  193. WHERE idvisit = ?";
  194. Db::query($sql, $bind);
  195. // update log_conversion
  196. $sql = "UPDATE " . Common::prefixTable('log_conversion') . "
  197. SET " . implode(', ', $columnsToSet) . "
  198. WHERE idvisit = ?";
  199. Db::query($sql, $bind);
  200. }
  201. Log::info(round($start * 100 / $count) . "% done...");
  202. flush();
  203. }
  204. if ($start >= $count) {
  205. Log::info("100% done!");
  206. Log::info("");
  207. Log::info("[note] Now that you've geolocated your old visits, you need to force your reports to be re-processed. See this FAQ entry: http://piwik.org/faq/how-to/#faq_59");
  208. }