Google Analytics を利用する
きっとこれが一番オーソドックスな方法でしょう。PHP関係ないですねw
備考: googleアナリティクスで簡単にクリックカウント出来るコード
PHP と MySQL を利用する
「いやいや、俺はPHPコードをゴリゴリ書いてMySQL上で管理したいんだ!」 っていう人はこちらの方法で。今回はIPごとの集計はせず、単純に直近のIPをチェックして連続クリックを防止する仕様にしてみます。また、 http://
または https://
スキームから始まるURLに限定することにします。つまり、相対パスや絶対パスで記述された自サイト内のリンクは対象としないということです。
「相対パスや絶対パスのリンクも取得したい!」 という場合、 こんなふうに 結構面倒なソースを書かなければならなくなるので、今回は割愛します。そのときこそGoogle大先生に頼りましょう。
データベースのスキーマを準備する
analytics
テーブル
名前 | 型 | 属性 |
---|---|---|
url | varchar(2083) | PRIMARY KEY NOT NULL |
count | unsigned int | NOT NULL |
last_ip | varchar(39) | NOT NULL |
last_datetime | datetime | NOT NULL |
URLの最大長は Internet Explorer の実装に合わせて 2083
バイト、IPアドレスの最大長はIPv6の実装に合わせて 39
バイトとします。但し、プライマリキー最大長制限に対応するための 手順 を踏まなければならないようです。
集計用のクッションページを作る
INSERT ... ON DUPLICATE KEY UPDATE 構文が大活躍します。
<?php
function h($str) {
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
try {
switch (true) {
case !isset($_GET['url']):
throw new RuntimeException('Missing url parameter');
case !filter_var($_GET['url'], FILTER_VALIDATE_URL):
throw new RuntimeException('Invalid url parameter');
}
$url = $_GET['url'];
$pdo = new PDO(
'mysql:dbname=analytics;host=localhost;charset=utf8',
'root',
'',
array(
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
)
);
$stmt = $pdo->prepare(implode(' ', array(
'INSERT INTO analytics VALUES (?, 1, ?, NOW()) ON DUPLICATE KEY UPDATE',
implode(', ', array(
'count = IF(last_ip <> VALUES(last_ip), count + 1, count)',
'last_ip = IF(last_ip <> VALUES(last_ip), VALUES(last_ip), last_ip)',
'last_datetime = IF(last_ip <> VALUES(last_ip), VALUES(last_datetime), last_datetime)',
)),
)));
$stmt->execute(array($url, $_SERVER['REMOTE_ADDR']));
} catch (PDOException $e) {
$error = 'Database error';
} catch (RuntimeException $e) {
$error = $e->getMessage();
}
header('Content-Type: text/html; charset=utf-8', true, 400);
if (isset($url)) {
header('Location: ' . $url);
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Redirecting...</title>
</head>
<body>
<?php if (isset($error)): ?>
<p><?=h($error)?></p>
<?php endif; ?>
<?php if (isset($url)): ?>
<p>リダイレクトしない場合は<a href="<?=h($url)?>">ここ</a>をクリック</p>
<?php endif; ?>
</body>
</html>
URLリライタを登録する
以下のような関数を作成しておきます。
function register_href_rewriter(callable $callback) {
$functions[0] = function ($m) use ($callback) {
if (!$m[1]) {
return $m[0];
}
$m[0] = htmlspecialchars_decode($m[0], ENT_QUOTES);
$m[0] = $callback($m[0]);
return htmlspecialchars($m[0], ENT_QUOTES, 'UTF-8');
};
$functions[1] = function ($m) use ($functions) {
return preg_replace_callback(
'/\s++(?:(href)|[^=]++)=(?:"\K[^"]++(?=")|\'\K[^\']++(?=\')|\K[^\s]++)/i',
$functions[0],
$m[0]
);
};
$functions[2] = function ($buffer) use ($functions) {
return preg_replace_callback(
'/<a\K[^>]++(?=>)/',
$functions[1],
$buffer
);
};
return ob_start($functions[2]);
}
利用するときは、HTML出力を開始する前に以下のようにして、集計用のクッションページにジャンプさせるようにします。
register_href_rewriter(function ($url) {
return
filter_var($url, FILTER_VALIDATE_URL) ?
'http://example.com/jump.php?url=' . urlencode($url) :
$url
;
});
(追記)
コメント欄で 提案 があったように、クッションページとしてではなくAjaxで登録&更新専用スクリプトとして実装した方がいろいろメリットがありそうです。