LoginSignup
25
23

More than 5 years have passed since last update.

URLのクリック数を集計する

Last updated at Posted at 2014-05-11

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 構文が大活躍します。

http
<?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リライタを登録する

以下のような関数を作成しておきます。

PHP5.3の場合はcallableタイプヒンティングを削除してください
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で登録&更新専用スクリプトとして実装した方がいろいろメリットがありそうです。

25
23
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
23