仙台の山奥で自転車に乗ったり転んだり

愛車の GIOS でサイクリングしたりポタリングしたり、それをブログに記録してみたり。ロードバイクや自転車や坂のことを書いてみたり。ときたまプログラムのことを忘れないようにメモってみたり。

いまさら1000speakers@仙台のネタ

1000speakers@仙台#1の感想とか書こうと思いつつ、いつのまにか時が流れ……。
相当いまさら感はあるものの、id:sato165さんが発表していたLPOのネタを何となく。

<?php
/**
 * 検索キーワードをハイライト
 */
class HighlightKeywords
{
    /**
     * 終了タグ
     */
    const PREFIX_TAG = '<span class="keyword">';

    /**
     * 終了タグ
     */
    const SUFFIX_TAG = '</span>';

    /**
     * HTTP_REFERERのQUERY_STRINGをマッチする正規表現
     */
    const REGEXP_HTTP_REFERER = '^http(?:s)?:\/\/(?:.+?)(?:\?)(.+)$';

    /**
     * 検索エンジンのクエリ情報
     * 
     * @var array
     */
    static private $searchEngines = array(
            'google_cojp' => array(
                    'pre_url' => 'http://www.google.co.jp/', 
                    'query'   => 'q', 
                    'encode'  => 'UTF-8', 
                ), 
            'google_com' => array(
                    'pre_url' => 'http://www.google.com/', 
                    'query'   => 'q', 
                    'encode'  => 'UTF-8', 
                ), 
            'yahoo_cojp' => array(
                    'pre_url' => 'http://search.yahoo.co.jp/', 
                    'query'   => 'p', 
                    'encode'  => 'UTF-8', 
                ), 
            'msn_cojp' => array(
                    'pre_url' => 'http://search.msn.co.jp/', 
                    'query'   => 'q', 
                    'encode'  => 'UTF-8', 
                ), 
            'goo' => array(
                    'pre_url' => 'http://search.goo.ne.jp/', 
                    'query'   => 'MT', 
                    'encode'  => 'EUC-JP', 
                ), 
            'infoseek' => array(
                    'pre_url' => 'http://search.www.infoseek.co.jp/', 
                    'query'   => 'qt', 
                    'encode'  => 'UTF-8', 
                ), 
            'livedoor' => array(
                    'pre_url' => 'http://search.livedoor.com/', 
                    'query'   => 'q', 
                    'encode'  => 'UTF-8', 
                ), 
        );

    /**
     * 検索キーワード
     * 
     * @var array
     */
    private $keywords = array();

    /**
     * コンストラクタ
     */
    public function __construct()
    {
        $encode         = mb_internal_encoding();
        $regex_encoding = mb_regex_encoding();
        mb_regex_encoding($encode);

        if (($referer = self::getEnv('HTTP_REFERER')) !== null) {
            foreach (self::$searchEngines as $s) {
                if (strpos($referer, $s['pre_url']) === 0) {
                    if (($query = self::getRefererQuery($s['query'])) !== null) {
                        $query = rawurldecode($query);
                        $query = mb_convert_encoding($query, $encode, $s['encode']);
                        $query = mb_convert_kana($query, 's', $encode);
                        foreach (mb_split('\s', $query) as $k) {
                            $this->keywords[] = htmlspecialchars($k, ENT_QUOTES);
                        }
                    }
                }
            }
        }

        mb_regex_encoding($regex_encoding);
    }

    /**
     * 検索キーワードをハイライト表示
     * 
     * @param  string $buffer
     * @return string
     */
    public function highlight($buffer)
    {
        $replace = array();
        foreach ($this->keywords as $k) {
            $replace[] = self::PREFIX_TAG.$k.self::SUFFIX_TAG;
        }

        return 
            str_replace($this->keywords, $replace, $buffer);
    }

    /**
     * このキーワードで検索されているかをチェック
     * 
     * @param  string $keyword
     * @return boolean
     */
    public function isRequested($keyword)
    {
        return 
            in_array($keyword, $this->keywords, true);
    }

    /**
     * HTTP_REFERERのQUERY_STRINGを返却
     * 
     * @param  string $varkey
     * @return string
     */
    private static function getRefererQuery($varkey)
    {
        $varvalue = null;
        if (($referer = self::getEnv('HTTP_REFERER')) !== null) {
            $raw = '';
            if (preg_match('/'.self::REGEXP_HTTP_REFERER.'/', $referer, $matches)) {
                $raw = $matches[1];
            }
            foreach (explode('&', $raw) as $row) {
                $query = split('=', $row);
                if (isset($query[0], $query[1]) && ($query[0] === $varkey)) {
                    $varvalue = $query[1];
                }
            }
        }

        return $varvalue;
    }

    /**
     * 環境変数の値を返却
     * 
     * @param  string $varkey
     * @return string
     */
    private static function getEnv($varkey)
    {
        $varvalue = null;
        if (isset($_SERVER[$varkey])) {
            $varvalue = 
                str_replace(
                    array("\x00","\x01","\x02","\x03","\x04","\x05",
                          "\x06","\x07","\x08","\x0B","\x0C","\x0E",
                          "\x0F","\x10","\x11","\x12","\x13","\x14",
                          "\x15","\x16","\x17","\x18","\x19","\x1A",
                          "\x1B","\x1C","\x1D","\x1E","\x1F","\x7F"), 
                    '', 
                    $_SERVER[$varkey]);
        }

        return $varvalue;
    }
}

といいつつも検索キーワードをハイライトするだけだから、ちょっと趣旨が違ってる。
↓みたいに使ってHTML出力のキーワードをハイライト用のタグで囲むような。

<?php
.....

$highlight = new HighlightKeywords;
ob_start(array($highlight, 'highlight'));
?>
<html>
.....
</html>

↓みたいなことをすれば、一応は本来の目的としても使えるかも。

<?php
.....

$highlight = new HighlightKeywords;
ob_start(array($highlight, 'highlight'));
?>
<html>
.....
<?php if ($highlight->isRequested('ぐらころ')) : ?>
ぐらころなら!
<?php endif ?>
.....
</html>