<?php
/* SVN FILE: $Id: rkt_access.php 525 2008-05-30 16:22:40Z bb_yujiro $ */
/**
 * アクセスログ管理
 *
 * PHP versions 5
 *
 *      hitSuji : Social Network Service <http://rakuto.net/rktSNS/>
 *      Copyright (c) 2007 Yujiro Takahashi
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @filesource
 * @package         hitSuji
 * @copyright       Copyright (c) 2007 Yujiro Takahashi
 * @link            http://rakuto.net/
 * @author          Yujiro Takahashi <yujiro@rakuto.net>
 * @version         $Revision: 525 $
 * @modifiedby      $LastChangedBy: bb_yujiro $
 * @lastmodified    $Date: 2008-05-31 01:22:40 +0900 (土, 31 5 2008) $
 * @license         http://opensource.org/licenses/mit-license.php The MIT License
 */

/**
 * アクセスログクラス
 *
 * @category        hitSuji Core
 * @package         hitSuji
 * @copyright       Copyright (c) 2007 Yujiro Takahashi
 * @link            http://rakuto.net/rktSNS/
 * @author          Yujiro Takahashi <yujiro@rakuto.net>
 * @version         $Revision: 525 $
 * @modifiedby      $LastChangedBy: bb_yujiro $
 * @lastmodified    $Date: 2008-05-31 01:22:40 +0900 (土, 31 5 2008) $
 * @license         http://opensource.org/licenses/mit-license.php The MIT License
 */
class RKT_access
{
    /**
     * 一般ページ
     * @const integer
     */
    const TARGET_PUBLIC = 1;

    /**
     * プライベートページ
     * @const integer
     */
    const TARGET_PRIVATE = 2;

    /**
     * メンバーページ
     * @const integer
     */
    const TARGET_MEMBER = 3;

    /**
     * コミュニティページ
     * @const integer
     */
    const TARGET_COMMUNITY = 4;

    /**
     * ユニークフラグ
     * @const integer
     */
    const IS_UNIQUE = 1;

    /**
     * 最終ログイン日時の更新タイミング（秒）
     * @const integer
     */
    const UPDATE_LASTLOGIN = 1800;

    /**
     * 最大足跡数
     * @const integer
     */
    const MAX_FOOTPRINT = 50;

    /**
     * 更新日時
     * @var strint 
     */
    public $modified = null;

    /**
     * ターゲット 
     * @var number 
     */
    public $target = null;

    /**
     * ターゲットID 
     * @var number 
     */
    public $ref_target = null;

    /**
     * 閲覧者のユーザーID 
     * @var number 
     */
    public $ref_audience = null;

    /**
     * Singleton用インスタンス
     * @var object
     */
    private static $_instance;

    /**
     * アクセスログの挿入結果
     * @var integer
     */
    private $_inserted = false;

    /**
     * DB接続オジェクト
     * @var object
     */
    private $_objdb = null;

    /**
     * コンストラクタ
     *
     * @access private
     * @return void
     */
    private function __construct()
    {
        $this->modified = date('Y-m-d H:i:s');
        $this->_objdb   = RKT_DB::getInstance();
    }

    /**
     * 実行処理スタテック版
     *
     * @access public
     * @return object
     *
     * Example:
     * <code>
     * RKT_access::exec()->access($target, $ref_target, $ref_audience);
     * </code>
     *
     */
    public static function exec()
    {
        if (!isset(self::$_instance)) {
            $classname = __CLASS__;
            self::$_instance = new $classname();
        }
        return self::$_instance;
    }

    /**
     * アクセス処理
     *
     * @access public
     * @return void
     **/
    public function access($target, $ref_target, $ref_audience)
    {
        $this->target       = $target;
        $this->ref_target   = $ref_target;
        $this->ref_audience = $ref_audience;

        register_shutdown_function(array(&$this, 'accessLog'));
    }

    /**
     * アクセス処理
     *
     * @access public
     * @return void
     **/
    public function accessLog()
    {
        $pos = strrpos( $_SERVER['REQUEST_URI'], '/json/');
        if ($pos !== false){
            return ;
        }
        if (empty($this->ref_target)){
            return ;
        }

        try {
            if ($this->target == self::TARGET_MEMBER){
                if ($this->ref_target != $this->ref_audience){
                    $this->_insertAccesslog();
                    /* あしあと処理 */
                    $this->_manipFootprint();
                    $this->_deleteFootprint();
                    $this->_sendKiriban();
                }
            } elseif ($this->target == self::TARGET_COMMUNITY){
                $this->_insertAccesslog();
                /* あしあと処理 */
                $this->_manipFootprint();
                $this->_deleteFootprint();
            } else {
                $this->_insertAccesslog();
            } // if ($this->target == self::TARGET_MEMBER)

            $this->_lastLogin();
        } catch (PDOException $exception){
            error_log('-----------  '.date('Y-m-d H:i:s')."  --------------\r\n", 3, ERROR_LOG_FILE);
            error_log($_SERVER['REQUEST_URI']."\r\n", 3, ERROR_LOG_FILE);
            error_log($exception->getMessage()."\r\n", 3, ERROR_LOG_FILE);
            error_log($exception->getTraceAsString()."\r\n", 3, ERROR_LOG_FILE);
        }
    }

    /**
     * アクセスログの挿入
     *
     * @access private
     * @return boolean 正常時：真
     */
    private function _insertAccesslog()
    {
        $hour = date('Y-m-d H:i:s', time()-3600);
        $sql = 'SELECT count(*) as ct FROM '.DB_PREFIX.'log_access '.
               'WHERE modified > :hour AND target = :target AND '.
               'ref_target = :ref_target AND ref_audience = :ref_audience';
        $stmt = $this->_objdb->prepare($sql);

        $stmt->bindParam(':hour',         $hour,                PDO::PARAM_STR);
        $stmt->bindParam(':target',       $this->target,        PDO::PARAM_INT);
        $stmt->bindParam(':ref_target',   $this->ref_target,    PDO::PARAM_INT);
        $stmt->bindParam(':ref_audience', $this->ref_audience,  PDO::PARAM_INT);
        $stmt->execute(); 

        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        $stmt->closeCursor();

        if (empty($result['ct'])){
            $unique_flag = self::IS_UNIQUE;
            $url         = getUrlSelf();
            $referrer    = empty($_SERVER['HTTP_REFERER'])? '':$_SERVER['HTTP_REFERER'];
            $ismobile    = $this->_getMobileAccess();
            $sql = 'INSERT INTO '.DB_PREFIX.'log_access('.
                        ' modified, target, ref_target, ref_audience,'.
                        ' unique_flag, url, referrer, ip_address, agent, ismobile) VALUES ('.
                        ':modified,:target,:ref_target,:ref_audience,'.
                        ':unique_flag,:url,:referrer,:ip_address,:agent,:ismobile)';
            $stmt = $this->_objdb->prepare($sql);
            $stmt->bindParam(':modified',     $this->modified,             PDO::PARAM_STR);
            $stmt->bindParam(':target',       $this->target,               PDO::PARAM_INT);
            $stmt->bindParam(':ref_target',   $this->ref_target,           PDO::PARAM_INT);
            $stmt->bindParam(':ref_audience', $this->ref_audience,         PDO::PARAM_INT);
            $stmt->bindParam(':unique_flag',  $unique_flag,                PDO::PARAM_INT);
            $stmt->bindParam(':url',          $url,                        PDO::PARAM_STR);
            $stmt->bindParam(':referrer',     $referrer,                   PDO::PARAM_STR);
            $stmt->bindParam(':ip_address',   $_SERVER['REMOTE_ADDR'],     PDO::PARAM_STR);
            $stmt->bindParam(':agent',        $_SERVER['HTTP_USER_AGENT'], PDO::PARAM_STR);
            $stmt->bindParam(':agent',        $_SERVER['HTTP_USER_AGENT'], PDO::PARAM_STR);
            $stmt->bindParam(':ismobile',     $ismobile,                   PDO::PARAM_INT);
            
            $this->_inserted = $stmt->execute();
        }
    }

    /**
     * 携帯アクセスかの判定
     *
     * @access private
     * @return integer
     */
    private function _getMobileAccess()
    {
        $agent = Net_UserAgent_Mobile::singleton();
        $name  = $agent->getCarrierLongName();
        
        $name2ismobile = array(
            'NonMobile'=>0,
            'DoCoMo'=>   1,
            'EZweb'=>    1,
            'SoftBank'=> 1,
            'WILLCOM'=>  1,
        );

        return $name2ismobile[$name];
    }

    /**
     * 足跡の処理
     *
     * @access private
     * @return integer  データ操作結果
     */
    private function _manipFootprint()
    {
        $to_date = RKT_DB::toDate('modified');
        $today   = date('Y-m-d');

        $sql = 'SELECT id FROM '.DB_PREFIX.'log_footprint WHERE '.$to_date.
               ' = :today AND target = :target AND ref_target = '.
               ':ref_target AND ref_audience = :ref_audience';
        $stmt = $this->_objdb->prepare($sql);

        $stmt->bindParam(':today',        $today,              PDO::PARAM_STR);
        $stmt->bindParam(':target',       $this->target,       PDO::PARAM_INT);
        $stmt->bindParam(':ref_target',   $this->ref_target,   PDO::PARAM_INT);
        $stmt->bindParam(':ref_audience', $this->ref_audience, PDO::PARAM_INT);
        $stmt->execute();

        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        $stmt->closeCursor();
        $stmt = null;

        if (empty($result['id'])){
            $sql = 'INSERT INTO '.DB_PREFIX.'log_footprint('.
                   ' modified, target, ref_target, ref_audience) VALUES ('.
                   ':modified,:target,:ref_target,:ref_audience)';
            $stmt = $this->_objdb->prepare($sql);

            $stmt->bindParam(':modified',     $this->modified,     PDO::PARAM_STR);
            $stmt->bindParam(':target',       $this->target,       PDO::PARAM_INT);
            $stmt->bindParam(':ref_target',   $this->ref_target,   PDO::PARAM_INT);
            $stmt->bindParam(':ref_audience', $this->ref_audience, PDO::PARAM_INT);
        } else {
            $sql = 'UPDATE '.DB_PREFIX.'log_footprint '.
                   'SET modified = :modified WHERE id = :id';
            $stmt = $this->_objdb->prepare($sql);

            $stmt->bindParam(':modified', $this->modified, PDO::PARAM_STR);
            $stmt->bindParam(':id',       $result['id'],   PDO::PARAM_INT);
        }
        return $stmt->execute(); 
    }

    /**
     * メッセージ送信
     *
     * @access private
     * @return void
     */
    private function _sendMessage($footprint)
    {
        include_once LIB_DIR.'rkt_message.php';
        $objmsg  = new RKT_Message('footprint');
        $subject = $objmsg->getSubject();

        /* プロフィール読み込み */
        $condition = array(
            'ref_account'=> $this->ref_target
        );
        $profile = RKT_DB::getRow('act_profile', $condition);

        $condition = array(
            'ref_account'=> $this->ref_audience
        );
        $friend = RKT_DB::getRow('act_profile', $condition);

        $objmsg->Assign('footprint', $footprint);
        $objmsg->Assign('user_name', $profile['nickname']);
        $objmsg->Assign('friend',    $friend['nickname']);
        $objmsg->Assign('url', HTTP_ACTION.'member/personal/profile/'.$this->ref_audience.'/');
        $body = $objmsg->getBody();
        $body = nl2br($body);

        /* メッセージの挿入 */
        $sql = 'INSERT INTO '.DB_PREFIX.'fri_message('.
                ' modified, regist_date, status, ref_account,'.
                ' ref_to, ref_from, flag, subject, body) VALUES ('.
                ':modified,:regist_date,'.MESSAGE_INBOX.', :ref_account,'.
                ':ref_to,0,'.FLAG_UNREAD.',:subject,:body)';
        $stmt = $this->_objdb->prepare($sql);

        $stmt->bindParam(':modified',    $this->modified,   PDO::PARAM_STR);
        $stmt->bindParam(':regist_date', $this->modified,   PDO::PARAM_STR);
        $stmt->bindParam(':ref_account', $this->ref_target, PDO::PARAM_INT);
        $stmt->bindParam(':ref_to',      $this->ref_target, PDO::PARAM_INT);
        $stmt->bindParam(':subject',     $subject,          PDO::PARAM_STR);
        $stmt->bindParam(':body',        $body,             PDO::PARAM_STR);
        $stmt->execute();
    }

    /**
     * キリ番訪問のお知らせ
     *
     * @access private
     * @return void
     */
    private function _sendKiriban()
    {
        /* アクセスカウンタの変更があるか */
        if (empty($this->_inserted)){
            return ;
        }

        $account = RKT_DB::getRow('act_account', $this->ref_target);
        $condition = array(
            'target'=>      $this->target,
            'ref_target'=>  $this->ref_target,
            'unique_flag'=> self::IS_UNIQUE
        );
        $access = RKT_DB::getRow('log_access', $condition, 'count(*) as ct');
        if (empty($account['footprint'])){
            return ;
        }
        $total = $account['access_number'] + $access['ct'];
        if ($total != $account['footprint']){
            return ;
        }
        $this->_sendMessage($account['footprint']);
    }

    /**
     * 古いあしあとの削除
     *
     * @access private
     * @return void
     */
    private function _deleteFootprint()
    {
        $sql = 'SELECT modified FROM '.DB_PREFIX.'log_footprint '.
               'WHERE target = :target AND ref_target = :ref_target '.
               'ORDER BY modified DESC LIMIT '.self::MAX_FOOTPRINT;
        $stmt = $this->_objdb->prepare($sql);
        $stmt->bindParam(':target',     $this->target,     PDO::PARAM_INT);
        $stmt->bindParam(':ref_target', $this->ref_target, PDO::PARAM_INT);
        $stmt->execute(); 
        $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
        
        if (count($result) < self::MAX_FOOTPRINT){
            return ;
        }
        
        $last = end($result);
        if (empty($last['modified'])){
            return ;
        }

        $sql = 'DELETE FROM '.DB_PREFIX.'log_footprint '.
               'WHERE target = :target AND ref_target = :ref_target '.
               'AND modified < :modified';
        $stmt = $this->_objdb->prepare($sql);
        $stmt->bindParam(':target',     $this->target,     PDO::PARAM_INT);
        $stmt->bindParam(':ref_target', $this->ref_target, PDO::PARAM_INT);
        $stmt->bindParam(':modified',   $last['modified'], PDO::PARAM_STR);
        $stmt->execute(); 
    }

    /**
     * ログイン日時の更新
     *
     * @access private
     * @return boolean 正常時：真
     */
    private function _lastLogin()
    {
        $account = RKT_DB::getRow('act_account', $this->ref_audience, 'last_login');
        if (empty($account['last_login'])){
            return ;
        }
        $elapsed = time() - strtotime($account['last_login']);
        if ($elapsed < self::UPDATE_LASTLOGIN){
            return ;
        }

        $sql = 'UPDATE '.DB_PREFIX.'act_account '.
               'SET last_login = :modified WHERE id = :id';

        $stmt = $this->_objdb->prepare($sql);
        $stmt->bindParam(':modified', $this->modified,     PDO::PARAM_STR);
        $stmt->bindParam(':id',       $this->ref_audience, PDO::PARAM_INT);

        $stmt->execute();
    }
} // class RKT_access
?>
