1

我正在寻找一种方法来扩展我们的默认日志记录类,而无需更改整个应用程序或库。我们有很多地方可以写日志。例如:

App_Log::getInstance()->write(
    $name,
    $type,
    "LOGOUT",
    $url
);

Auth_Log

<?php
class App_Auth_Log {

    /**
     * Singleton instance
     *
     * Marked only as protected to allow extension of the class. To extend,
     * simply override {@link getInstance()}.
     *
     * @var App_Auth_Log
     */
    protected static $_instance = null;


    /**
     * Auth logging enabled flag.
     *
     * @var boolean
     */
    protected $_enabled = false;


    /**
     * If flag is true then cleanup will not remove login records.
     *
     * @var boolean
     */
    protected $_keepLoginRecords = false;


    /**
     * Class constructor.
     */
    public function __construct() {
        if(App_Front::getInstance()->hasParam("withAuthLog"))
            $this->_enabled = true;

        if(App_Front::getInstance()->hasParam("withKeepLoginRecords"))
            $this->_keepLoginRecords = true;

        $this->cleanup();
    }


    /**
     * Singleton instance
     *
     * @return App_Auth_Log
     */
    public static function getInstance() {
        if (is_null(self::$_instance))
            self::$_instance = new self();
        return self::$_instance;
    }


    /**
     * Write new auth log record with given details. if succesful then method
     * returns true otherwise returns false.
     *
     * @param string $class
     * @param string $ident
     * @param string $action
     * @param string $url
     * @param string $ipaddr
     * @return boolean
     * @throws Exception
     */
    public function write($class,$ident,$action,$url,$ipaddr=null) {

        if($this->isEnabled())  {
            $db = App_Db_Connections::getInstance()->getConnection();
            try {

                // if address not specificed get remote addr
                $ipaddr = ($ipaddr == null) ? $_SERVER['REMOTE_ADDR'] : $ipaddr;

                // manual insert so we can take advantage of insert delayed
                $stmnt = "INSERT INTO accesslogs
                    VALUES('',NOW(),'$class','$ident','$action','$url','$ipaddr')";

                // execute insert
                $db->query($stmnt);

            } catch (Exception $e) {
                throw $e;
            }
            return true;
        }
        return false;
    }

    /**
     * Cleanup old accesslog records. Cached to run once a day.
     *
     * @return boolean - returns true if run false if not.
     */
    public function cleanup() {

        $cache  = App_Cache::getInstance()->newObject(86400);

        if($this->isEnabled()) {

            if (!$res = $cache->load(App_Cache::getCacheName(__CLASS__. "cleanup"))) {
                // add cache
                $db = App_Db_Connections::getInstance()->getConnection();
                try {
                    $where = $db->quoteInto("DATEDIFF(NOW(),accesslog_datetime) > ?", 6);
                    $and = ($this->_keepLoginRecords) ? " AND accesslog_action != 'LOGIN'" : "";
                    $db->query("DELETE LOW_PRIORITY FROM accesslogs WHERE $where $and");
                } catch (Exception $e) {
                    throw $e;
                }


                $cache->save($res,App_Cache::getCacheName(__CLASS__. "cleanup"));
            } // end cache
        }
        return;
    }

    /**
     * Returns boolean check if auth log enabled.
     *
     * @return boolean
     */
    public function isEnabled() {
        return ($this->_enabled) ? true : false;
    }

    /**
     * Enabled disable the auth log process.
     *
     * @param boolean $boolean
     * @return App_Auth_Log
     */
    public function setEnabled($boolean) {
        $this->_enabled = ($boolean) ? true : false;
        return $this;
    }
}
?>

这是核心代码的默认行为。但是对于这个特定的项目,我需要能够扩展/覆盖该write方法,例如使用额外的参数。

问:如何更改此 App_Auth_Log 类,使其向后兼容以前调用的项目App_Log::getInstance()->write

我认为它应该如何工作(但不知道该怎么做)。如果App_Front::getInstance()->hasParam("withAuthLog")传递一个自定义类名,例如:My_Custom_Auth_Log它会覆盖原始的 write 方法。只是不确定如何修改单例部分

4

1 回答 1

2

你没有选择。你必须修改你的App_Log代码,因为一切都是静态地调用它。对于最小的更改,您可以类并返回extend该子类的实例;但这很混乱,因为父母永远不应该知道自己的孩子。App_LogApp_Log::getInstance

也许您可以阻止App_Log加载的默认实现并从不同的文件加载它的不同实现。不过这也很乱。

这正是单例和静态调用非常不受欢迎的原因之一。请参阅如何不使用静力学破坏您的可测试性

于 2012-11-21T10:24:57.020 回答