From 9afc22bd8ffb906829a4ca87a3d683e34c1a767c Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Fri, 7 Jul 2023 09:29:08 +0200 Subject: [PATCH 1/5] =?UTF-8?q?=20N=C2=B06123=20-=20Add=20tests=20and=20co?= =?UTF-8?q?mments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup/backup.class.inc.php | 4 +++ .../unitary-tests/setup/DBBackupTest.php | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/setup/backup.class.inc.php b/setup/backup.class.inc.php index a28755afc..edb429961 100644 --- a/setup/backup.class.inc.php +++ b/setup/backup.class.inc.php @@ -523,6 +523,8 @@ EOF; } /** + * Define if we should force a transport option + * * @param string $sHost * * @return string . @@ -533,6 +535,8 @@ EOF; { $sTransportOptions = ''; + /** N°6123 As we're using a --port option, if we use localhost as host, + * MariaDB > 10.6 will implicitly change its protocol from socket to tcp and throw a warning **/ if($sHost === 'localhost'){ $sTransportOptions = '--protocol=tcp'; } diff --git a/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php b/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php index 9506b5a59..f0694967b 100644 --- a/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php @@ -84,4 +84,31 @@ class DBBackupTest extends ItopTestCase $this->assertStringStartsWith(' --ssl', $sCliArgsCapathCfg); $this->assertStringEndsWith('--ssl-ca='.DBBackup::EscapeShellArg($sTestCa), $sCliArgsCapathCfg); } + + /** + * Host is localhost, we should be forced into tcp + * + * @return void + */ + public function testGetMysqlCliTransportOptionWithLocalhost() + { + $sHost= 'localhost'; + $sTransport = DBBackup::GetMysqlCliTransportOption($sHost); + + $this->assertStringStartsWith('--protocol=tcp', $sTransport); + $this->assertStringEndsWith('--protocol=tcp', $sTransport); + } + + /** + * Host is not localhost, we shouldn't be forced into tcp + * + * @return void + */ + public function testGetMysqlCliTransportOptionWithoutLocalhost() + { + $sHost= '127.0.0.1'; + $sTransport = DBBackup::GetMysqlCliTransportOption($sHost); + + $this->assertEmpty($sTransport); + } } From 78396d8e4aef06a1949022a1643500f704ec27dc Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Mon, 10 Jul 2023 17:37:27 +0200 Subject: [PATCH 2/5] 6548 - [ER] Hide DBHost and DBUser in log --- application/startup.inc.php | 8 +++++++- core/counter.class.inc.php | 4 ++-- core/mutex.class.inc.php | 2 +- setup/backup.class.inc.php | 4 ++-- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/application/startup.inc.php b/application/startup.inc.php index 8f9ea4dd5..0454e7f63 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -91,4 +91,10 @@ else $_SESSION['itop_env'] = ITOP_DEFAULT_ENV; } $sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE; -MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv); +try { + MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv); +} +catch (MySQLException $e) { + IssueLog::Debug($e->getMessage()); + throw new MySQLException('Could not connect to the DB server', []); +} \ No newline at end of file diff --git a/core/counter.class.inc.php b/core/counter.class.inc.php index eeddc4249..e7fc88985 100644 --- a/core/counter.class.inc.php +++ b/core/counter.class.inc.php @@ -188,8 +188,8 @@ final class ItopCounter if (!$hDBLink) { - throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')'); - } + throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser)); + } return $hDBLink; } diff --git a/core/mutex.class.inc.php b/core/mutex.class.inc.php index f9580a963..bd6c54d18 100644 --- a/core/mutex.class.inc.php +++ b/core/mutex.class.inc.php @@ -257,7 +257,7 @@ class iTopMutex $this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false); if (!$this->hDBLink) { - throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')'); + throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser)); } // Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection, diff --git a/setup/backup.class.inc.php b/setup/backup.class.inc.php index edb429961..a7bb9ac70 100644 --- a/setup/backup.class.inc.php +++ b/setup/backup.class.inc.php @@ -417,8 +417,8 @@ EOF; if ($oMysqli->connect_errno) { $sHost = is_null($this->iDBPort) ? $this->sDBHost : $this->sDBHost.' on port '.$this->iDBPort; - throw new BackupException("Cannot connect to the MySQL server '$sHost' (".$oMysqli->connect_errno.") ".$oMysqli->connect_error); - } + throw new MySQLException('Could not connect to the DB server '.$oMysqli->connect_errno.' (mysql errno: '.$oMysqli->connect_error, array('host' => $sHost, 'user' => $sUser)); + } if (!$oMysqli->select_db($this->sDBName)) { throw new BackupException("The database '$this->sDBName' does not seem to exist"); From 2b5973ec6794e11556b1a9bae8f0375b3ac7fc85 Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Fri, 7 Jul 2023 09:29:08 +0200 Subject: [PATCH 3/5] =?UTF-8?q?N=C2=B06436=20-=20Integrate=20Performance?= =?UTF-8?q?=20Audit=20pre=20requisite=20in=20iTop=20Pro=202.7.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/applicationextension.inc.php | 24 +++ application/cmdbabstract.class.inc.php | 24 ++- application/utils.inc.php | 57 +----- bootstrap.inc.php | 10 +- composer.json | 1 + core/MyHelpers.class.inc.php | 6 +- core/cmdbsource.class.inc.php | 5 +- core/config.class.inc.php | 8 + core/dbobject.class.php | 25 ++- core/dbobjectset.class.php | 52 +++++- core/kpi.class.inc.php | 197 +++++++++++++++------ core/metamodel.class.php | 7 +- core/trigger.class.inc.php | 2 + lib/autoload.php | 5 - lib/composer/ClassLoader.php | 2 +- lib/composer/autoload_classmap.php | 5 +- lib/composer/autoload_files.php | 10 +- lib/composer/autoload_namespaces.php | 2 +- lib/composer/autoload_psr4.php | 2 +- lib/composer/autoload_real.php | 30 ++-- lib/composer/autoload_static.php | 11 +- lib/composer/include_paths.php | 2 +- log/.htaccess | 13 -- log/index.php | 2 - log/web.config | 13 -- sources/Core/Kpi/KpiLogData.php | 125 +++++++++++++ sources/Service/Module/ModuleService.php | 214 +++++++++++++++++++++++ 27 files changed, 663 insertions(+), 191 deletions(-) delete mode 100644 log/.htaccess delete mode 100644 log/index.php delete mode 100644 log/web.config create mode 100644 sources/Core/Kpi/KpiLogData.php create mode 100644 sources/Service/Module/ModuleService.php diff --git a/application/applicationextension.inc.php b/application/applicationextension.inc.php index 833ecb6e0..e50bd73aa 100644 --- a/application/applicationextension.inc.php +++ b/application/applicationextension.inc.php @@ -1857,4 +1857,28 @@ class RestUtils interface iModuleExtension { 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); } \ No newline at end of file diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 9f4188c18..f4844313e 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -4003,7 +4003,9 @@ EOF /** @var \iApplicationObjectExtension $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; @@ -4020,13 +4022,16 @@ EOF 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.) /** @var \iApplicationObjectExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) { + $oKPI = new ExecutionKPI(); $oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange()); + $oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert'); } return $oNewObj; @@ -4054,7 +4059,9 @@ EOF /** @var \iApplicationObjectExtension $oExtensionInstance */ foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) { + $oKPI = new ExecutionKPI(); $oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange()); + $oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate'); } } catch (Exception $e) @@ -4100,7 +4107,9 @@ EOF /** @var \iApplicationObjectExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) { + $oKPI = new ExecutionKPI(); $oExtensionInstance->OnDBDelete($this, self::GetCurrentChange()); + $oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete'); } return parent::DBDeleteTracked_Internal($oDeletionPlan); @@ -4118,7 +4127,10 @@ EOF /** @var \iApplicationObjectExtension $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; } @@ -4162,7 +4174,9 @@ EOF /** @var \iApplicationObjectExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) { + $oKPI = new ExecutionKPI(); $aNewIssues = $oExtensionInstance->OnCheckToWrite($this); + $oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite'); 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); @@ -4210,7 +4224,9 @@ EOF /** @var \iApplicationObjectExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) { + $oKPI = new ExecutionKPI(); $aNewIssues = $oExtensionInstance->OnCheckToDelete($this); + $oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete'); if (is_array($aNewIssues) && count($aNewIssues) > 0) { $this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues); @@ -4722,7 +4738,7 @@ EOF $bResult = (count($aErrors) == 0); if ($bResult) { - list($bResult, $aErrors) = $oObj->CheckToWrite(); + [$bResult, $aErrors] = $oObj->CheckToWrite(); } if ($bPreview) { diff --git a/application/utils.inc.php b/application/utils.inc.php index 4f12fec5f..2746aff09 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -17,6 +17,7 @@ * You should have received a copy of the GNU Affero General Public License */ +use Combodo\iTop\Service\Module\ModuleService; use ScssPhp\ScssPhp\Compiler; @@ -1946,24 +1947,7 @@ class utils */ public static function GetCurrentModuleName($iCallDepth = 0) { - $sCurrentModuleName = ''; - $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; + return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth = 0); } /** @@ -1979,24 +1963,7 @@ class utils */ public static function GetCurrentModuleDir($iCallDepth) { - $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; + return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth); } /** @@ -2011,12 +1978,7 @@ class utils */ public static function GetCurrentModuleUrl() { - $sDir = static::GetCurrentModuleDir(1); - if ( $sDir !== '') - { - return static::GetAbsoluteUrlModulesRoot().'/'.$sDir; - } - return ''; + return ModuleService::GetInstance()->GetCurrentModuleUrl(); } /** @@ -2026,8 +1988,7 @@ class utils */ public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null) { - $sModuleName = static::GetCurrentModuleName(1); - return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue); + return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue); } /** @@ -2036,12 +1997,7 @@ class utils */ public static function GetCompiledModuleVersion($sModuleName) { - $aModulesInfo = GetModulesInfo(); - if (array_key_exists($sModuleName, $aModulesInfo)) - { - return $aModulesInfo[$sModuleName]['version']; - } - return null; + return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName); } /** @@ -2505,4 +2461,5 @@ class utils return (substr(PHP_OS,0,3) === 'WIN'); } + } diff --git a/bootstrap.inc.php b/bootstrap.inc.php index abbc1a877..4c97a9811 100644 --- a/bootstrap.inc.php +++ b/bootstrap.inc.php @@ -22,14 +22,8 @@ define('ITOP_DEFAULT_ENV', 'production'); define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance'); define('READONLY_MODE_FILE', APPROOT.'data/.readonly'); -if (function_exists('microtime')) -{ - $fItopStarted = microtime(true); -} -else -{ - $fItopStarted = 1000 * time(); -} +$fItopStarted = microtime(true); +$iItopInitialMemory = memory_get_usage(true); if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) { diff --git a/composer.json b/composer.json index afa4b10b3..e236ecfea 100644 --- a/composer.json +++ b/composer.json @@ -62,6 +62,7 @@ "sources/application", "sources/Composer", "sources/Controller", + "sources/Service", "sources/Core" ], "exclude-from-classmap": [ diff --git a/core/MyHelpers.class.inc.php b/core/MyHelpers.class.inc.php index 9d78d657c..31e5a253a 100644 --- a/core/MyHelpers.class.inc.php +++ b/core/MyHelpers.class.inc.php @@ -67,8 +67,7 @@ class MyHelpers // format sss.mmmuuupppnnn public static function getmicrotime() { - list($usec, $sec) = explode(" ",microtime()); - return ((float)$usec + (float)$sec); + return microtime(true); } /* @@ -420,6 +419,7 @@ class MyHelpers //} return $sOutput; } + } /** @@ -524,5 +524,3 @@ class Str return (strtolower($sString) == $sString); } } - -?> diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 190babbdc..6c0839cc7 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -731,8 +731,9 @@ class CMDBSource { self::LogDeadLock($e); throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e)); - } - $oKPI->ComputeStats('Query exec (mySQL)', $sSql); + } finally { + $oKPI->ComputeStats('Query exec (mySQL)', $sSql); + } if ($oResult === false) { $aContext = array('query' => $sSql); diff --git a/core/config.class.inc.php b/core/config.class.inc.php index c0178cc7e..3d23ed24b 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -963,6 +963,14 @@ class Config 'source_of_value' => '', '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( '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.', diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 2baf3c3f7..3d87d3abf 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -2225,7 +2225,7 @@ abstract class DBObject implements iDisplay $oKPI = new ExecutionKPI(); $this->DoCheckToWrite(); - $oKPI->ComputeStats('CheckToWrite', get_class($this)); + $oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite'); if (count($this->m_aCheckIssues) == 0) { $this->m_bCheckStatus = true; @@ -2693,8 +2693,12 @@ abstract class DBObject implements iDisplay $sRootClass = MetaModel::GetRootClass($sClass); // Ensure the update of the values (we are accessing the data directly) + $oKPI = new ExecutionKPI(); $this->DoComputeValues(); + $oKPI->ComputeStatsForExtension($this, 'DoComputeValues'); + $oKPI = new ExecutionKPI(); $this->OnInsert(); + $oKPI->ComputeStatsForExtension($this, 'OnInsert'); if ($this->m_iKey < 0) { @@ -2712,7 +2716,7 @@ abstract class DBObject implements iDisplay } // Ultimate check - ensure DB integrity - list($bRes, $aIssues) = $this->CheckToWrite(); + [$bRes, $aIssues] = $this->CheckToWrite(); if (!$bRes) { 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; } + $oKPI = new ExecutionKPI(); $this->AfterInsert(); + $oKPI->ComputeStatsForExtension($this, 'AfterInsert'); // Activate any existing trigger $sClass = get_class($this); @@ -3094,8 +3100,11 @@ abstract class DBObject implements iDisplay try { + $oKPI = new ExecutionKPI(); $this->DoComputeValues(); - // Stop watches + $oKPI->ComputeStatsForExtension($this, 'DoComputeValues'); + + // Stop watches $sState = $this->GetState(); 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(); if (count($aChanges) == 0) @@ -3126,7 +3137,7 @@ abstract class DBObject implements iDisplay } // Ultimate check - ensure DB integrity - list($bRes, $aIssues) = $this->CheckToWrite(); + [$bRes, $aIssues] = $this->CheckToWrite(); if (!$bRes) { throw new CoreCannotSaveObjectException(array( @@ -3326,7 +3337,9 @@ abstract class DBObject implements iDisplay try { - $this->AfterUpdate(); + $oKPI = new ExecutionKPI(); + $this->AfterUpdate(); + $oKPI->ComputeStatsForExtension($this, 'AfterUpdate'); // Reload to get the external attributes if ($bHasANewExternalKeyValue) diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index 076dbb954..d90e652a4 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -763,7 +763,10 @@ class DBObjectSet implements iDBObjectSetIterator try { + $oKPI = new ExecutionKPI(); $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) { // 1116 = ER_TOO_MANY_TABLES @@ -843,8 +846,11 @@ class DBObjectSet implements iDBObjectSetIterator { if (is_null($this->m_iNumTotalDBRows)) { + $oKPI = new ExecutionKPI(); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true); $resQuery = CMDBSource::Query($sSQL); + $sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true); + $oKPI->ComputeStats('OQL Query Exec', $sOQL); if (!$resQuery) return 0; $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 ?? } + /** + * @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 * @@ -871,8 +913,11 @@ class DBObjectSet implements iDBObjectSetIterator { if (is_null($this->m_iNumTotalDBRows)) { + $oKPI = new ExecutionKPI(); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true); $resQuery = CMDBSource::Query($sSQL); + $sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true); + $oKPI->ComputeStats('OQL Query Exec', $sOQL); if ($resQuery) { $aRow = CMDBSource::FetchArray($resQuery); @@ -883,7 +928,7 @@ class DBObjectSet implements iDBObjectSetIterator { $iCount = 0; } - } + } else { $iCount = $this->m_iNumTotalDBRows; @@ -908,8 +953,11 @@ class DBObjectSet implements iDBObjectSetIterator { if (is_null($this->m_iNumTotalDBRows)) { + $oKPI = new ExecutionKPI(); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true); $resQuery = CMDBSource::Query($sSQL); + $sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true); + $oKPI->ComputeStats('OQL Query Exec', $sOQL); if ($resQuery) { $aRow = CMDBSource::FetchArray($resQuery); @@ -920,7 +968,7 @@ class DBObjectSet implements iDBObjectSetIterator { $iCount = 0; } - } + } else { $iCount = $this->m_iNumTotalDBRows; diff --git a/core/kpi.class.inc.php b/core/kpi.class.inc.php index a5514e23b..57f167d87 100644 --- a/core/kpi.class.inc.php +++ b/core/kpi.class.inc.php @@ -15,6 +15,8 @@ // // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see +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_bBlameCaller = false; static protected $m_sAllowedUser = '*'; + static protected $m_bReportExtensionsOnly = false; + static protected $m_fSlowQueries = 0; static protected $m_aStats = array(); // Recurrent operations static protected $m_aExecData = array(); // One shot operations @@ -77,14 +81,39 @@ class ExecutionKPI return false; } + static public function SetReportExtensionsOnly($bReportExtensionsOnly) + { + self::$m_bReportExtensionsOnly = $bReportExtensionsOnly; + } + + static public function SetSlowQueries($fSlowQueries) + { + self::$m_fSlowQueries = $fSlowQueries; + } + static public function GetDescription() { $aFeatures = array(); if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration'; if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage'; - $sFeatures = implode(', ', $aFeatures); + $sFeatures = 'Measures: '.implode(', ', $aFeatures); $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() @@ -92,7 +121,28 @@ class ExecutionKPI if (!self::IsEnabled()) return; global $fItopStarted; + global $iItopInitialMemory; $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(); foreach (self::$m_aExecData as $aOpStats) @@ -105,7 +155,7 @@ class ExecutionKPI self::Report("
"); self::Report("
"); - self::Report("

KPIs - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")

"); + self::Report("

KPIs - $sRequest

"); self::Report("

".date('Y-m-d H:i:s', $fItopStarted)."

"); self::Report("

log_kpi_user_id: ".UserRights::GetUserId()."

"); self::Report("
"); @@ -200,8 +250,6 @@ class ExecutionKPI self::Report("

Next page stats

"); - $fSlowQueries = MetaModel::GetConfig()->Get('log_kpi_slow_queries'); - // Report operation details foreach (self::$m_aStats as $sOperation => $aOpStats) { @@ -245,7 +293,7 @@ class ExecutionKPI $sTotalInter = round($fTotalInter, 3); $sMinInter = round($fMinInter, 3); $sMaxInter = round($fMaxInter, 3); - if (($fTotalInter >= $fSlowQueries)) + if (($fTotalInter >= self::$m_fSlowQueries)) { if ($bDisplayHeader) { @@ -271,11 +319,19 @@ class ExecutionKPI self::Report(' '); } + 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() { $this->ResetCounters(); - } + } // Get the duration since startup, and reset the counter for the next measure // @@ -285,6 +341,8 @@ class ExecutionKPI $aNewEntry = null; + $fStarted = $this->m_fStarted; + $fStopped = $this->m_fStarted; if (self::$m_bEnabled_Duration) { $fStopped = MyHelpers::getmicrotime(); @@ -297,6 +355,9 @@ class ExecutionKPI $this->m_fStarted = $fStopped; } + $iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory; + $iCurrentMemory = 0; + $iPeakMemory = 0; if (self::$m_bEnabled_Memory) { $iCurrentMemory = self::memory_get_usage(); @@ -306,41 +367,95 @@ class ExecutionKPI } $aNewEntry['mem_begin'] = $this->m_iInitialMemory; $aNewEntry['mem_end'] = $iCurrentMemory; - if (function_exists('memory_get_peak_usage')) - { - $aNewEntry['mem_peak'] = memory_get_peak_usage(); - } + $iPeakMemory = self::memory_get_peak_usage(); + $aNewEntry['mem_peak'] = $iPeakMemory; // Reset for the next operation (if the object is recycled) $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; } $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) { if (self::$m_bEnabled_Duration) { $fStopped = MyHelpers::getmicrotime(); $fDuration = $fStopped - $this->m_fStarted; - if (self::$m_bBlameCaller) - { - self::$m_aStats[$sOperation][$sArguments][] = array( - 'time' => $fDuration, - 'callers' => MyHelpers::get_callstack(1), - ); - } - else - { - self::$m_aStats[$sOperation][$sArguments][] = array( - 'time' => $fDuration - ); - } - } + $aCallstack = []; + if (!self::$m_bReportExtensionsOnly) { + if (self::$m_bBlameCaller) { + $aCallstack = MyHelpers::get_callstack(1); + self::$m_aStats[$sOperation][$sArguments][] = [ + 'time' => $fDuration, + 'callers' => $aCallstack, + ]; + } else { + self::$m_aStats[$sOperation][$sArguments][] = [ + '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() @@ -370,35 +485,7 @@ class ExecutionKPI static protected function memory_get_usage() { - if (function_exists('memory_get_usage')) - { - 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; - } + return memory_get_usage(true); } static public function memory_get_peak_usage($bRealUsage = false) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index d523fcc87..9af5005e8 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2778,7 +2778,7 @@ abstract class MetaModel // 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) { self::$m_aExtensionClasses[$sInterface] = array(); @@ -6348,7 +6348,9 @@ abstract class MetaModel ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration')); 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_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys'); @@ -6485,6 +6487,7 @@ abstract class MetaModel CMDBSource::InitFromConfig(self::$m_oConfig); // Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone); + ExecutionKPI::InitStats(); } /** diff --git a/core/trigger.class.inc.php b/core/trigger.class.inc.php index 6783882ca..99a389f2f 100644 --- a/core/trigger.class.inc.php +++ b/core/trigger.class.inc.php @@ -126,7 +126,9 @@ abstract class Trigger extends cmdbAbstractObject $oAction = MetaModel::GetObject('Action', $iActionId); if ($oAction->IsActive()) { + $oKPI = new ExecutionKPI(); $oAction->DoExecute($this, $aContextArgs); + $oKPI->ComputeStatsForExtension($oAction, 'DoExecute'); } } } diff --git a/lib/autoload.php b/lib/autoload.php index 64168f99a..79c1600b5 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -2,11 +2,6 @@ // 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'; return ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b::getLoader(); diff --git a/lib/composer/ClassLoader.php b/lib/composer/ClassLoader.php index afef3fa2a..0cd6055d1 100644 --- a/lib/composer/ClassLoader.php +++ b/lib/composer/ClassLoader.php @@ -149,7 +149,7 @@ class ClassLoader /** * @return string[] Array of classname => path - * @psalm-return array + * @psalm-var array */ public function getClassMap() { diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index adac47083..f080a002f 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -2,7 +2,7 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); 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\\Email\\EmailFactory' => $baseDir . '/sources/Core/Email/EmailFactory.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\\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', 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Config' => $baseDir . '/core/config.class.inc.php', @@ -2712,6 +2714,7 @@ return array( 'iDBObjectSetIterator' => $baseDir . '/core/dbobjectiterator.php', 'iDBObjectURLMaker' => $baseDir . '/application/applicationcontext.class.inc.php', 'iDisplay' => $baseDir . '/core/dbobject.class.php', + 'iKPILoggerExtension' => $baseDir . '/application/applicationextension.inc.php', 'iLogFileNameBuilder' => $baseDir . '/core/log.class.inc.php', 'iLoginExtension' => $baseDir . '/application/applicationextension.inc.php', 'iLoginFSMExtension' => $baseDir . '/application/applicationextension.inc.php', diff --git a/lib/composer/autoload_files.php b/lib/composer/autoload_files.php index ae02e5199..7be757bea 100644 --- a/lib/composer/autoload_files.php +++ b/lib/composer/autoload_files.php @@ -2,25 +2,25 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '5255c38a0faeba867671b61dfda6d864' => $vendorDir . '/paragonie/random_compat/lib/random.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', 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', - 'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/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', - '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', '2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php', ); diff --git a/lib/composer/autoload_namespaces.php b/lib/composer/autoload_namespaces.php index e6117c750..d12922d08 100644 --- a/lib/composer/autoload_namespaces.php +++ b/lib/composer/autoload_namespaces.php @@ -2,7 +2,7 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index 651c9f0c1..ca8b4b9f6 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -2,7 +2,7 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( diff --git a/lib/composer/autoload_real.php b/lib/composer/autoload_real.php index 752e35fbd..661cd2543 100644 --- a/lib/composer/autoload_real.php +++ b/lib/composer/autoload_real.php @@ -25,20 +25,33 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b require __DIR__ . '/platform_check.php'; 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')); $includePaths = require __DIR__ . '/include_paths.php'; $includePaths[] = get_include_path(); set_include_path(implode(PATH_SEPARATOR, $includePaths)); - require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit0018331147de7601e7552f7da8e3bb8b::getInitializer($loader)); + $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); + 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->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) { composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file); } @@ -47,16 +60,11 @@ class ComposerAutoloaderInit0018331147de7601e7552f7da8e3bb8b } } -/** - * @param string $fileIdentifier - * @param string $file - * @return void - */ function composerRequire0018331147de7601e7552f7da8e3bb8b($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - require $file; + + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; } } diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index de1646c31..4a74a9cc4 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -8,21 +8,21 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b { public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.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', 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', '25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', - 'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php', 'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php', 'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/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', - '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.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\\Email\\EmailFactory' => __DIR__ . '/../..' . '/sources/Core/Email/EmailFactory.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\\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', 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 'Config' => __DIR__ . '/../..' . '/core/config.class.inc.php', @@ -3080,6 +3082,7 @@ class ComposerStaticInit0018331147de7601e7552f7da8e3bb8b 'iDBObjectSetIterator' => __DIR__ . '/../..' . '/core/dbobjectiterator.php', 'iDBObjectURLMaker' => __DIR__ . '/../..' . '/application/applicationcontext.class.inc.php', 'iDisplay' => __DIR__ . '/../..' . '/core/dbobject.class.php', + 'iKPILoggerExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', 'iLogFileNameBuilder' => __DIR__ . '/../..' . '/core/log.class.inc.php', 'iLoginExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', 'iLoginFSMExtension' => __DIR__ . '/../..' . '/application/applicationextension.inc.php', diff --git a/lib/composer/include_paths.php b/lib/composer/include_paths.php index af33c1491..d4fb96718 100644 --- a/lib/composer/include_paths.php +++ b/lib/composer/include_paths.php @@ -2,7 +2,7 @@ // include_paths.php @generated by Composer -$vendorDir = dirname(__DIR__); +$vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( diff --git a/log/.htaccess b/log/.htaccess deleted file mode 100644 index 782472c78..000000000 --- a/log/.htaccess +++ /dev/null @@ -1,13 +0,0 @@ -# Apache 2.4 - -Require all denied - - -# Apache 2.2 - -deny from all -Satisfy All - - -# Apache 2.2 and 2.4 -IndexIgnore * diff --git a/log/index.php b/log/index.php deleted file mode 100644 index 112807643..000000000 --- a/log/index.php +++ /dev/null @@ -1,2 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/sources/Core/Kpi/KpiLogData.php b/sources/Core/Kpi/KpiLogData.php new file mode 100644 index 000000000..de39ab388 --- /dev/null +++ b/sources/Core/Kpi/KpiLogData.php @@ -0,0 +1,125 @@ +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()); + } +} \ No newline at end of file diff --git a/sources/Service/Module/ModuleService.php b/sources/Service/Module/ModuleService.php new file mode 100644 index 000000000..5370f7bbe --- /dev/null +++ b/sources/Service/Module/ModuleService.php @@ -0,0 +1,214 @@ +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()).'/(?.+)/@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 ''; + } + +} \ No newline at end of file From da45651121f576c8703812d1f82493eb3a2941e8 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Tue, 18 Jul 2023 09:34:48 +0200 Subject: [PATCH 4/5] Merge branch 'feature/6548_Hide_DBHost_and_DBUser_in_log' into support/2.7 --- log/.htaccess | 13 +++++++++++++ log/index.php | 2 ++ log/web.config | 8 ++++++++ 3 files changed, 23 insertions(+) create mode 100644 log/.htaccess create mode 100644 log/index.php create mode 100644 log/web.config diff --git a/log/.htaccess b/log/.htaccess new file mode 100644 index 000000000..782472c78 --- /dev/null +++ b/log/.htaccess @@ -0,0 +1,13 @@ +# Apache 2.4 + +Require all denied + + +# Apache 2.2 + +deny from all +Satisfy All + + +# Apache 2.2 and 2.4 +IndexIgnore * diff --git a/log/index.php b/log/index.php new file mode 100644 index 000000000..112807643 --- /dev/null +++ b/log/index.php @@ -0,0 +1,2 @@ + + + + + + + + \ No newline at end of file From b2e80d37dd44c93ad89403ca301fb896780949e4 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Tue, 18 Jul 2023 14:48:32 +0200 Subject: [PATCH 5/5] =?UTF-8?q?N=C2=B06436=20-=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/applicationextension.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/applicationextension.inc.php b/application/applicationextension.inc.php index c9a1279aa..1b64d2668 100644 --- a/application/applicationextension.inc.php +++ b/application/applicationextension.inc.php @@ -2204,9 +2204,9 @@ interface iKPILoggerExtension /** * Add a new KPI to the stats * - * @param \Combodo\iTop\Core\Kpi\KpiLogData $oKPILogData + * @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData * * @return mixed */ - public function LogOperation($oKPILogData); + public function LogOperation($oKpiLogData); } \ No newline at end of file