WordPressにカウンターを設置(同じIPはカウントしない)

blognplusからWordPressへ移行した際に良いカウンターがなかったので自作しました。
自作と言っても元はblogplusのモジュール用に作られた「カルカン(お手軽カウンター)」をWordPress用に改造したものです。
作者様に改造品の掲載許可を頂きたかったのですが、既にHPも閉鎖されているようで連絡が取れず…
もし作者様がこのブログを見られましたら許可・非許可のコメントを頂ければ幸いです。

「カルカン(お手軽カウンター)」で出来ることは以下の3つです。
1.トータルのカウント数を表示
2.当日のカウント数を表示
3.昨日のカウント数を表示
全てのカウント数で同じ日の同じIPからのアクセスは、何度アクセスしても1回としてカウントされます。
いわゆる「延べ数」なPVとは違い、実際の訪問者数が分かります。
(BOTかどうかまでは分かりませんが)
尚、7行目のIPアドレス保存件数しかIPを保存しないので、人気のあるサイトは数を大目にしないと直ぐに同じIPでもカウントアップしてしまいます。

使い方は簡単で以下のソースコードをテーマのfunction.phpに追記するだけで、ウィジェットとして使用することが可能となります。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
/*
* カウンターウィジェット
*/
// 保存ファイル名
define('WIDGET_COUNTER_FILE', WP_CONTENT_DIR."/widget_counter_file");
// IPアドレス保存件数
define('WIDGET_COUNTER_REMOTE', 30);
class Counter_Widget extends WP_Widget {
/* コンストラクタ */
function __construct() {
parent::__construct(
false,
$name = 'カウンター',
array( 'description' => 'IP重複なしのカウンターです', )
);
}
/* 管理画面のウィジェット設定用コード */
function form($instance) {
?>
/* * カウンターウィジェット */ // 保存ファイル名 define('WIDGET_COUNTER_FILE', WP_CONTENT_DIR."/widget_counter_file"); // IPアドレス保存件数 define('WIDGET_COUNTER_REMOTE', 30); class Counter_Widget extends WP_Widget { /* コンストラクタ */ function __construct() { parent::__construct( false, $name = 'カウンター', array( 'description' => 'IP重複なしのカウンターです', ) ); } /* 管理画面のウィジェット設定用コード */ function form($instance) { ?>
/*
* カウンターウィジェット
*/
// 保存ファイル名
define('WIDGET_COUNTER_FILE', WP_CONTENT_DIR."/widget_counter_file");
// IPアドレス保存件数
define('WIDGET_COUNTER_REMOTE', 30);
class Counter_Widget extends WP_Widget {
	/* コンストラクタ */
	function __construct() {
		parent::__construct(
			false,
			$name = 'カウンター',
			array( 'description' => 'IP重複なしのカウンターです', )
		);
	}
	
	/* 管理画面のウィジェット設定用コード */
	function form($instance) {
	?>

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?php
}
/* 管理画面のウィジェット設定を変更した時の処理 */
function update($new_instance, $old_instance) {
$instance = $old_instance;
$instance['title'] = strip_tags($new_instance['title']);
return $instance;
}
/* ウィジェットを配置した時の表示用コード */
function widget($args, $instance) {
extract( $args );
// カウント取得
$counter = $this->widgetCounterGetCount('default');
$out_count = '';
$out_count .= "合計: ".$counter['total']."\n";
$out_count .= "今日: ".$counter['today']."\n";
$out_count .= "昨日: ".$counter['yesterday']."\n";
if($instance['title'] != ''){
$title = apply_filters('widget_title', $instance['title']);
}
echo $before_widget;
if( $title ){
echo $before_title . $title . $after_title;
}
echo $out_count;
echo $after_widget;
}
/* 日付を取得-----------------------------------------------------------------*/
function widgetCounterGetDate($format, $timestamp = NULL) {
$format = preg_replace('/(?<!\\\)T/', preg_replace('/(.)/', '\\\$1', 'JST'), $format);
$time = 9 * 3600 + (($timestamp !== NULL) ? $timestamp : time() - date('Z'));
return date($format, $time);
}
/* カウンターの値の取得及びカウントアップ-------------------------------------*/
function widgetCounterGetCount($page) {
$ipMax = (WIDGET_COUNTER_REMOTE<1) ? 1 : WIDGET_COUNTER_REMOTE;
$default = array(
'total' => 0,
'date' => $this->widgetCounterGetDate('Y/m/d'),
'today' => 0,
'yesterday' => 0
);
$counters = array();
$couterns[$page] = $default;
for ($i=0; $i<$ipMax; $i++) {
$default["ip$i"] = '';
}
$file = WIDGET_COUNTER_FILE.'.'.$page.'.dat';
$modify = FALSE; //更新フラグ
if (! $fp = @fopen($file, file_exists($file) ? 'r+' : 'w+')) return $default;
set_file_buffer($fp, 0);
flock($fp, LOCK_EX);
rewind($fp);
foreach ($default as $key=>$val) {
//データ取得
$counters[$page][$key] = rtrim(fgets($fp, 256));
if (feof($fp)) break;
}
if ($counters[$page]['date'] != $default['date']) {
// 新しい日付
$modify = TRUE;
$yesterday = ($counters[$page]['date'] == $this->widgetCounterGetDate('Y/m/d', (time() - date('Z') - 3600 * 24)));
for ($i=$ipMax-1; $i>0; $i--) {
$counters[$page]["ip$i"] = '';
}
$counters[$page]['ip0'] = $_SERVER['REMOTE_ADDR'];
$counters[$page]['date'] = $default['date'];
$counters[$page]['yesterday'] = $yesterday ? $counters[$page]['today'] : 0;
$counters[$page]['today'] = 1;
$counters[$page]['total']++;
} else {
$newer = TRUE;
for ($i=0; $i<$ipMax; $i++) {
if ($counters[$page]["ip$i"] == $_SERVER['REMOTE_ADDR']) {
$newer = FALSE;
break;
}
}
if ($newer) {
// カウントアップ
$modify = TRUE;
for ($i=$ipMax-1; $i>0; $i--) {
$j = $i-1;
$counters[$page]["ip$i"] = $counters[$page]["ip$j"];
}
$counters[$page]['ip0'] = $_SERVER['REMOTE_ADDR'];
$counters[$page]['today']++;
$counters[$page]['total']++;
}
}
// 更新されていた場合
if ($modify) {
rewind($fp);
ftruncate($fp, 0);
foreach (array_keys($default) as $key)
fputs($fp, $counters[$page][$key] . "\n");
}
flock($fp, LOCK_UN);
fclose($fp);
return $counters[$page];
}
}
/* カウンターウィジェットを使えるように登録 */
register_widget('Counter_Widget');
<?php } /* 管理画面のウィジェット設定を変更した時の処理 */ function update($new_instance, $old_instance) { $instance = $old_instance; $instance['title'] = strip_tags($new_instance['title']); return $instance; } /* ウィジェットを配置した時の表示用コード */ function widget($args, $instance) { extract( $args ); // カウント取得 $counter = $this->widgetCounterGetCount('default'); $out_count = ''; $out_count .= "合計: ".$counter['total']."\n"; $out_count .= "今日: ".$counter['today']."\n"; $out_count .= "昨日: ".$counter['yesterday']."\n"; if($instance['title'] != ''){ $title = apply_filters('widget_title', $instance['title']); } echo $before_widget; if( $title ){ echo $before_title . $title . $after_title; } echo $out_count; echo $after_widget; } /* 日付を取得-----------------------------------------------------------------*/ function widgetCounterGetDate($format, $timestamp = NULL) { $format = preg_replace('/(?<!\\\)T/', preg_replace('/(.)/', '\\\$1', 'JST'), $format); $time = 9 * 3600 + (($timestamp !== NULL) ? $timestamp : time() - date('Z')); return date($format, $time); } /* カウンターの値の取得及びカウントアップ-------------------------------------*/ function widgetCounterGetCount($page) { $ipMax = (WIDGET_COUNTER_REMOTE<1) ? 1 : WIDGET_COUNTER_REMOTE; $default = array( 'total' => 0, 'date' => $this->widgetCounterGetDate('Y/m/d'), 'today' => 0, 'yesterday' => 0 ); $counters = array(); $couterns[$page] = $default; for ($i=0; $i<$ipMax; $i++) { $default["ip$i"] = ''; } $file = WIDGET_COUNTER_FILE.'.'.$page.'.dat'; $modify = FALSE; //更新フラグ if (! $fp = @fopen($file, file_exists($file) ? 'r+' : 'w+')) return $default; set_file_buffer($fp, 0); flock($fp, LOCK_EX); rewind($fp); foreach ($default as $key=>$val) { //データ取得 $counters[$page][$key] = rtrim(fgets($fp, 256)); if (feof($fp)) break; } if ($counters[$page]['date'] != $default['date']) { // 新しい日付 $modify = TRUE; $yesterday = ($counters[$page]['date'] == $this->widgetCounterGetDate('Y/m/d', (time() - date('Z') - 3600 * 24))); for ($i=$ipMax-1; $i>0; $i--) { $counters[$page]["ip$i"] = ''; } $counters[$page]['ip0'] = $_SERVER['REMOTE_ADDR']; $counters[$page]['date'] = $default['date']; $counters[$page]['yesterday'] = $yesterday ? $counters[$page]['today'] : 0; $counters[$page]['today'] = 1; $counters[$page]['total']++; } else { $newer = TRUE; for ($i=0; $i<$ipMax; $i++) { if ($counters[$page]["ip$i"] == $_SERVER['REMOTE_ADDR']) { $newer = FALSE; break; } } if ($newer) { // カウントアップ $modify = TRUE; for ($i=$ipMax-1; $i>0; $i--) { $j = $i-1; $counters[$page]["ip$i"] = $counters[$page]["ip$j"]; } $counters[$page]['ip0'] = $_SERVER['REMOTE_ADDR']; $counters[$page]['today']++; $counters[$page]['total']++; } } // 更新されていた場合 if ($modify) { rewind($fp); ftruncate($fp, 0); foreach (array_keys($default) as $key) fputs($fp, $counters[$page][$key] . "\n"); } flock($fp, LOCK_UN); fclose($fp); return $counters[$page]; } } /* カウンターウィジェットを使えるように登録 */ register_widget('Counter_Widget');
	<?php
	}

	/* 管理画面のウィジェット設定を変更した時の処理 */
	function update($new_instance, $old_instance) {
		$instance = $old_instance;
		$instance['title'] = strip_tags($new_instance['title']);
		return $instance;
	}

	/* ウィジェットを配置した時の表示用コード */
	function widget($args, $instance) {
		extract( $args );
		
		// カウント取得
		$counter = $this->widgetCounterGetCount('default');
		$out_count = '';
		$out_count .= "合計: ".$counter['total']."\n";
		$out_count .= "今日: ".$counter['today']."\n";
		$out_count .= "昨日: ".$counter['yesterday']."\n";

		if($instance['title'] != ''){
			$title = apply_filters('widget_title', $instance['title']);
		}
		echo $before_widget;
		if( $title ){
			echo $before_title . $title . $after_title;
		}
		echo $out_count;
		echo $after_widget;
	}
	
	/* 日付を取得-----------------------------------------------------------------*/
	function widgetCounterGetDate($format, $timestamp = NULL) {
		$format = preg_replace('/(?<!\\\)T/', preg_replace('/(.)/', '\\\$1', 'JST'), $format);

		$time = 9 * 3600 + (($timestamp !== NULL) ? $timestamp : time() - date('Z'));

		return date($format, $time);
	}

	/* カウンターの値の取得及びカウントアップ-------------------------------------*/
	function widgetCounterGetCount($page) {
		$ipMax				= (WIDGET_COUNTER_REMOTE<1) ? 1 : WIDGET_COUNTER_REMOTE;
		
		$default = array(
			'total'		=> 0,
			'date'		=> $this->widgetCounterGetDate('Y/m/d'),
			'today'		=> 0,
			'yesterday'	=> 0
		);
		
		$counters			= array();
		$couterns[$page]	= $default;
		
		for ($i=0; $i<$ipMax; $i++) {
			$default["ip$i"] = '';
		}
		
		$file = WIDGET_COUNTER_FILE.'.'.$page.'.dat';
		
		$modify = FALSE;	//更新フラグ
		
		if (! $fp = @fopen($file, file_exists($file) ? 'r+' : 'w+')) return $default;
		set_file_buffer($fp, 0);
		flock($fp, LOCK_EX);
		rewind($fp);
		foreach ($default as $key=>$val) {
			//データ取得
			$counters[$page][$key] = rtrim(fgets($fp, 256));
			if (feof($fp)) break;
		}
		if ($counters[$page]['date'] != $default['date']) {
			// 新しい日付
			$modify = TRUE;
			$yesterday = ($counters[$page]['date'] == $this->widgetCounterGetDate('Y/m/d', (time() - date('Z') - 3600 * 24)));
			for ($i=$ipMax-1; $i>0; $i--) {
				$counters[$page]["ip$i"] = '';
			}
			$counters[$page]['ip0']       = $_SERVER['REMOTE_ADDR'];
			$counters[$page]['date']      = $default['date'];
			$counters[$page]['yesterday'] = $yesterday ? $counters[$page]['today'] : 0;
			$counters[$page]['today']     = 1;
			$counters[$page]['total']++;
			
		} else {
			$newer = TRUE;
			for ($i=0; $i<$ipMax; $i++) {
				if ($counters[$page]["ip$i"] == $_SERVER['REMOTE_ADDR']) {
					$newer = FALSE;
					break;
				}
			}
			if ($newer) {
				// カウントアップ
				$modify = TRUE;
				for ($i=$ipMax-1; $i>0; $i--) {
					$j = $i-1;
					$counters[$page]["ip$i"] = $counters[$page]["ip$j"];
				}
				$counters[$page]['ip0'] = $_SERVER['REMOTE_ADDR'];
				$counters[$page]['today']++;
				$counters[$page]['total']++;
			}
		}
		
		// 更新されていた場合
		if ($modify) {
			rewind($fp);
			ftruncate($fp, 0);
			foreach (array_keys($default) as $key)
				fputs($fp, $counters[$page][$key] . "\n");
		}
		
		flock($fp, LOCK_UN);
		fclose($fp);

		return $counters[$page];
	}
}
/* カウンターウィジェットを使えるように登録 */
register_widget('Counter_Widget');

上記コードをfunction.phpに登録すると、テーマのカスタマイズのウィジェットで選択して使用可能となります。

カウンタの数はデータベースではなくファイルに保存されます。
ファイルの保存場所は「wp-content」の直下でファイル名は「widget_counter_file.default.dat」です。
テーマを変更しても変更したテーマのfunction.phpに上記コードを書けば、カウンター数の内容を引き継ぐことが出来ます。

設置後初回だけはファイルが存在せずにエラーが出るかもしれませんが、自動でファイルが作成されますので2回目以降はエラーにならないはずです。
wp-contentの下にファイルが出来ていない場合は、ディレクトリに正しく権限が付与されていない可能性があります。
(プラグインのインストールが失敗するケースなどと同じ理由です)
基本は正しく権限を与えてもらうべきですが、手動でwidget_counter_file.default.datを作成して全権限を与えるというお勧めできない方法もあるにはあります。

また、見られても特に困るファイルではないと思いますが、念のために「wp-content」の直下に.htaccessを置いて以下の記述をしておくとwidget_counter_file.default.datへのアクセスが不可となります。
(アクセスしようとしても403エラーが返るようになります)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<Files widget_counter_file.default.dat>
Deny from all
</Files>
<Files widget_counter_file.default.dat> Deny from all </Files>
<Files widget_counter_file.default.dat>
    Deny from all
</Files>