2 // PukiWiki - Yet another WikiWikiWeb clone
5 // 2002-2022 PukiWiki Development Team
6 // 2002 Y.MASUI GPL2 http://masui.net/pukiwiki/ masui@masui.net
9 // Counter plugin (per page)
11 // Counter file's suffix
12 define('PLUGIN_COUNTER_SUFFIX', '.count');
13 // Ignore REMOTE_ADDR : FALSE(0) or TRUE(1) for reverse proxy / load balancer environment
14 define('PLUGIN_COUNTER_IGNORE_REMOTE_ADDR', 0);
15 // Use Database (1) or not (0)
16 define('PLUGIN_COUNTER_USE_DB', 0);
17 // Database Connection setting
18 define('PLUGIN_COUNTER_DB_CONNECT_STRING', 'sqlite:counter/counter.db');
19 define('PLUGIN_COUNTER_DB_USERNAME', '');
20 define('PLUGIN_COUNTER_DB_PASSWORD', '');
21 global $plugin_counter_db_options;
22 $plugin_counter_db_options = null;
24 // $plugin_counter_db_options = array(PDO::MYSQL_ATTR_INIT_COMMAND =>
25 // "SET NAMES utf8mb4 COLLATE utf8mb4_bin");
27 define('PLUGIN_COUNTER_DB_TABLE_NAME_PREFIX', '');
28 // Async Counter by JavaScript: FALSE(0; Default) or TRUE(1)
29 define('PLUGIN_COUNTER_ASYNC', 0);
31 define('PLUGIN_COUNTER_ASYNC_HTML_TOTAL',
32 '<span class="_plugin_counter_item _plugin_counter_item_total"></span>');
33 define('PLUGIN_COUNTER_ASYNC_HTML_TODAY',
34 '<span class="_plugin_counter_item _plugin_counter_item_today"></span>');
35 define('PLUGIN_COUNTER_ASYNC_HTML_YESTERDAY',
36 '<span class="_plugin_counter_item _plugin_counter_item_yesterday"></span>');
38 if (PLUGIN_COUNTER_USE_DB) {
39 ini_set('default_socket_timeout', 2);
43 function plugin_counter_inline()
47 // BugTrack2/106: Only variables can be passed by reference from PHP 5.0.5
48 $args = func_get_args(); // with array_shift()
50 $arg = strtolower(array_shift($args));
51 if (PLUGIN_COUNTER_ASYNC) {
53 case '' : /*FALLTHROUGH*/
55 return PLUGIN_COUNTER_ASYNC_HTML_TOTAL;
57 return PLUGIN_COUNTER_ASYNC_HTML_TODAY;
59 return PLUGIN_COUNTER_ASYNC_HTML_YESTERDAY;
61 return htmlsc('&counter([total|today|yesterday]);');
65 case '' : $arg = 'total'; /*FALLTHROUGH*/
66 case 'total': /*FALLTHROUGH*/
67 case 'today': /*FALLTHROUGH*/
69 $counter = plugin_counter_get_count($vars['page']);
70 return $counter[$arg];
72 return htmlsc('&counter([total|today|yesterday]);');
77 function plugin_counter_convert()
80 if (PLUGIN_COUNTER_ASYNC) {
81 $total_html = PLUGIN_COUNTER_ASYNC_HTML_TOTAL;
82 $today_html = PLUGIN_COUNTER_ASYNC_HTML_TODAY;
83 $yesterday_html = PLUGIN_COUNTER_ASYNC_HTML_YESTERDAY;
86 Counter: ${total_html},
88 yesterday: ${yesterday_html}
92 $counter = plugin_counter_get_count($vars['page']);
95 Counter: {$counter['total']},
96 today: {$counter['today']},
97 yesterday: {$counter['yesterday']}
102 function plugin_counter_action()
105 if (! PLUGIN_COUNTER_ASYNC) {
106 die('Invalid counter action');
109 $page = isset($vars['page']) ? $vars['page'] : '';
110 if ($page && is_page($page) && is_page_readable($page)) {
111 $c = plugin_counter_get_count($page);
114 'total' => '' . $c['total'],
115 'today' => '' . $c['today'],
116 'yesterday' => '' . $c['yesterday'],
121 header('Content-Type: application/json; charset=UTF-8');
122 print(json_encode($obj, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
123 | JSON_FORCE_OBJECT));
128 function plugin_counter_get_count($page)
130 global $vars, $plugin_counter_db_options, $plugin;
131 static $counters = array();
133 $page_counter_t = PLUGIN_COUNTER_DB_TABLE_NAME_PREFIX . 'page_counter';
135 if (! isset($default))
138 'date' => get_date('Y/m/d'),
143 if (! is_page($page)) return $default;
144 if (isset($counters[$page])) return $counters[$page];
147 $counters[$page] = $default;
149 $c = & $counters[$page];
151 if (PLUGIN_COUNTER_USE_DB) {
152 if (SOURCE_ENCODING !== 'UTF-8') {
153 die('counter.inc.php: Database counter is only available in UTF-8 mode');
155 $is_new_page = false;
157 $pdo = new PDO(PLUGIN_COUNTER_DB_CONNECT_STRING,
158 PLUGIN_COUNTER_DB_USERNAME, PLUGIN_COUNTER_DB_PASSWORD,
159 $plugin_counter_db_options);
160 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
161 $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
162 $pdo->setAttribute(PDO::ATTR_TIMEOUT, 5);
163 $stmt = $pdo->prepare(
164 "SELECT total, update_date,
165 today_viewcount, yesterday_viewcount, remote_addr
169 $stmt->execute(array($page));
174 $c['ip'] = $r['remote_addr'];
175 $c['date'] = $r['update_date'];
176 $c['yesterday'] = intval($r['yesterday_viewcount']);
177 $c['today'] = intval($r['today_viewcount']);
178 $c['total'] = intval($r['total']);
179 $stmt->closeCursor();
181 } catch (Exception $e) {
183 $db_error = '(DBError)';
185 'total' => $db_error,
187 'today' => $db_error,
188 'yesterday' => $db_error,
193 $file = COUNTER_DIR . encode($page) . PLUGIN_COUNTER_SUFFIX;
194 pkwk_touch_file($file);
195 $fp = fopen($file, 'r+')
196 or die('counter.inc.php: Cannot open COUNTER_DIR/' . basename($file));
197 set_file_buffer($fp, 0);
202 foreach (array_keys($default) as $key) {
204 $c[$key] = rtrim(fgets($fp, 256));
205 if (feof($fp)) break;
210 $remote_addr = $_SERVER['REMOTE_ADDR'];
211 if (! $remote_addr) {
215 if ($c['date'] != $default['date']) {
217 $is_yesterday = ($c['date'] == get_date('Y/m/d', UTIME - 24 * 60 * 60));
218 $c[$page]['ip'] = $remote_addr;
219 $c['date'] = $default['date'];
220 $c['yesterday'] = $is_yesterday ? $c['today'] : 0;
224 } else if ($c['ip'] != $remote_addr || PLUGIN_COUNTER_IGNORE_REMOTE_ADDR) {
227 $c['ip'] = $remote_addr;
232 $is_count_target_plugin = ($plugin == 'read' || $plugin == 'counter');
233 if (PLUGIN_COUNTER_USE_DB) {
234 if ($modify && $is_count_target_plugin) {
238 $add_stmt = $pdo->prepare(
239 "INSERT INTO $page_counter_t
240 (page_name, total, update_date, today_viewcount,
241 yesterday_viewcount, remote_addr)
242 VALUES (?, ?, ?, ?, ?, ?)"
244 $r_add = $add_stmt->execute(array($page, $c['total'],
245 $c['date'], $c['today'], $c['yesterday'], $c['ip']));
246 } else if ($count_up) {
247 // Update on counting up 'total'
248 $upd_stmt = $pdo->prepare(
249 "UPDATE $page_counter_t
250 SET total = total + 1,
253 yesterday_viewcount = ?,
257 $r_upd = $upd_stmt->execute(array($c['date'],
258 $c['today'], $c['yesterday'], $c['ip'], $page));
260 } catch (PDOException $e) {
261 foreach (array_keys($c) as $key) {
262 $c[$key] .= '(DBError)';
268 if ($modify && $is_count_target_plugin) {
271 foreach (array_keys($default) as $key)
272 fputs($fp, $c[$key] . "\n");
281 function plugin_counter_get_popular_list($today, $except, $max) {
282 if (PLUGIN_COUNTER_USE_DB === 0) {
283 return plugin_counter_get_popular_list_file($today, $except, $max);
285 return plugin_counter_get_popular_list_db($today, $except, $max);
289 function plugin_counter_get_popular_list_file($today, $except, $max) {
292 $except_quote = str_replace('#', '\#', $except);
293 foreach (get_existpages(COUNTER_DIR, '.count') as $file=>$page) {
294 if (($except != '' && preg_match("#$except_quote#", $page)) ||
295 $page == $whatsnew || check_non_list($page) ||
299 $array = file(COUNTER_DIR . $file);
300 $count = rtrim($array[0]);
301 $date = rtrim($array[1]);
302 $today_count = rtrim($array[2]);
305 // $pageが数値に見える(たとえばencode('BBS')=424253)とき、
306 // array_splice()によってキー値が変更されてしまうのを防ぐ
308 if ($today == $date) $counters['_' . $page] = $today_count;
310 $counters['_' . $page] = $count;
314 asort($counters, SORT_NUMERIC);
316 // BugTrack2/106: Only variables can be passed by reference from PHP 5.0.5
317 $counters = array_reverse($counters, TRUE); // with array_splice()
318 $counters = array_splice($counters, 0, $max);
322 function plugin_counter_get_popular_list_db($today, $except, $max) {
324 $page_counter_t = PLUGIN_COUNTER_DB_TABLE_NAME_PREFIX . 'page_counter';
326 $order_by_c = 'today_viewcount';
328 $order_by_c = 'total';
332 $pdo = new PDO(PLUGIN_COUNTER_DB_CONNECT_STRING,
333 PLUGIN_COUNTER_DB_USERNAME, PLUGIN_COUNTER_DB_PASSWORD,
334 $plugin_counter_db_options);
335 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
336 $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
337 $pdo->setAttribute(PDO::ATTR_TIMEOUT, 5);
339 $stmt = $pdo->prepare(
340 "SELECT page_name, total, update_date,
341 today_viewcount, yesterday_viewcount
343 WHERE update_date = ?
344 ORDER BY $order_by_c DESC
348 $stmt = $pdo->prepare(
349 "SELECT page_name, total, update_date,
350 today_viewcount, yesterday_viewcount
352 ORDER BY $order_by_c DESC
356 $except_quote = str_replace('#', '\#', $except);
359 $stmt->execute(array($today, $limit));
361 $stmt->execute(array($limit));
363 foreach ($stmt as $r) {
364 $page = $r['page_name'];
365 if (($except != '' && preg_match("#$except_quote#", $page)) ||
366 $page == $whatsnew || check_non_list($page) ||
371 $counters['_' . $page] = $r['today_viewcount'];
373 $counters['_' . $page] = $r['total'];
376 $stmt->closeCursor();
377 return array_splice($counters, 0, $max);
378 } catch (Exception $e) {
379 die('counter.inc.php: Error occurred on getting pupular pages');
383 function plugin_counter_page_rename($pages) {
384 global $plugin_counter_db_options;
385 if (PLUGIN_COUNTER_USE_DB !== 0) {
386 $page_counter_t = PLUGIN_COUNTER_DB_TABLE_NAME_PREFIX . 'page_counter';
387 $pdo = new PDO(PLUGIN_COUNTER_DB_CONNECT_STRING,
388 PLUGIN_COUNTER_DB_USERNAME, PLUGIN_COUNTER_DB_PASSWORD,
389 $plugin_counter_db_options);
390 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
391 $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
392 $stmt_delete = $pdo->prepare(
393 "DELETE FROM $page_counter_t
396 $stmt_rename = $pdo->prepare(
397 "UPDATE $page_counter_t
401 foreach ($pages as $old_name=>$new_name) {
402 $stmt_delete->execute(array($new_name));
403 $stmt_rename->execute(array($new_name, $old_name));
409 * php -r "include 'plugin/counter.inc.php'; plugin_counter_tool_setup_table();"
411 function plugin_counter_tool_setup_table() {
412 global $plugin_counter_db_options;
413 $page_counter_t = PLUGIN_COUNTER_DB_TABLE_NAME_PREFIX . 'page_counter';
414 $pdo = new PDO(PLUGIN_COUNTER_DB_CONNECT_STRING,
415 PLUGIN_COUNTER_DB_USERNAME, PLUGIN_COUNTER_DB_PASSWORD,
416 $plugin_counter_db_options);
417 $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
418 $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
420 "CREATE TABLE $page_counter_t (
421 page_name VARCHAR(190) PRIMARY KEY,
422 total INTEGER NOT NULL,
423 update_date VARCHAR(20) NOT NULL,
424 today_viewcount INTEGER NOT NULL,
425 yesterday_viewcount INTEGER NOT NULL,
426 remote_addr VARCHAR(100)