我正在寻找一种方法来扩展我们的默认日志记录类,而无需更改整个应用程序或库。我们在很多地方写日志。 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
,则会覆盖原始写入方法。只是不确定如何修改单例部分
答案 0 :(得分:2)
你别无选择。您有来修改您的App_Log
代码,因为所有内容都会静态调用它。对于最小的更改,您可以extend
App_Log
类,并使App_Log::getInstance
返回该子类的实例;但这很麻烦,因为父母永远不应该知道它的孩子。
也许您可以阻止加载App_Log
的默认实现,并从不同的文件加载它的不同实现。这也很混乱。
这正是(单个)单身和静态调用非常不赞成的原因之一。请参阅How Not To Kill Your Testability Using Statics。