N°6436 - Integrate Performance Audit pre requisite in iTop Pro 2.7.9

This commit is contained in:
Stephen Abello
2023-07-07 09:29:08 +02:00
committed by Eric Espie
parent 9afc22bd8f
commit 2b5973ec67
27 changed files with 663 additions and 191 deletions

View File

@@ -1857,4 +1857,28 @@ class RestUtils
interface iModuleExtension interface iModuleExtension
{ {
public function __construct(); public function __construct();
}
/**
* KPI logging extensibility point
*
* KPI Logger extension
*/
interface iKPILoggerExtension
{
/**
* Init the statistics collected
*
* @return void
*/
public function InitStats();
/**
* Add a new KPI to the stats
*
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKPILogData
*
* @return mixed
*/
public function LogOperation($oKPILogData);
} }

View File

@@ -4003,7 +4003,9 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange()); $oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
} }
return $res; return $res;
@@ -4020,13 +4022,16 @@ EOF
protected function DBCloneTracked_Internal($newKey = null) protected function DBCloneTracked_Internal($newKey = null)
{ {
$oNewObj = parent::DBCloneTracked_Internal($newKey); /** @var cmdbAbstractObject $oNewObj */
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
// Invoke extensions after insertion (the object must exist, have an id, etc.) // Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange()); $oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
} }
return $oNewObj; return $oNewObj;
@@ -4054,7 +4059,9 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange()); $oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
} }
} }
catch (Exception $e) catch (Exception $e)
@@ -4100,7 +4107,9 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange()); $oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
} }
return parent::DBDeleteTracked_Internal($oDeletionPlan); return parent::DBDeleteTracked_Internal($oDeletionPlan);
@@ -4118,7 +4127,10 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
if ($oExtensionInstance->OnIsModified($this)) $oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
if ($bIsModified)
{ {
return true; return true;
} }
@@ -4162,7 +4174,9 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this); $aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
{ {
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues); $this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
@@ -4210,7 +4224,9 @@ EOF
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this); $aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
if (is_array($aNewIssues) && count($aNewIssues) > 0) if (is_array($aNewIssues) && count($aNewIssues) > 0)
{ {
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues); $this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
@@ -4722,7 +4738,7 @@ EOF
$bResult = (count($aErrors) == 0); $bResult = (count($aErrors) == 0);
if ($bResult) if ($bResult)
{ {
list($bResult, $aErrors) = $oObj->CheckToWrite(); [$bResult, $aErrors] = $oObj->CheckToWrite();
} }
if ($bPreview) if ($bPreview)
{ {

View File

@@ -17,6 +17,7 @@
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
*/ */
use Combodo\iTop\Service\Module\ModuleService;
use ScssPhp\ScssPhp\Compiler; use ScssPhp\ScssPhp\Compiler;
@@ -1946,24 +1947,7 @@ class utils
*/ */
public static function GetCurrentModuleName($iCallDepth = 0) public static function GetCurrentModuleName($iCallDepth = 0)
{ {
$sCurrentModuleName = ''; return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth = 0);
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleName = $sModuleName;
break;
}
}
}
return $sCurrentModuleName;
} }
/** /**
@@ -1979,24 +1963,7 @@ class utils
*/ */
public static function GetCurrentModuleDir($iCallDepth) public static function GetCurrentModuleDir($iCallDepth)
{ {
$sCurrentModuleDir = ''; return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleDir = basename($sRootDir);
break;
}
}
}
return $sCurrentModuleDir;
} }
/** /**
@@ -2011,12 +1978,7 @@ class utils
*/ */
public static function GetCurrentModuleUrl() public static function GetCurrentModuleUrl()
{ {
$sDir = static::GetCurrentModuleDir(1); return ModuleService::GetInstance()->GetCurrentModuleUrl();
if ( $sDir !== '')
{
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
}
return '';
} }
/** /**
@@ -2026,8 +1988,7 @@ class utils
*/ */
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null) public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{ {
$sModuleName = static::GetCurrentModuleName(1); return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
} }
/** /**
@@ -2036,12 +1997,7 @@ class utils
*/ */
public static function GetCompiledModuleVersion($sModuleName) public static function GetCompiledModuleVersion($sModuleName)
{ {
$aModulesInfo = GetModulesInfo(); return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
} }
/** /**
@@ -2505,4 +2461,5 @@ class utils
return (substr(PHP_OS,0,3) === 'WIN'); return (substr(PHP_OS,0,3) === 'WIN');
} }
} }

View File

@@ -22,14 +22,8 @@ define('ITOP_DEFAULT_ENV', 'production');
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance'); define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly'); define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
if (function_exists('microtime')) $fItopStarted = microtime(true);
{ $iItopInitialMemory = memory_get_usage(true);
$fItopStarted = microtime(true);
}
else
{
$fItopStarted = 1000 * time();
}
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
{ {

View File

@@ -62,6 +62,7 @@
"sources/application", "sources/application",
"sources/Composer", "sources/Composer",
"sources/Controller", "sources/Controller",
"sources/Service",
"sources/Core" "sources/Core"
], ],
"exclude-from-classmap": [ "exclude-from-classmap": [

View File

@@ -67,8 +67,7 @@ class MyHelpers
// format sss.mmmuuupppnnn // format sss.mmmuuupppnnn
public static function getmicrotime() public static function getmicrotime()
{ {
list($usec, $sec) = explode(" ",microtime()); return microtime(true);
return ((float)$usec + (float)$sec);
} }
/* /*
@@ -420,6 +419,7 @@ class MyHelpers
//} //}
return $sOutput; return $sOutput;
} }
} }
/** /**
@@ -524,5 +524,3 @@ class Str
return (strtolower($sString) == $sString); return (strtolower($sString) == $sString);
} }
} }
?>

View File

@@ -731,8 +731,9 @@ class CMDBSource
{ {
self::LogDeadLock($e); self::LogDeadLock($e);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e)); throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
} } finally {
$oKPI->ComputeStats('Query exec (mySQL)', $sSql); $oKPI->ComputeStats('Query exec (mySQL)', $sSql);
}
if ($oResult === false) if ($oResult === false)
{ {
$aContext = array('query' => $sSql); $aContext = array('query' => $sSql);

View File

@@ -963,6 +963,14 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, 'show_in_conf_sample' => false,
), ),
'log_kpi_report_to_extensions_only' => array(
'type' => 'bool',
'description' => 'Report only the KPI logging extensions',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'max_linkset_output' => array( 'max_linkset_output' => array(
'type' => 'integer', 'type' => 'integer',
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.', 'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',

View File

@@ -2225,7 +2225,7 @@ abstract class DBObject implements iDisplay
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
$this->DoCheckToWrite(); $this->DoCheckToWrite();
$oKPI->ComputeStats('CheckToWrite', get_class($this)); $oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite');
if (count($this->m_aCheckIssues) == 0) if (count($this->m_aCheckIssues) == 0)
{ {
$this->m_bCheckStatus = true; $this->m_bCheckStatus = true;
@@ -2693,8 +2693,12 @@ abstract class DBObject implements iDisplay
$sRootClass = MetaModel::GetRootClass($sClass); $sRootClass = MetaModel::GetRootClass($sClass);
// Ensure the update of the values (we are accessing the data directly) // Ensure the update of the values (we are accessing the data directly)
$oKPI = new ExecutionKPI();
$this->DoComputeValues(); $this->DoComputeValues();
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
$oKPI = new ExecutionKPI();
$this->OnInsert(); $this->OnInsert();
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
if ($this->m_iKey < 0) if ($this->m_iKey < 0)
{ {
@@ -2712,7 +2716,7 @@ abstract class DBObject implements iDisplay
} }
// Ultimate check - ensure DB integrity // Ultimate check - ensure DB integrity
list($bRes, $aIssues) = $this->CheckToWrite(); [$bRes, $aIssues] = $this->CheckToWrite();
if (!$bRes) if (!$bRes)
{ {
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey())); throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
@@ -2818,7 +2822,9 @@ abstract class DBObject implements iDisplay
$this->m_aOrigValues[$sAttCode] = $value; $this->m_aOrigValues[$sAttCode] = $value;
} }
$oKPI = new ExecutionKPI();
$this->AfterInsert(); $this->AfterInsert();
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
// Activate any existing trigger // Activate any existing trigger
$sClass = get_class($this); $sClass = get_class($this);
@@ -3094,8 +3100,11 @@ abstract class DBObject implements iDisplay
try try
{ {
$oKPI = new ExecutionKPI();
$this->DoComputeValues(); $this->DoComputeValues();
// Stop watches $oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
// Stop watches
$sState = $this->GetState(); $sState = $this->GetState();
if ($sState != '') if ($sState != '')
{ {
@@ -3114,7 +3123,9 @@ abstract class DBObject implements iDisplay
} }
} }
} }
$this->OnUpdate(); $oKPI = new ExecutionKPI();
$this->OnUpdate();
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
$aChanges = $this->ListChanges(); $aChanges = $this->ListChanges();
if (count($aChanges) == 0) if (count($aChanges) == 0)
@@ -3126,7 +3137,7 @@ abstract class DBObject implements iDisplay
} }
// Ultimate check - ensure DB integrity // Ultimate check - ensure DB integrity
list($bRes, $aIssues) = $this->CheckToWrite(); [$bRes, $aIssues] = $this->CheckToWrite();
if (!$bRes) if (!$bRes)
{ {
throw new CoreCannotSaveObjectException(array( throw new CoreCannotSaveObjectException(array(
@@ -3326,7 +3337,9 @@ abstract class DBObject implements iDisplay
try try
{ {
$this->AfterUpdate(); $oKPI = new ExecutionKPI();
$this->AfterUpdate();
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
// Reload to get the external attributes // Reload to get the external attributes
if ($bHasANewExternalKeyValue) if ($bHasANewExternalKeyValue)

View File

@@ -763,7 +763,10 @@ class DBObjectSet implements iDBObjectSetIterator
try try
{ {
$oKPI = new ExecutionKPI();
$this->m_oSQLResult = CMDBSource::Query($sSQL); $this->m_oSQLResult = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
} catch (MySQLException $e) } catch (MySQLException $e)
{ {
// 1116 = ER_TOO_MANY_TABLES // 1116 = ER_TOO_MANY_TABLES
@@ -843,8 +846,11 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
if (is_null($this->m_iNumTotalDBRows)) if (is_null($this->m_iNumTotalDBRows))
{ {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$resQuery = CMDBSource::Query($sSQL); $resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if (!$resQuery) return 0; if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery); $aRow = CMDBSource::FetchArray($resQuery);
@@ -855,6 +861,42 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ?? return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
} }
/**
* @param \DBSearch $oFilter
* @param array $aOrder
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bCount
*
* @return string
*/
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
{
$sOQL = '';
if ($bCount) {
$sOQL .= 'COUNT ';
}
$sOQL .= $oFilter->ToOQL();
if ($iLimitCount > 0) {
$sOQL .= ' LIMIT ';
if ($iLimitStart > 0) {
$sOQL .= "$iLimitStart, ";
}
$sOQL .= "$iLimitCount";
}
if (count($aOrder) > 0) {
$sOQL .= ' ORDER BY ';
$aOrderBy = [];
foreach ($aOrder as $sAttCode => $bAsc) {
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
}
$sOQL .= implode(', ', $aOrderBy);
}
return $sOQL;
}
/** /**
* Check if the count exceeds a given limit * Check if the count exceeds a given limit
* *
@@ -871,8 +913,11 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
if (is_null($this->m_iNumTotalDBRows)) if (is_null($this->m_iNumTotalDBRows))
{ {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL); $resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery) if ($resQuery)
{ {
$aRow = CMDBSource::FetchArray($resQuery); $aRow = CMDBSource::FetchArray($resQuery);
@@ -883,7 +928,7 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
$iCount = 0; $iCount = 0;
} }
} }
else else
{ {
$iCount = $this->m_iNumTotalDBRows; $iCount = $this->m_iNumTotalDBRows;
@@ -908,8 +953,11 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
if (is_null($this->m_iNumTotalDBRows)) if (is_null($this->m_iNumTotalDBRows))
{ {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL); $resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery) if ($resQuery)
{ {
$aRow = CMDBSource::FetchArray($resQuery); $aRow = CMDBSource::FetchArray($resQuery);
@@ -920,7 +968,7 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
$iCount = 0; $iCount = 0;
} }
} }
else else
{ {
$iCount = $this->m_iNumTotalDBRows; $iCount = $this->m_iNumTotalDBRows;

View File

@@ -15,6 +15,8 @@
// //
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/> // along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Core\Kpi\KpiLogData;
use Combodo\iTop\Service\Module\ModuleService;
/** /**
@@ -30,6 +32,8 @@ class ExecutionKPI
static protected $m_bEnabled_Memory = false; static protected $m_bEnabled_Memory = false;
static protected $m_bBlameCaller = false; static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*'; static protected $m_sAllowedUser = '*';
static protected $m_bReportExtensionsOnly = false;
static protected $m_fSlowQueries = 0;
static protected $m_aStats = array(); // Recurrent operations static protected $m_aStats = array(); // Recurrent operations
static protected $m_aExecData = array(); // One shot operations static protected $m_aExecData = array(); // One shot operations
@@ -77,14 +81,39 @@ class ExecutionKPI
return false; return false;
} }
static public function SetReportExtensionsOnly($bReportExtensionsOnly)
{
self::$m_bReportExtensionsOnly = $bReportExtensionsOnly;
}
static public function SetSlowQueries($fSlowQueries)
{
self::$m_fSlowQueries = $fSlowQueries;
}
static public function GetDescription() static public function GetDescription()
{ {
$aFeatures = array(); $aFeatures = array();
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration'; if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage'; if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
$sFeatures = implode(', ', $aFeatures); $sFeatures = 'Measures: '.implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'"; $sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
return "KPI logging is active for $sFor. Measures: $sFeatures"; $sSlowQueries = '';
if (self::$m_fSlowQueries > 0) {
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
}
$aExtensions = [];
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
}
$sExtensions = '';
if (count($aExtensions) > 0) {
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
}
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
} }
static public function ReportStats() static public function ReportStats()
@@ -92,7 +121,28 @@ class ExecutionKPI
if (!self::IsEnabled()) return; if (!self::IsEnabled()) return;
global $fItopStarted; global $fItopStarted;
global $iItopInitialMemory;
$sExecId = microtime(); // id to differentiate the hrefs! $sExecId = microtime(); // id to differentiate the hrefs!
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
if (isset($_POST['operation'])) {
$sRequest .= ' operation: '.$_POST['operation'];
}
$fStop = MyHelpers::getmicrotime();
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (self::$m_bReportExtensionsOnly) {
return;
}
$aBeginTimes = array(); $aBeginTimes = array();
foreach (self::$m_aExecData as $aOpStats) foreach (self::$m_aExecData as $aOpStats)
@@ -105,7 +155,7 @@ class ExecutionKPI
self::Report("<hr/>"); self::Report("<hr/>");
self::Report("<div style=\"background-color: grey; padding: 10px;\">"); self::Report("<div style=\"background-color: grey; padding: 10px;\">");
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>"); self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>");
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>"); self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
self::Report("<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>"); self::Report("<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>");
self::Report("<div>"); self::Report("<div>");
@@ -200,8 +250,6 @@ class ExecutionKPI
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>"); self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
$fSlowQueries = MetaModel::GetConfig()->Get('log_kpi_slow_queries');
// Report operation details // Report operation details
foreach (self::$m_aStats as $sOperation => $aOpStats) foreach (self::$m_aStats as $sOperation => $aOpStats)
{ {
@@ -245,7 +293,7 @@ class ExecutionKPI
$sTotalInter = round($fTotalInter, 3); $sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3); $sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3); $sMaxInter = round($fMaxInter, 3);
if (($fTotalInter >= $fSlowQueries)) if (($fTotalInter >= self::$m_fSlowQueries))
{ {
if ($bDisplayHeader) if ($bDisplayHeader)
{ {
@@ -271,11 +319,19 @@ class ExecutionKPI
self::Report('<a name="end-'.md5($sExecId).'">&nbsp;</a>'); self::Report('<a name="end-'.md5($sExecId).'">&nbsp;</a>');
} }
public static function InitStats()
{
// Invoke extensions to initialize the KPI statistics
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oExtensionInstance->InitStats();
}
}
public function __construct() public function __construct()
{ {
$this->ResetCounters(); $this->ResetCounters();
} }
// Get the duration since startup, and reset the counter for the next measure // Get the duration since startup, and reset the counter for the next measure
// //
@@ -285,6 +341,8 @@ class ExecutionKPI
$aNewEntry = null; $aNewEntry = null;
$fStarted = $this->m_fStarted;
$fStopped = $this->m_fStarted;
if (self::$m_bEnabled_Duration) if (self::$m_bEnabled_Duration)
{ {
$fStopped = MyHelpers::getmicrotime(); $fStopped = MyHelpers::getmicrotime();
@@ -297,6 +355,9 @@ class ExecutionKPI
$this->m_fStarted = $fStopped; $this->m_fStarted = $fStopped;
} }
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory) if (self::$m_bEnabled_Memory)
{ {
$iCurrentMemory = self::memory_get_usage(); $iCurrentMemory = self::memory_get_usage();
@@ -306,41 +367,95 @@ class ExecutionKPI
} }
$aNewEntry['mem_begin'] = $this->m_iInitialMemory; $aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory; $aNewEntry['mem_end'] = $iCurrentMemory;
if (function_exists('memory_get_peak_usage')) $iPeakMemory = self::memory_get_peak_usage();
{ $aNewEntry['mem_peak'] = $iPeakMemory;
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
// Reset for the next operation (if the object is recycled) // Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory; $this->m_iInitialMemory = $iCurrentMemory;
} }
if (!is_null($aNewEntry)) if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
{
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_REPORT,
'Step',
$sOperationDesc,
$fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!is_null($aNewEntry) && !self::$m_bReportExtensionsOnly)
{ {
self::$m_aExecData[] = $aNewEntry; self::$m_aExecData[] = $aNewEntry;
} }
$this->ResetCounters(); $this->ResetCounters();
} }
public function ComputeStatsForExtension($object, $sMethod)
{
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', $sSignature);
}
}
public function ComputeStats($sOperation, $sArguments) public function ComputeStats($sOperation, $sArguments)
{ {
if (self::$m_bEnabled_Duration) if (self::$m_bEnabled_Duration)
{ {
$fStopped = MyHelpers::getmicrotime(); $fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted; $fDuration = $fStopped - $this->m_fStarted;
if (self::$m_bBlameCaller) $aCallstack = [];
{ if (!self::$m_bReportExtensionsOnly) {
self::$m_aStats[$sOperation][$sArguments][] = array( if (self::$m_bBlameCaller) {
'time' => $fDuration, $aCallstack = MyHelpers::get_callstack(1);
'callers' => MyHelpers::get_callstack(1), self::$m_aStats[$sOperation][$sArguments][] = [
); 'time' => $fDuration,
} 'callers' => $aCallstack,
else ];
{ } else {
self::$m_aStats[$sOperation][$sArguments][] = array( self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration 'time' => $fDuration
); ];
} }
} }
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
}
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,
$sArguments,
$this->m_fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory,
$aCallstack);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
} }
protected function ResetCounters() protected function ResetCounters()
@@ -370,35 +485,7 @@ class ExecutionKPI
static protected function memory_get_usage() static protected function memory_get_usage()
{ {
if (function_exists('memory_get_usage')) return memory_get_usage(true);
{
return memory_get_usage(true);
}
// Copied from the PHP manual
//
//If its Windows
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
//Doesn't work for 2000
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
if (substr(PHP_OS,0,3) == 'WIN')
{
$output = array();
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
}
else
{
//We now assume the OS is UNIX
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
//This should work on most UNIX systems
$pid = getmypid();
exec("ps -eo%mem,rss,pid | grep $pid", $output);
$output = explode(" ", $output[0]);
//rss is given in 1024 byte units
return $output[1] * 1024;
}
} }
static public function memory_get_peak_usage($bRealUsage = false) static public function memory_get_peak_usage($bRealUsage = false)

View File

@@ -2778,7 +2778,7 @@ abstract class MetaModel
// Build the list of available extensions // Build the list of available extensions
// //
$aInterfaces = array('iApplicationUIExtension', 'iPreferencesExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginUIExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider', 'iModuleExtension'); $aInterfaces = array('iApplicationUIExtension', 'iPreferencesExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginUIExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider', 'iModuleExtension', 'iKPILoggerExtension');
foreach($aInterfaces as $sInterface) foreach($aInterfaces as $sInterface)
{ {
self::$m_aExtensionClasses[$sInterface] = array(); self::$m_aExtensionClasses[$sInterface] = array();
@@ -6348,7 +6348,9 @@ abstract class MetaModel
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration')); ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory')); ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id')); ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
ExecutionKPI::SetReportExtensionsOnly(self::$m_oConfig->Get('log_kpi_report_to_extensions_only'));
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write'); self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys'); self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
@@ -6485,6 +6487,7 @@ abstract class MetaModel
CMDBSource::InitFromConfig(self::$m_oConfig); CMDBSource::InitFromConfig(self::$m_oConfig);
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone); // Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
ExecutionKPI::InitStats();
} }
/** /**

View File

@@ -126,7 +126,9 @@ abstract class Trigger extends cmdbAbstractObject
$oAction = MetaModel::GetObject('Action', $iActionId); $oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive()) if ($oAction->IsActive())
{ {
$oKPI = new ExecutionKPI();
$oAction->DoExecute($this, $aContextArgs); $oAction->DoExecute($this, $aContextArgs);
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
} }
} }
} }

View File

@@ -2,11 +2,6 @@
// autoload.php @generated by Composer // autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php'; require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b::getLoader(); return ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b::getLoader();

View File

@@ -149,7 +149,7 @@ class ClassLoader
/** /**
* @return string[] Array of classname => path * @return string[] Array of classname => path
* @psalm-return array<string, string> * @psalm-var array<string, string>
*/ */
public function getClassMap() public function getClassMap()
{ {

View File

@@ -2,7 +2,7 @@
// autoload_classmap.php @generated by Composer // autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__); $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
@@ -158,8 +158,10 @@ return array(
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php', 'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => $baseDir . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php',
'Combodo\\iTop\\Core\\Email\\EmailFactory' => $baseDir . '/sources/Core/Email/EmailFactory.php', 'Combodo\\iTop\\Core\\Email\\EmailFactory' => $baseDir . '/sources/Core/Email/EmailFactory.php',
'Combodo\\iTop\\Core\\Email\\iEMail' => $baseDir . '/sources/Core/Email/iEMail.php', 'Combodo\\iTop\\Core\\Email\\iEMail' => $baseDir . '/sources/Core/Email/iEMail.php',
'Combodo\\iTop\\Core\\Kpi\\KpiLogData' => $baseDir . '/sources/Core/Kpi/KpiLogData.php',
'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\DesignDocument' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\DesignElement' => $baseDir . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\Service\\Module\\ModuleService' => $baseDir . '/sources/Service/Module/ModuleService.php',
'Combodo\\iTop\\TwigExtension' => $baseDir . '/application/twigextension.class.inc.php', 'Combodo\\iTop\\TwigExtension' => $baseDir . '/application/twigextension.class.inc.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'Config' => $baseDir . '/core/config.class.inc.php', 'Config' => $baseDir . '/core/config.class.inc.php',
@@ -2712,6 +2714,7 @@ return array(
'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php', 'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php',
'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php', 'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php',
'iDisplay' => $baseDir . '/core/dbobject.class.php', 'iDisplay' => $baseDir . '/core/dbobject.class.php',
'iKPILoggerExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php',
'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php', 'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php',
'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php', 'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php',

View File

@@ -2,25 +2,25 @@
// autoload_files.php @generated by Composer // autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__); $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.php',
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php', '023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
'7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php', '7e9bd612cc444b3eed788ebbe46263a0' => $vendorDir . '/laminas/laminas-zendframework-bridge/src/autoload.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
'32dcc8afd4335739640db7d200c1971d' => $vendorDir . '/symfony/polyfill-apcu/bootstrap.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php', 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => $vendorDir . '/symfony/polyfill-iconv/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
); );

View File

@@ -2,7 +2,7 @@
// autoload_namespaces.php @generated by Composer // autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__); $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(

View File

@@ -2,7 +2,7 @@
// autoload_psr4.php @generated by Composer // autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__); $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(

View File

@@ -25,20 +25,33 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b
require __DIR__ . '/platform_check.php'; require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b', 'loadClassLoader'), true, true); spl_autoload_register(array('ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b', 'loadClassLoader')); spl_autoload_unregister(array('ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b', 'loadClassLoader'));
$includePaths = require __DIR__ . '/include_paths.php'; $includePaths = require __DIR__ . '/include_paths.php';
$includePaths[] = get_include_path(); $includePaths[] = get_include_path();
set_include_path(implode(PATH_SEPARATOR, $includePaths)); set_include_path(implode(PATH_SEPARATOR, $includePaths));
require __DIR__ . '/autoload_static.php'; $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
call_user_func(\Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::getInitializer($loader)); if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::getInitializer($loader));
} else {
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->setClassMapAuthoritative(true); $loader->setClassMapAuthoritative(true);
$loader->register(true); $loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$files; if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) { foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file); composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file);
} }
@@ -47,16 +60,11 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b
} }
} }
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file) function composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file)
{ {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file; require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
} }
} }

View File

@@ -8,21 +8,21 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
{ {
public static $files = array ( public static $files = array (
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php', '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php', '023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
'7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php', '7e9bd612cc444b3eed788ebbe46263a0' => __DIR__ . '/..' . '/laminas/laminas-zendframework-bridge/src/autoload.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
'32dcc8afd4335739640db7d200c1971d' => __DIR__ . '/..' . '/symfony/polyfill-apcu/bootstrap.php',
'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php', 'def43f6c87e4f8dfd0c9e1b1bab14fe8' => __DIR__ . '/..' . '/symfony/polyfill-iconv/bootstrap.php',
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
'2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php', '2c102faa651ef8ea5874edb585946bce' => __DIR__ . '/..' . '/swiftmailer/swiftmailer/lib/swift_required.php',
); );
@@ -526,8 +526,10 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php', 'Combodo\\iTop\\Core\\Authentication\\Client\\OAuth\\OAuthClientProviderGoogle' => __DIR__ . '/../..' . '/sources/Core/Authentication/Client/OAuth/OAuthClientProviderGoogle.php',
'Combodo\\iTop\\Core\\Email\\EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.php', 'Combodo\\iTop\\Core\\Email\\EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.php',
'Combodo\\iTop\\Core\\Email\\iEMail' => __DIR__ . '/../..' . '/sources/Core/Email/iEMail.php', 'Combodo\\iTop\\Core\\Email\\iEMail' => __DIR__ . '/../..' . '/sources/Core/Email/iEMail.php',
'Combodo\\iTop\\Core\\Kpi\\KpiLogData' => __DIR__ . '/../..' . '/sources/Core/Kpi/KpiLogData.php',
'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\DesignDocument' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php', 'Combodo\\iTop\\DesignElement' => __DIR__ . '/../..' . '/core/designdocument.class.inc.php',
'Combodo\\iTop\\Service\\Module\\ModuleService' => __DIR__ . '/../..' . '/sources/Service/Module/ModuleService.php',
'Combodo\\iTop\\TwigExtension' => __DIR__ . '/../..' . '/application/twigextension.class.inc.php', 'Combodo\\iTop\\TwigExtension' => __DIR__ . '/../..' . '/application/twigextension.class.inc.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php', 'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php',
@@ -3080,6 +3082,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b
'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php', 'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php',
'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', 'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php',
'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php', 'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php',
'iKPILoggerExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php',
'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', 'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',
'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', 'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php',

View File

@@ -2,7 +2,7 @@
// include_paths.php @generated by Composer // include_paths.php @generated by Composer
$vendorDir = dirname(__DIR__); $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir); $baseDir = dirname($vendorDir);
return array( return array(

View File

@@ -1,13 +0,0 @@
# Apache 2.4
<ifModule mod_authz_core.c>
Require all denied
</ifModule>
# Apache 2.2
<ifModule !mod_authz_core.c>
deny from all
Satisfy All
</ifModule>
# Apache 2.2 and 2.4
IndexIgnore *

View File

@@ -1,2 +0,0 @@
<?php
echo 'Access denied';

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering>
<fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions>
</requestFiltering>
<authorization>
<deny users="*" /> <!-- Denies all users -->
</authorization>
</security>
</system.webServer>
</configuration>

View File

@@ -0,0 +1,125 @@
<?php
/**
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Core\Kpi;
class KpiLogData
{
const TYPE_REPORT = 'report';
const TYPE_STATS = 'stats';
const TYPE_REQUEST = 'request';
/** @var string */
public $sType;
/** @var string */
public $sOperation;
/** @var string */
public $sArguments;
/** @var float */
public $fStartTime;
/** @var float */
public $fStopTime;
/** @var string */
public $sExtension;
/** @var int */
public $iInitialMemory;
/** @var int */
public $iCurrentMemory;
/** @var int */
public $iPeakMemory;
/** @var array */
public $aData;
/**
* @param string $sType
* @param string $sOperation
* @param string $sArguments
* @param float $fStartTime
* @param float $fStopTime
* @param string $sExtension
* @param int $iInitialMemory
* @param int $iCurrentMemory
* @param array $aData
*/
public function __construct($sType, $sOperation, $sArguments, $fStartTime, $fStopTime, $sExtension, $iInitialMemory = 0, $iCurrentMemory = 0, $iPeakMemory = 0, $aData = [])
{
$this->sType = $sType;
$this->sOperation = $sOperation;
$this->sArguments = @iconv(mb_detect_encoding($sArguments, mb_detect_order(), true), 'UTF-8', $sArguments);
$this->fStartTime = $fStartTime;
$this->fStopTime = $fStopTime;
$this->sExtension = $sExtension;
$this->iInitialMemory = $iInitialMemory;
$this->iCurrentMemory = $iCurrentMemory;
$this->iPeakMemory = $iPeakMemory;
$this->aData = $aData;
}
/**
* Return the CSV Header
*
* @return string
*/
public static function GetCSVHeader()
{
return "Type,Operation,Arguments,StartTime,StopTime,Duration,Extension,InitialMemory,CurrentMemory,PeakMemory";
}
/**
* Return the CSV line for the values
* @return string
*/
public function GetCSV()
{
$fDuration = sprintf('%01.4f', $this->fStopTime - $this->fStartTime);
$sType = $this->RemoveQuotes($this->sType);
$sOperation = $this->RemoveQuotes($this->sOperation);
$sArguments = $this->RemoveQuotes($this->sArguments);
$sExtension = $this->RemoveQuotes($this->sExtension);
return "\"$sType\",\"$sOperation\",\"$sArguments\",$this->fStartTime,$this->fStopTime,$fDuration,\"$sExtension\",$this->iInitialMemory,$this->iCurrentMemory,$this->iPeakMemory";
}
private function RemoveQuotes(string $sEntry): string
{
return str_replace('"', "'", $sEntry);
}
/**
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oOther
*
* @return float
*/
public function Compare(KpiLogData $oOther): float
{
if ($oOther->fStartTime > $this->fStartTime) {
return -1;
}
return 1;
}
public function Contains(KpiLogData $oOther): bool
{
if ($oOther->fStartTime < $this->fStartTime) {
return false;
}
if ($oOther->fStartTime > $this->fStopTime) {
return false;
}
return true;
}
public function __toString()
{
return "$this->sType:$this->sOperation:$this->sArguments";
}
public function GetUUID(): string
{
return sha1($this->__toString());
}
}

View File

@@ -0,0 +1,214 @@
<?php
/**
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Service\Module;
use MetaModel;
use ReflectionClass;
use ReflectionMethod;
use utils;
class ModuleService
{
/** @var ModuleService */
private static $oInstance;
private function __construct()
{
}
public static function GetInstance(): ModuleService
{
if (!isset(static::$oInstance)) {
static::$oInstance = new ModuleService();
}
return static::$oInstance;
}
/**
* Get a "signature" of the method of an extension in the form of: "[module-name] class::method()"
*
* @param object|string $object Object or class
* @param string $sMethod
*
* @return string
* @throws \ReflectionException
*/
public function GetModuleMethodSignature($object, string $sMethod): string
{
$sSignature = '';
$oReflectionMethod = new ReflectionMethod($object, $sMethod);
$oReflectionClass = $oReflectionMethod->getDeclaringClass();
$sExtension = $this->GetModuleNameFromObject($oReflectionClass->getName());
if (strlen($sExtension) !== 0) {
$sSignature .= '['.$sExtension.'] ';
}
$sSignature .= $oReflectionClass->getShortName().'::'.$sMethod.'()';
return $sSignature;
}
/**
* Get the module name from an object or class
*
* @param object|string $object
*
* @return string
* @throws \ReflectionException
*/
public function GetModuleNameFromObject($object): string
{
$oReflectionClass = new ReflectionClass($object);
$sPath = str_replace('\\', '/', $oReflectionClass->getFileName());
$sPattern = str_replace('\\', '/', '@'.APPROOT.'env-'.utils::GetCurrentEnvironment()).'/(?<ext>.+)/@U';
if (preg_match($sPattern, $sPath, $aMatches) !== false) {
if (isset($aMatches['ext'])) {
return $aMatches['ext'];
}
}
return '';
}
/**
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
*
* @param int $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
*
* @return string the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
* this function is made
* or an empty string if no such module is found (or not called within a module file)
*
* @uses \debug_backtrace()
*/
public function GetCurrentModuleDir(int $iCallDepth): string
{
$sCurrentModuleDir = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleDir = basename($sRootDir);
break;
}
}
}
return $sCurrentModuleDir;
}
/**
* **Warning** : as this method uses {@see GetCurrentModuleDir} it produces hazardous results.
* You should better uses directly {@see GetAbsoluteUrlModulesRoot} and add the module dir name yourself ! See N°4573
*
* @return string the base URL for all files in the current module from which this method is called
* or an empty string if no such module is found (or not called within a module file)
* @throws \Exception
*
* @uses GetCurrentModuleDir
*/
public function GetCurrentModuleUrl(): string
{
$sDir = $this->GetCurrentModuleDir(1);
if ( $sDir !== '')
{
return utils::GetAbsoluteUrlModulesRoot().'/'.$sDir;
}
return '';
}
/**
* @param string $sProperty The name of the property to retrieve
* @param mixed $defaultValue
*
* @return mixed the value of a given setting for the current module
*/
public function GetCurrentModuleSetting(string $sProperty, $defaultValue = null)
{
$sModuleName = $this->GetCurrentModuleName(1);
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultValue);
}
/**
* @param string $sModuleName
*
* @return string|NULL compiled version of a given module, as it was seen by the compiler
*/
public function GetCompiledModuleVersion(string $sModuleName): ?string
{
$aModulesInfo = GetModulesInfo();
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
}
/**
* Returns the name of the module containing the file where the call to this function is made
* or an empty string if no such module is found (or not called within a module file)
*
* @param int $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
*
* @return string
*/
public function GetCurrentModuleName(int $iCallDepth = 0): string
{
$sCurrentModuleName = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
return $this->GetModuleNameFromPath($sCallerFile);
}
private function GetModuleNameFromPath($sPath)
{
foreach (GetModulesInfo() as $sModuleName => $aInfo) {
if ($aInfo['root_dir'] !== '') {
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if (substr($sPath, 0, strlen($sRootDir)) === $sRootDir) {
return $sModuleName;
}
}
}
return '';
}
/**
* Get the extension code from the call stack.
* Scan the call stack until a module is found.
*
* @param int $iLevelsToIgnore
*
* @return string module name
*/
public function GetModuleNameFromCallStack(int $iLevelsToIgnore = 0): string
{
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$aCallStack = array_slice($aCallStack, $iLevelsToIgnore);
foreach ($aCallStack as $aCallInfo) {
$sFile = realpath(empty($aCallInfo['file']) ? '' : $aCallInfo['file']);
$sModuleName = $this->GetModuleNameFromPath($sFile);
if (strlen($sModuleName) > 0) {
return $sModuleName;
}
}
return '';
}
}