BBClone + PukiWiki 向けのカスタマイズ

SEO対策としても使うことのできるフリー(GPL) の PHP 用のログ解析ソフトである BBClone と PHP ベースでフリーの Wiki クローン PukiWiki を連携動作させたのですが、 日本語の表示対応は一応されているののですが日本語環境でのみ改良ができるので付け焼き刃っぽいですが対応しました。 備忘録として、その作業ログを書いておきます。

環境

  • PHP 7.0
  • BBClone 0.6.4
  • PukiWiki 1.5.1 / UTF-8 版

BBClone 本体は http://bbclone.de/ で入手して解説サイトなどを見ながらインストールしてください。

BBClone (アクセス解析ソフト) 本体の変更

必要なモジュール

PHP に 日本語文字列処理 (mbstring) がインストールされていること

mbstring インストール例

yum install php-mbstring

全角スペースの区切り文字変換

全角スペースを区切り文字として扱わずリファラに設定した場合、全体で 1単語として扱われます。
キャラクタセットを設定した場合 ($BBC_CUSTOM_CHARSET = "UTF-8";など) 、大文字小文字を区別して扱われます。

※ 小文字化や全角スペースを変換せずに放置しておくと検索ワードのパターン数が増殖して access.php が急速に肥大化していきます。

変更内容

  1. 英数字は半角に統一、カタカナ文字は全角に統一する
  2. キャラクタセット(UTF-8含む)の指定によって大文字小文字が区別されたままなので小文字に変換する
  3. 全角スペースを文字列の分割キーワード "+" 文字*1 に変換する
./lib/referer.php bbc_get_keywords 関数 / 200行目付近
 
  // Conversion of keywords, if applicable
  $from = extension_loaded("mbstring") ? bbc_get_encoding($raw_search) : false;
  $char = (!empty($BBC_CUSTOM_CHARSET)) ? $BBC_CUSTOM_CHARSET : false;
  $raw_search = (($from !== false) || extension_loaded("recode")) ?
                bbc_convert_lang($raw_search, $from, $char) : $raw_search;
 
  // ↓↓ 追加始まり 全角スペースを区切り文字(+)へ変換する (※※ 変更点 1 および 2 の処理 ※※)
  $raw_search = mb_convert_kana($raw_search,"aKV");
  $raw_search = preg_replace('/[ ]/u', '+', $raw_search); // /u スイッチは UTF-8 環境でのみ有効
  // ↑↑ 追加おわり
 
  $flt_search = bbc_get_sep($raw_search, $word_sep);
 
  for ($i = 0, $j = count($flt_search); $i < $j; $i++) {
    // Filter search engine cache indicator
    if ((strlen($flt_search[$i]) > 50) || (strlen($flt_search[$i]) < 2) ||
        (preg_match("#^(cache|tbn)\:[a-z0-9_\-]{8,16}\:#", $flt_search[$i]))) {
      unset($flt_search[$i]);
      continue;
    }
 
    // strtolower messes up UTF-8 so we leave things case sensitive if it's
    // requested as charset
    $flt_search[$i] = (!$char || (stristr($char, "UTF") === false)) ?
                      strtolower(bbc_clean($flt_search[$i])) : bbc_clean($flt_search[$i]);
 
  // ↓↓ 追加始まり 全角スペースを区切り文字(+)へ変換する (※※ 変更点 3 の処理 ※※)
    $flt_search[$i] = mb_strtolower($flt_search[$i]);
  // ↑↑ 追加おわり
 
    $access['key'][($flt_search[$i])] = !isset($access['key'][($flt_search[$i])]) ? 1 :
                                        ++$access['key'][($flt_search[$i])];
  }
  return (!empty($flt_search) ? $flt_search : false);
}

8 以上の数字を含む 0 はじまりの数字文字列が検索ワードにあったときのクラッシュ対応

検索で "0180-***-***" のようなワードで単語分割後に 0 はじまりの数字文字列になってしまう場合
連想配列のキーが "0180" => 1 でなくクオートなしの 0180 => 1 のような形式で access.log に保存される。
その次の読み出し時 PHP5 以前では 8進数で解析し "01" + "8" の 8 以降を無視する。つまり 10進表記で 数値の 1 にエラーなしで変換する。(※ 0 始まりの数値文字は 8進数 という仕様)

だが PHP7 ではこの文字列変換を実行すると PHP Parse error: Invalid numeric literal in line nnn のエラーとなる。

エラー発生検証用コード ( PHP5だと 0 が表示される )

# php
<?php
    $a = 08;
    print_r($a);
?>
Ctrl+D
PHP Parse error:  Invalid numeric literal in - on line 2

The key treatment 部分

./lib/io.php  bbc_array_to_str 関数 / 150 行目付近
 
  while (list($key, $val) = each($tab)) {
    // The separator treatment
    if (($last_is_array) || (is_array($val) && ($k !== 0))) {
      $str .= $sep."$nl";
    }
    else $str .= $sep;
 
    // The key treatment
 
    // ↓↓ 置換 始まり ※※※ 0 で始まる文字列は数値として扱わないようにする ※※※
    // if (preg_match(":^[0-9]+$:", $key)) { 
    if (preg_match(":^[1-9][0-9]*$|^0$:", $key)) {
    // ↑↑ 置換 おわり ※※※ ":^[0-9]+$:" を ":^[1-9][0-9]*$|^0$:" に変更 ※※※
 
      if ($key !== $k) {
        $str .= (((is_array($val)) || ($k === 0) || ($last_is_array)) ? "$indent" : "")
               ."$key =>".((is_array($val)) ? "$nl" : "");
      }
      else $str .= ($k === 0) ? (is_array($val) ? "" : "$indent") : "";
    }

すぐその下の The value treatment も同様に

    // The value treatment
    $last_is_array = false;
    if (is_array($val)) {
      $str .= bbc_array_to_str($val);
      $last_is_array = true;
      $sep = ",";
    }
    else {
      // ↓↓ 置換 始まり ※※※ 0 で始まる文字列は数値として扱わないようにする ※※※
      // $str .= (preg_match(":^[0-9]+$|^0$:", $val) ? $val : "\"$val\"");
      $str .= (preg_match(":^[1-9][0-9]+$|^0$:", $val) ? $val : "\"$val\"");
      // ↑↑ 置換 おわり ※※※ ":^[0-9]+$:" を ":^[1-9][0-9]*$|^0$:" に変更 ※※※
      $sep = ",";
    }

PukiWiki 連携に関する変更

PukiWiki と BBClone の両方に手を加える。

PukiWiki 側の変更

PukiWiki 使用時の USER_AGENT 情報の損失への対応

PukiWiki は 初期化時に USER_AGENT 情報を取り出したのち 削除している。
このため、BBClone に ログ解析させるタイミングには、この情報を保持していないため記録できない。
といって、最初に BBClone にログ解析をさせると、PukiWiki名 を取り出すのがやっかいでなので簡単な方法で妥協しています。

ただデフォルトで削除しているということは何かしら経由での問題がある可能性を想定していると思われます。

BBClone を使っているサイトは HTML 生成エンジンとして PukiWikiを運用しているため問題点はなさそうなのですが 一般利用者に書き換えを許可している PukiWikiサイトを運用している方は慎重に検討した上で対応してください。
この変更による具体的なリスクについては、詳しくないので残念ながらわかりません。つまり自己責任ということでお願いします。

PukiWiki バージョン 1.5.1
./lib/init.php
140 行目付近
/////////////////////////////////////////////////
// INI_FILE: $agents:  UserAgentの識別
 
$ua = 'HTTP_USER_AGENT';
$user_agent = $matches = array();
 
$user_agent['agent'] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
// unset(${$ua}, $_SERVER[$ua], $HTTP_SERVER_VARS[$ua], $ua);	// safety
↑↑↑  この行をコメントアウト

PukiWiki exit 処理削除

Pukwiki 1.4.6 以降から 終了処理 (exit) が組み込まれているため PHP での処理が終了してしまい BBClone に処理を継続させることができない。  require(LIB_DIR . 'pukiwiki.php'); 以降の処理が実行されなくなっている。その処理を削除する。

./lib/pukiwiki.php
最終行
// Output
catbody($title, $page, $body);
// exit;   ← ここをコメントアウト
?>

BBClone 側の変更

セパレーターを chr(0) に変更

後述の 「PukiWiki の設置例」 書いてある

define("_BBC_PAGE_NAME", $cook_page); 

BBClone に引き渡すページ名 ( $cook_page 変数の値 ) 、つまり PukiWiki ページ名が特定の日本語コードで始まる場合 BBClone 側の 訪問済ページ がブランク("") に置き換わってしまう文字がある。
例) UTF-8 の場合、カタカナの "ス" で始まるページは "" というページに集約して集計される。

./config.php / 50 行目付近
 
// $BBC_SEP = chr(173);
$BBC_SEP = chr(0);

\0 文字で問題がある場合にはタブコード "chr(9)" に変更。

PukiWiki 使用時のグローバル統計の訪問ページの URL の変更

強引な変更 かつ PukiWiki専用変更になります。(confファイルを読み込んでいないため、直接埋め込みました)
さらに ?plugin=*** 経由等の訪問履歴に対応してませんのでクリックしないでください。

./show_global.php bbc_show_top_pages 関数 / 230 行目付近
function bbc_show_top_pages() {
  global $_, $access, $BBC_MAXPAGE;
 
  $page_tab = isset($access['page']) ? $access['page'] : array();
 
  for ($page_total = 0;
       list(, $page_elem) = each($page_tab); $page_total += $page_elem['count']);
 
  uasort($page_tab, "bbc_sort_page_count");
  reset($page_tab);
 
  $str = bbc_rank_head($BBC_MAXPAGE, "gstat_pages", 1);
 // ↓↓ 追加 始まり ※※※
  $BBC_PUKIWIKI_SITE = "http://サイト名/index.php?";
 // ↑↑ 追加 おわり ※※※
 
  for ($k = 0; (list($page_name, $page_elem) = each($page_tab))
                 && ($k < $BBC_MAXPAGE); $k++) {
 // ↓↓変更 はじまり ※※※
 //    $page_name = ($page_name == "index") ? $_['navbar_Main_Site'] : $page_name;
 //    $str .= bbc_list_item("", "<a href=\"".$page_elem['uri']."\">$page_name</a>",
 //              $page_elem['count'], $page_total);
    $str .= bbc_list_item("", "<a href=\"". $BBC_PUKIWIKI_SITE . rawurlencode($page_name) .
            "\">$page_name</a>", $page_elem['count'], $page_total);
 // ↑↑変更 おわり
  }

PukiWiki の設置例

まだいろいろなパターンをハンドリングできていないと思います。$non_script なども適当に編集してください。

./index.php
12 行目あたり
// Special
//define('PKWK_READONLY',  1);
//define('PKWK_SAFE_MODE', 1);
//define('PKWK_OPTIMISE',  1);
//define('TDIARY_THEME',   'digital_gadgets');
 
// Directory definition
// (Ended with a slash like '../path/to/pkwk/', or '')
define('DATA_HOME',	'');
define('LIB_DIR',	'lib/');
 
require(LIB_DIR . 'pukiwiki.php');
 
// ↓↓ 追加 始まり ※※※ BBClone 用にページ名を加工して提供 ※※※
// BBClone begin ログ解析開始
$script = $vars['cmd'] . $vars['plugin'];
$script = ($script == "") ? "read?" : $script;
$raw_page = isset($vars['page']) ? $vars['page'] : "no page";
$cook_page = isset($page) ? $page : "no title";
 
//$non_script ="^$";
//$non_raw_page   = "^$";
//$non_cook_page   = "^$";
 
$non_script  ="^attach|^backup|^diff|^edit|^freeze|";
$non_script .="^list|^new|^ref($|erer)|^rename|^rss|^template";
$non_raw_page   = $non_list;
$non_cook_page = "^単語検索";
 
if (! mb_ereg_match($non_script, $script) and ! mb_ereg_match($non_raw_page, $raw_page)
 and ! mb_ereg_match($non_cook_page, $cook_page)) {
	$cook_page = ($script == "read") ? $cook_page : "$script($cook_page)";
	define("_BBC_PAGE_NAME", $cook_page);
	define("_BBCLONE_DIR", "〜/bbclone/");  // ← ここは自分の環境用にあわせて
	define("COUNTER", _BBCLONE_DIR."mark_page.php");
	if (is_readable(COUNTER)) include_once(COUNTER);
}
// BBClone end ログ解析終了
exit;
// ↑↑ 追加 おわり ※※※
?>
 


日本オラクル
■ 日本オラクル 株式会社
■ オラクルマスター資格 (オラクルマスターとは
■ 会員制(無料)の公式技術サイト

*1 サーチエンジンにより"+"が区切り文字でない場合には誤解釈されます。