单例实例,如何写自写

时间:2012-11-21 10:20:39

标签: php oop singleton

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

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,则会覆盖原始写入方法。只是不确定如何修改单例部分

1 个答案:

答案 0 :(得分:2)

你别无选择。您来修改您的App_Log代码,因为所有内容都会静态调用它。对于最小的更改,您可以extend App_Log类,并使App_Log::getInstance返回该子类的实例;但这很麻烦,因为父母永远不应该知道它的孩子。

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

这正是(单个)单身和静态调用非常不赞成的原因之一。请参阅How Not To Kill Your Testability Using Statics