mirror of
https://e.coding.net/circlecloud/MinecraftAccount.git
synced 2024-11-17 00:58:55 +00:00
611 lines
17 KiB
PHP
611 lines
17 KiB
PHP
|
<?php
|
|||
|
// +----------------------------------------------------------------------
|
|||
|
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
|||
|
// +----------------------------------------------------------------------
|
|||
|
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
|
|||
|
// +----------------------------------------------------------------------
|
|||
|
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
|||
|
// +----------------------------------------------------------------------
|
|||
|
// | Author: luofei614 <weibo.com/luofei614>
|
|||
|
// +----------------------------------------------------------------------
|
|||
|
// $Id$
|
|||
|
|
|||
|
/**
|
|||
|
* 将Trace信息输出到chrome浏览器的控制器,从而不影响ajax效果和页面的布局。
|
|||
|
* 使用前,你需要先安装 chrome log 这个插件: http://craig.is/writing/chrome-logger。
|
|||
|
* 定义应用的tags.php文件 Application/Common/Conf/tags.php,
|
|||
|
* <code>
|
|||
|
* <?php return array(
|
|||
|
* 'app_end'=>array(
|
|||
|
* 'Behavior\ChromeShowPageTrace'
|
|||
|
* )
|
|||
|
* );
|
|||
|
* </code>
|
|||
|
* 如果trace信息没有正常输出,请查看您的日志。
|
|||
|
* 这是通过http headers和chrome通信,所以要保证在输出trace信息之前不能有
|
|||
|
* headers输出,你可以在入口文件第一行加入代码 ob_start(); 或者配置output_buffering
|
|||
|
*
|
|||
|
*/
|
|||
|
namespace Behavior;
|
|||
|
use Think\Log;
|
|||
|
|
|||
|
/**
|
|||
|
* 系统行为扩展 页面Trace显示输出
|
|||
|
*/
|
|||
|
class ChromeShowPageTraceBehavior {
|
|||
|
|
|||
|
protected $tracePageTabs = array('BASE'=>'基本','FILE'=>'文件','INFO'=>'流程','ERR|NOTIC'=>'错误','SQL'=>'SQL','DEBUG'=>'调试');
|
|||
|
|
|||
|
// 行为扩展的执行入口必须是run
|
|||
|
public function run(&$params){
|
|||
|
if(C('SHOW_PAGE_TRACE')) $this->showTrace();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* 显示页面Trace信息
|
|||
|
* @access private
|
|||
|
*/
|
|||
|
private function showTrace() {
|
|||
|
// 系统默认显示信息
|
|||
|
$files = get_included_files();
|
|||
|
$info = array();
|
|||
|
foreach ($files as $key=>$file){
|
|||
|
$info[] = $file.' ( '.number_format(filesize($file)/1024,2).' KB )';
|
|||
|
}
|
|||
|
$trace = array();
|
|||
|
$base = array(
|
|||
|
'请求信息' => date('Y-m-d H:i:s',$_SERVER['REQUEST_TIME']).' '.$_SERVER['SERVER_PROTOCOL'].' '.$_SERVER['REQUEST_METHOD'].' : '.__SELF__,
|
|||
|
'运行时间' => $this->showTime(),
|
|||
|
'吞吐率' => number_format(1/G('beginTime','viewEndTime'),2).'req/s',
|
|||
|
'内存开销' => MEMORY_LIMIT_ON?number_format((memory_get_usage() - $GLOBALS['_startUseMems'])/1024,2).' kb':'不支持',
|
|||
|
'查询信息' => N('db_query').' queries '.N('db_write').' writes ',
|
|||
|
'文件加载' => count(get_included_files()),
|
|||
|
'缓存信息' => N('cache_read').' gets '.N('cache_write').' writes ',
|
|||
|
'配置加载' => count(c()),
|
|||
|
'会话信息' => 'SESSION_ID='.session_id(),
|
|||
|
);
|
|||
|
// 读取应用定义的Trace文件
|
|||
|
$traceFile = COMMON_PATH.'Conf/trace.php';
|
|||
|
if(is_file($traceFile)) {
|
|||
|
$base = array_merge($base,include $traceFile);
|
|||
|
}
|
|||
|
|
|||
|
$debug = trace();
|
|||
|
$tabs = C('TRACE_PAGE_TABS',null,$this->tracePageTabs);
|
|||
|
foreach ($tabs as $name=>$title){
|
|||
|
switch(strtoupper($name)) {
|
|||
|
case 'BASE':// 基本信息
|
|||
|
$trace[$title] = $base;
|
|||
|
break;
|
|||
|
case 'FILE': // 文件信息
|
|||
|
$trace[$title] = $info;
|
|||
|
break;
|
|||
|
default:// 调试信息
|
|||
|
$name = strtoupper($name);
|
|||
|
if(strpos($name,'|')) {// 多组信息
|
|||
|
$array = explode('|',$name);
|
|||
|
$result = array();
|
|||
|
foreach($array as $name){
|
|||
|
$result += isset($debug[$name])?$debug[$name]:array();
|
|||
|
}
|
|||
|
$trace[$title] = $result;
|
|||
|
}else{
|
|||
|
$trace[$title] = isset($debug[$name])?$debug[$name]:'';
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
chrome_debug('TRACE信息:'.__SELF__,'group');
|
|||
|
//输出日志
|
|||
|
foreach($trace as $title=>$log){
|
|||
|
'错误'==$title?chrome_debug($title,'group'):chrome_debug($title,'groupCollapsed');
|
|||
|
foreach($log as $i=>$logstr){
|
|||
|
chrome_debug($i.'.'.$logstr,'log');
|
|||
|
}
|
|||
|
chrome_debug('','groupEnd');
|
|||
|
}
|
|||
|
chrome_debug('','groupEnd');
|
|||
|
if($save = C('PAGE_TRACE_SAVE')) { // 保存页面Trace日志
|
|||
|
if(is_array($save)) {// 选择选项卡保存
|
|||
|
$tabs = C('TRACE_PAGE_TABS',null,$this->tracePageTabs);
|
|||
|
$array = array();
|
|||
|
foreach ($save as $tab){
|
|||
|
$array[] = $tabs[$tab];
|
|||
|
}
|
|||
|
}
|
|||
|
$content = date('[ c ]').' '.get_client_ip().' '.$_SERVER['REQUEST_URI']."\r\n";
|
|||
|
foreach ($trace as $key=>$val){
|
|||
|
if(!isset($array) || in_array($key,$array)) {
|
|||
|
$content .= '[ '.$key." ]\r\n";
|
|||
|
if(is_array($val)) {
|
|||
|
foreach ($val as $k=>$v){
|
|||
|
$content .= (!is_numeric($k)?$k.':':'').print_r($v,true)."\r\n";
|
|||
|
}
|
|||
|
}else{
|
|||
|
$content .= print_r($val,true)."\r\n";
|
|||
|
}
|
|||
|
$content .= "\r\n";
|
|||
|
}
|
|||
|
}
|
|||
|
error_log(str_replace('<br/>',"\r\n",$content), 3,LOG_PATH.date('y_m_d').'_trace.log');
|
|||
|
}
|
|||
|
unset($files,$info,$base);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取运行时间
|
|||
|
*/
|
|||
|
private function showTime() {
|
|||
|
// 显示运行时间
|
|||
|
G('beginTime',$GLOBALS['_beginTime']);
|
|||
|
G('viewEndTime');
|
|||
|
// 显示详细运行时间
|
|||
|
return G('beginTime','viewEndTime').'s ( Load:'.G('beginTime','loadTime').'s Init:'.G('loadTime','initTime').'s Exec:'.G('initTime','viewStartTime').'s Template:'.G('viewStartTime','viewEndTime').'s )';
|
|||
|
}
|
|||
|
}
|
|||
|
if(!function_exists('chrome_debug')){
|
|||
|
//ChromePhp 输出trace的函数
|
|||
|
function chrome_debug($msg,$type='trace',$trace_level=1){
|
|||
|
if('trace'==$type){
|
|||
|
ChromePhp::groupCollapsed($msg);
|
|||
|
$traces=debug_backtrace(false);
|
|||
|
$traces=array_reverse($traces);
|
|||
|
$max=count($traces)-$trace_level;
|
|||
|
for($i=0;$i<$max;$i++){
|
|||
|
$trace=$traces[$i];
|
|||
|
$fun=isset($trace['class'])?$trace['class'].'::'.$trace['function']:$trace['function'];
|
|||
|
$file=isset($trace['file'])?$trace['file']:'unknown file';
|
|||
|
$line=isset($trace['line'])?$trace['line']:'unknown line';
|
|||
|
$trace_msg='#'.$i.' '.$fun.' called at ['.$file.':'.$line.']';
|
|||
|
if(!empty($trace['args'])){
|
|||
|
ChromePhp::groupCollapsed($trace_msg);
|
|||
|
ChromePhp::log($trace['args']);
|
|||
|
ChromePhp::groupEnd();
|
|||
|
}else{
|
|||
|
ChromePhp::log($trace_msg);
|
|||
|
}
|
|||
|
}
|
|||
|
ChromePhp::groupEnd();
|
|||
|
}else{
|
|||
|
if(method_exists('Behavior\ChromePhp',$type)){
|
|||
|
//支持type trace,warn,log,error,group, groupCollapsed, groupEnd等
|
|||
|
call_user_func(array('Behavior\ChromePhp',$type),$msg);
|
|||
|
}else{
|
|||
|
//如果type不为trace,warn,log等,则为log的标签
|
|||
|
call_user_func_array(array('Behavior\ChromePhp','log'),func_get_args());
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/**
|
|||
|
* Server Side Chrome PHP debugger class
|
|||
|
*
|
|||
|
* @package ChromePhp
|
|||
|
* @author Craig Campbell <iamcraigcampbell@gmail.com>
|
|||
|
*/
|
|||
|
class ChromePhp{
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const VERSION = '4.1.0';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const HEADER_NAME = 'X-ChromeLogger-Data';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const BACKTRACE_LEVEL = 'backtrace_level';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const LOG = 'log';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const WARN = 'warn';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const ERROR = 'error';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const GROUP = 'group';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const INFO = 'info';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const GROUP_END = 'groupEnd';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const GROUP_COLLAPSED = 'groupCollapsed';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
const TABLE = 'table';
|
|||
|
|
|||
|
/**
|
|||
|
* @var string
|
|||
|
*/
|
|||
|
protected $_php_version;
|
|||
|
|
|||
|
/**
|
|||
|
* @var int
|
|||
|
*/
|
|||
|
protected $_timestamp;
|
|||
|
|
|||
|
/**
|
|||
|
* @var array
|
|||
|
*/
|
|||
|
protected $_json = array(
|
|||
|
'version' => self::VERSION,
|
|||
|
'columns' => array('log', 'backtrace', 'type'),
|
|||
|
'rows' => array()
|
|||
|
);
|
|||
|
|
|||
|
/**
|
|||
|
* @var array
|
|||
|
*/
|
|||
|
protected $_backtraces = array();
|
|||
|
|
|||
|
/**
|
|||
|
* @var bool
|
|||
|
*/
|
|||
|
protected $_error_triggered = false;
|
|||
|
|
|||
|
/**
|
|||
|
* @var array
|
|||
|
*/
|
|||
|
protected $_settings = array(
|
|||
|
self::BACKTRACE_LEVEL => 1
|
|||
|
);
|
|||
|
|
|||
|
/**
|
|||
|
* @var ChromePhp
|
|||
|
*/
|
|||
|
protected static $_instance;
|
|||
|
|
|||
|
/**
|
|||
|
* Prevent recursion when working with objects referring to each other
|
|||
|
*
|
|||
|
* @var array
|
|||
|
*/
|
|||
|
protected $_processed = array();
|
|||
|
|
|||
|
/**
|
|||
|
* constructor
|
|||
|
*/
|
|||
|
private function __construct()
|
|||
|
{
|
|||
|
$this->_php_version = phpversion();
|
|||
|
$this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time();
|
|||
|
$this->_json['request_uri'] = $_SERVER['REQUEST_URI'];
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* gets instance of this class
|
|||
|
*
|
|||
|
* @return ChromePhp
|
|||
|
*/
|
|||
|
public static function getInstance()
|
|||
|
{
|
|||
|
if (self::$_instance === null) {
|
|||
|
self::$_instance = new self();
|
|||
|
}
|
|||
|
return self::$_instance;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* logs a variable to the console
|
|||
|
*
|
|||
|
* @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
public static function log()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log('', $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* logs a warning to the console
|
|||
|
*
|
|||
|
* @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
public static function warn()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::WARN, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* logs an error to the console
|
|||
|
*
|
|||
|
* @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
public static function error()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::ERROR, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* sends a group log
|
|||
|
*
|
|||
|
* @param string value
|
|||
|
*/
|
|||
|
public static function group()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::GROUP, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* sends an info log
|
|||
|
*
|
|||
|
* @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
public static function info()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::INFO, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* sends a collapsed group log
|
|||
|
*
|
|||
|
* @param string value
|
|||
|
*/
|
|||
|
public static function groupCollapsed()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::GROUP_COLLAPSED, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* ends a group log
|
|||
|
*
|
|||
|
* @param string value
|
|||
|
*/
|
|||
|
public static function groupEnd()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::GROUP_END, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* sends a table log
|
|||
|
*
|
|||
|
* @param string value
|
|||
|
*/
|
|||
|
public static function table()
|
|||
|
{
|
|||
|
$args = func_get_args();
|
|||
|
return self::_log(self::TABLE, $args);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* internal logging call
|
|||
|
*
|
|||
|
* @param string $type
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
protected static function _log($type, array $args)
|
|||
|
{
|
|||
|
// nothing passed in, don't do anything
|
|||
|
if (count($args) == 0 && $type != self::GROUP_END) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
$logger = self::getInstance();
|
|||
|
|
|||
|
$logger->_processed = array();
|
|||
|
|
|||
|
$logs = array();
|
|||
|
foreach ($args as $arg) {
|
|||
|
$logs[] = $logger->_convert($arg);
|
|||
|
}
|
|||
|
|
|||
|
$backtrace = debug_backtrace(false);
|
|||
|
$level = $logger->getSetting(self::BACKTRACE_LEVEL);
|
|||
|
|
|||
|
$backtrace_message = 'unknown';
|
|||
|
if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) {
|
|||
|
$backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line'];
|
|||
|
}
|
|||
|
|
|||
|
$logger->_addRow($logs, $backtrace_message, $type);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* converts an object to a better format for logging
|
|||
|
*
|
|||
|
* @param Object
|
|||
|
* @return array
|
|||
|
*/
|
|||
|
protected function _convert($object)
|
|||
|
{
|
|||
|
// if this isn't an object then just return it
|
|||
|
if (!is_object($object)) {
|
|||
|
return $object;
|
|||
|
}
|
|||
|
|
|||
|
//Mark this object as processed so we don't convert it twice and it
|
|||
|
//Also avoid recursion when objects refer to each other
|
|||
|
$this->_processed[] = $object;
|
|||
|
|
|||
|
$object_as_array = array();
|
|||
|
|
|||
|
// first add the class name
|
|||
|
$object_as_array['___class_name'] = get_class($object);
|
|||
|
|
|||
|
// loop through object vars
|
|||
|
$object_vars = get_object_vars($object);
|
|||
|
foreach ($object_vars as $key => $value) {
|
|||
|
|
|||
|
// same instance as parent object
|
|||
|
if ($value === $object || in_array($value, $this->_processed, true)) {
|
|||
|
$value = 'recursion - parent object [' . get_class($value) . ']';
|
|||
|
}
|
|||
|
$object_as_array[$key] = $this->_convert($value);
|
|||
|
}
|
|||
|
|
|||
|
$reflection = new ReflectionClass($object);
|
|||
|
|
|||
|
// loop through the properties and add those
|
|||
|
foreach ($reflection->getProperties() as $property) {
|
|||
|
|
|||
|
// if one of these properties was already added above then ignore it
|
|||
|
if (array_key_exists($property->getName(), $object_vars)) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
$type = $this->_getPropertyKey($property);
|
|||
|
|
|||
|
if ($this->_php_version >= 5.3) {
|
|||
|
$property->setAccessible(true);
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
$value = $property->getValue($object);
|
|||
|
} catch (ReflectionException $e) {
|
|||
|
$value = 'only PHP 5.3 can access private/protected properties';
|
|||
|
}
|
|||
|
|
|||
|
// same instance as parent object
|
|||
|
if ($value === $object || in_array($value, $this->_processed, true)) {
|
|||
|
$value = 'recursion - parent object [' . get_class($value) . ']';
|
|||
|
}
|
|||
|
|
|||
|
$object_as_array[$type] = $this->_convert($value);
|
|||
|
}
|
|||
|
return $object_as_array;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* takes a reflection property and returns a nicely formatted key of the property name
|
|||
|
*
|
|||
|
* @param ReflectionProperty
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
protected function _getPropertyKey(ReflectionProperty $property)
|
|||
|
{
|
|||
|
$static = $property->isStatic() ? ' static' : '';
|
|||
|
if ($property->isPublic()) {
|
|||
|
return 'public' . $static . ' ' . $property->getName();
|
|||
|
}
|
|||
|
|
|||
|
if ($property->isProtected()) {
|
|||
|
return 'protected' . $static . ' ' . $property->getName();
|
|||
|
}
|
|||
|
|
|||
|
if ($property->isPrivate()) {
|
|||
|
return 'private' . $static . ' ' . $property->getName();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* adds a value to the data array
|
|||
|
*
|
|||
|
* @var mixed
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
protected function _addRow(array $logs, $backtrace, $type)
|
|||
|
{
|
|||
|
// if this is logged on the same line for example in a loop, set it to null to save space
|
|||
|
if (in_array($backtrace, $this->_backtraces)) {
|
|||
|
$backtrace = null;
|
|||
|
}
|
|||
|
|
|||
|
// for group, groupEnd, and groupCollapsed
|
|||
|
// take out the backtrace since it is not useful
|
|||
|
if ($type == self::GROUP || $type == self::GROUP_END || $type == self::GROUP_COLLAPSED) {
|
|||
|
$backtrace = null;
|
|||
|
}
|
|||
|
|
|||
|
if ($backtrace !== null) {
|
|||
|
$this->_backtraces[] = $backtrace;
|
|||
|
}
|
|||
|
|
|||
|
$row = array($logs, $backtrace, $type);
|
|||
|
|
|||
|
$this->_json['rows'][] = $row;
|
|||
|
$this->_writeHeader($this->_json);
|
|||
|
}
|
|||
|
|
|||
|
protected function _writeHeader($data)
|
|||
|
{
|
|||
|
header(self::HEADER_NAME . ': ' . $this->_encode($data));
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* encodes the data to be sent along with the request
|
|||
|
*
|
|||
|
* @param array $data
|
|||
|
* @return string
|
|||
|
*/
|
|||
|
protected function _encode($data)
|
|||
|
{
|
|||
|
return base64_encode(utf8_encode(json_encode($data)));
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* adds a setting
|
|||
|
*
|
|||
|
* @param string key
|
|||
|
* @param mixed value
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
public function addSetting($key, $value)
|
|||
|
{
|
|||
|
$this->_settings[$key] = $value;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* add ability to set multiple settings in one call
|
|||
|
*
|
|||
|
* @param array $settings
|
|||
|
* @return void
|
|||
|
*/
|
|||
|
public function addSettings(array $settings)
|
|||
|
{
|
|||
|
foreach ($settings as $key => $value) {
|
|||
|
$this->addSetting($key, $value);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* gets a setting
|
|||
|
*
|
|||
|
* @param string key
|
|||
|
* @return mixed
|
|||
|
*/
|
|||
|
public function getSetting($key)
|
|||
|
{
|
|||
|
if (!isset($this->_settings[$key])) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
return $this->_settings[$key];
|
|||
|
}
|
|||
|
}
|
|||
|
}
|