From 54c7af11406747d3872a93284601f893f40b0c00 Mon Sep 17 00:00:00 2001 From: odain Date: Tue, 27 Jan 2026 17:26:16 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B08760=20-=20rename=20GetCreatedIn=20<-=20?= =?UTF-8?q?GetModuleName=20+=20compute=20module=20name=20live=20instead=20?= =?UTF-8?q?having=20complex=20stuff=20in=20MetaModel/compilation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/metamodel.class.php | 61 ++-- core/metamodel/IncludeFileReader.php | 52 --- setup/compiler.class.inc.php | 10 +- .../unitary-tests/core/MetaModelTest.php | 13 +- .../core/metamodel/IncludeFileReaderTest.php | 22 -- .../resources/ownershiplock.class.inc.php | 328 ------------------ 6 files changed, 45 insertions(+), 441 deletions(-) delete mode 100644 core/metamodel/IncludeFileReader.php delete mode 100644 tests/php-unit-tests/unitary-tests/core/metamodel/IncludeFileReaderTest.php delete mode 100644 tests/php-unit-tests/unitary-tests/core/metamodel/resources/ownershiplock.class.inc.php diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 08a1bb3e8..61d626db3 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -22,6 +22,8 @@ use Combodo\iTop\Application\EventRegister\ApplicationEvents; use Combodo\iTop\Core\MetaModel\FriendlyNameType; use Combodo\iTop\Service\Events\EventData; use Combodo\iTop\Service\Events\EventService; +use Combodo\iTop\Setup\ModuleDependency\Module; +use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader; require_once APPROOT.'core/modulehandler.class.inc.php'; require_once APPROOT.'core/querymodifier.class.inc.php'; @@ -30,7 +32,6 @@ require_once APPROOT.'core/computing.inc.php'; require_once APPROOT.'core/relationgraph.class.inc.php'; require_once APPROOT.'core/apc-compat.php'; require_once APPROOT.'core/expressioncache.class.inc.php'; -require_once APPROOT.'core/metamodel/IncludeFileReader.php'; /** * We need to have all iLoginFSMExtension/iLoginUIExtension impl loaded ! Cannot use autoloader... @@ -289,10 +290,6 @@ abstract class MetaModel * @var array */ private static $m_aRootClasses = []; - - //used to populate DM create_in field for classes declared in 'datamodel' - public static array $m_aCreatedIn = []; - /** * array of ("classname" => array of "parentclass") * @@ -473,11 +470,36 @@ abstract class MetaModel * @return string * @throws \CoreException */ - final public static function GetCreatedIn($sClass) + final public static function GetModuleName($sClass) { - self::_check_subclass($sClass); + try { + $oReflectionClass = new ReflectionClass($sClass); + $sDir = realpath(dirname($oReflectionClass->getFileName())); + $sApproot = realpath(APPROOT); + while (($sDir !== $sApproot) && (str_contains($sDir, $sApproot))) { + $aFiles = glob("$sDir/module.*.php"); + if (count($aFiles) > 1) { + return 'core'; + } - return self::$m_aClassParams[$sClass]['created_in'] ?? ""; + if (count($aFiles) == 0) { + $sDir = realpath(dirname($sDir)); + continue; + } + + $sModuleFilePath = $aFiles[0]; + $aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath); + $sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID]; + list($sModuleName, ) = ModuleDiscovery::GetModuleName($sModuleId); + + return $sModuleName; + } + } catch (\Exception $e) { + IssueLog::Error("Cannot compute class properly", null, [$e->getTraceAsString(), $e->getMessage()]); + return ""; + } + + return 'core'; } /** @@ -3163,7 +3185,6 @@ abstract class MetaModel $aMandatParams = [ "category" => "group classes by modules defining their visibility in the UI", "key_type" => "autoincrement | string", - //"created_in" => "module_name where class is defined", "name_attcode" => "define which attribute is the class name, may be an array of attributes (format specified in the dictionary as 'Class:myclass/Name' => '%1\$s %2\$s...'", "state_attcode" => "define which attribute is representing the state (object lifecycle)", "reconc_keys" => "define the attributes that will 'almost uniquely' identify an object in batch processes", @@ -3190,10 +3211,6 @@ abstract class MetaModel self::$m_aParentClasses[$sClass] = []; self::$m_aChildClasses[$sClass] = []; - if (! array_key_exists('created_in', $aParams)) { - $aParams['created_in'] = self::$m_aCreatedIn[$sClass] ?? ''; - } - self::$m_aClassParams[$sClass] = $aParams; self::$m_aAttribDefs[$sClass] = []; @@ -5873,7 +5890,6 @@ abstract class MetaModel // todo - verifier que toutes les classes mentionnees ici sont chargees dans InitClasses() self::$m_aExtensionClassNames = $result['m_aExtensionClassNames']; self::$m_Category2Class = $result['m_Category2Class']; - self::$m_aCreatedIn = $result['m_aCreatedIn']; self::$m_aRootClasses = $result['m_aRootClasses']; self::$m_aParentClasses = $result['m_aParentClasses']; self::$m_aChildClasses = $result['m_aChildClasses']; @@ -5909,7 +5925,6 @@ abstract class MetaModel $aCache = []; $aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames; $aCache['m_Category2Class'] = self::$m_Category2Class; - $aCache['m_aCreatedIn'] = self::$m_aCreatedIn; // array of "classname" => "created_in" $aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass" $aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass") $aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass") @@ -6010,12 +6025,11 @@ abstract class MetaModel /** * @param string $sToInclude - * @param string|null $sModuleType - * @param string|null $sModuleName + * @param string $sModuleType * * @throws \CoreException */ - public static function IncludeModule($sToInclude, $sModuleType = null, $sModuleName = null) + public static function IncludeModule($sToInclude, $sModuleType = null) { $sFirstChar = substr($sToInclude, 0, 1); $sSecondChar = substr($sToInclude, 1, 1); @@ -6046,17 +6060,6 @@ abstract class MetaModel } } - if (! is_null($sModuleName)) { - try { - $aClasses = IncludeFileReader::GetInstance()->GetClasses($sFile); - foreach ($aClasses as $sFoundClass) { - MetaModel::$m_aCreatedIn[$sFoundClass] = $sModuleName; - } - } catch (\Exception $e) { - SetupLog::Error(__METHOD__, null, [$e]); - } - } - // Note: We do not expect the modules to output characters while loading them. // Therefore, and because unexpected characters can corrupt the output, // they must be trashed here. diff --git a/core/metamodel/IncludeFileReader.php b/core/metamodel/IncludeFileReader.php deleted file mode 100644 index 8cc0b2d60..000000000 --- a/core/metamodel/IncludeFileReader.php +++ /dev/null @@ -1,52 +0,0 @@ -createForNewestSupportedVersion(); - $aNodes = $oParser->parse(file_get_contents($sPath)); - } catch (Error $e) { - throw new Exception("PHP Class Parsing of $sPath caused an exception: ".$e->getMessage(), 0, $e); - } - - $aRes = []; - try { - foreach ($aNodes as $sKey => $oNode) { - if ($oNode instanceof PhpParser\Node\Stmt\Class_) { - /** @var PhpParser\Node\Stmt\Class_ $oNode */ - //var_dump($oNode->name); - $aRes[] = $oNode->name->name; - } - - } - return $aRes; - } catch (Exception $e) { - throw new Exception("PHP Class Discovery of $sPath caused an exception: ".$e->getMessage(), 0, $e); - } - } -} diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 823ad8980..7c4741a80 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -477,7 +477,7 @@ class MFCompiler $sClass = $oClass->getAttribute("id"); $aAllClasses[] = $sClass; try { - $sCompiledCode .= $this->CompileClass($oClass, $sModuleName, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); + $sCompiledCode .= $this->CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sRelativeDir); } catch (DOMFormatException $e) { $sMessage = "Failed to process class '$sClass', "; if (!empty($sModuleRootDir)) { @@ -626,7 +626,7 @@ EOF; // files to include (PHP datamodels) foreach ($oModule->GetFilesToInclude('business') as $sRelFileName) { if (file_exists("{$sTempTargetDir}/{$sRelativeDir}/{$sRelFileName}")) { - $aDataModelFiles[] = sprintf("MetaModel::IncludeModule(MODULESROOT.'/$sRelativeDir/$sRelFileName', null, '%s');", $oModule->GetName()); + $aDataModelFiles[] = "MetaModel::IncludeModule(MODULESROOT.'/$sRelativeDir/$sRelFileName');"; } else { /** @noinspection NestedPositiveIfStatementsInspection */ if (utils::IsDevelopmentEnvironment()) { @@ -643,7 +643,7 @@ EOF; } // files to include (PHP webservices providers) foreach ($oModule->GetFilesToInclude('webservices') as $sRelFileName) { - $aWebservicesFiles[] = sprintf("MetaModel::IncludeModule(MODULESROOT.'/$sRelativeDir/$sRelFileName', null, '%s');", $oModule->GetName()); + $aWebservicesFiles[] = "MetaModel::IncludeModule(MODULESROOT.'/$sRelativeDir/$sRelFileName');"; } } // foreach module @@ -1189,7 +1189,6 @@ EOF /** * @param \MFElement $oClass - * @param string $sModuleName * @param string $sTempTargetDir * @param string $sFinalTargetDir * @param string $sModuleRelativeDir @@ -1197,7 +1196,7 @@ EOF * @return string * @throws \DOMFormatException */ - protected function CompileClass($oClass, $sModuleName, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir) + protected function CompileClass($oClass, $sTempTargetDir, $sFinalTargetDir, $sModuleRelativeDir) { $sClass = $oClass->getAttribute('id'); $oProperties = $oClass->GetUniqueElement('properties'); @@ -1210,7 +1209,6 @@ EOF $aClassParams = []; $aClassParams['category'] = $this->GetPropString($oProperties, 'category', ''); $aClassParams['key_type'] = "'autoincrement'"; - $aClassParams['created_in'] = "'$sModuleName'"; if ((bool)$this->GetPropNumber($oProperties, 'is_link', 0)) { $aClassParams['is_link'] = 'true'; } diff --git a/tests/php-unit-tests/unitary-tests/core/MetaModelTest.php b/tests/php-unit-tests/unitary-tests/core/MetaModelTest.php index c9cb24ea6..241ddff5e 100644 --- a/tests/php-unit-tests/unitary-tests/core/MetaModelTest.php +++ b/tests/php-unit-tests/unitary-tests/core/MetaModelTest.php @@ -499,24 +499,29 @@ class MetaModelTest extends ItopDataTestCase ]; } + public function testGetCreatedIn_UnknownClass() + { + $this->assertEquals('', MetaModel::GetModuleName('GABUZOMEU')); + } + public function testGetCreatedIn_ClassComingFromCorePhpFile() { - $this->assertEquals('', MetaModel::GetCreatedIn('BackgroundTask')); + $this->assertEquals('core', MetaModel::GetModuleName('BackgroundTask')); } public function testGetCreatedIn_ClassComingFromCorePhpFile2() { - $this->assertEquals('core', MetaModel::GetCreatedIn('lnkActionNotificationToContact')); + $this->assertEquals('core', MetaModel::GetModuleName('lnkActionNotificationToContact')); } public function testGetCreatedIn_ClassComingFromModulePhpFile() { - $this->assertEquals('itop-attachments', MetaModel::GetCreatedIn('CMDBChangeOpAttachmentAdded')); + $this->assertEquals('itop-attachments', MetaModel::GetModuleName('CMDBChangeOpAttachmentAdded')); } public function testGetCreatedIn_ClassComingFromXmlDataModelFile() { - $this->assertEquals('authent-ldap', MetaModel::GetCreatedIn('UserLDAP')); + $this->assertEquals('authent-ldap', MetaModel::GetModuleName('UserLDAP')); } } diff --git a/tests/php-unit-tests/unitary-tests/core/metamodel/IncludeFileReaderTest.php b/tests/php-unit-tests/unitary-tests/core/metamodel/IncludeFileReaderTest.php deleted file mode 100644 index e6c0c4fec..000000000 --- a/tests/php-unit-tests/unitary-tests/core/metamodel/IncludeFileReaderTest.php +++ /dev/null @@ -1,22 +0,0 @@ -RequireOnceItopFile('core/metamodel/IncludeFileReader.php'); - } - - public function testGetClasses() - { - $expected = [ - 'iTopOwnershipToken', - 'iTopOwnershipLock', - ]; - $this->assertEquals($expected, IncludeFileReader::GetInstance()->GetClasses(__DIR__.'/resources/ownershiplock.class.inc.php')); - } -} diff --git a/tests/php-unit-tests/unitary-tests/core/metamodel/resources/ownershiplock.class.inc.php b/tests/php-unit-tests/unitary-tests/core/metamodel/resources/ownershiplock.class.inc.php deleted file mode 100644 index a72131adc..000000000 --- a/tests/php-unit-tests/unitary-tests/core/metamodel/resources/ownershiplock.class.inc.php +++ /dev/null @@ -1,328 +0,0 @@ - - -/** - * Mechanism to obtain an exclusive lock while editing an object - * - * @package iTopORM - */ - -/** - * Persistent storage (in the database) for remembering that an object is locked - */ -class iTopOwnershipToken extends DBObject -{ - public static function Init() - { - $aParams = - [ - 'category' => '', - 'key_type' => 'autoincrement', - 'name_attcode' => ['obj_class', 'obj_key'], - 'state_attcode' => '', - 'reconc_keys' => [''], - 'db_table' => 'priv_ownership_token', - 'db_key_field' => 'id', - 'db_finalclass_field' => '', - ]; - MetaModel::Init_Params($aParams); - MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", ["allowed_values" => null, "sql" => 'acquired', "default_value" => 'NOW()', "is_null_allowed" => false, "depends_on" => []])); - MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", ["allowed_values" => null, "sql" => 'last_seen', "default_value" => 'NOW()', "is_null_allowed" => false, "depends_on" => []])); - MetaModel::Init_AddAttribute(new AttributeString("obj_class", ["allowed_values" => null, "sql" => 'obj_class', "default_value" => '', "is_null_allowed" => false, "depends_on" => []])); - MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", ["allowed_values" => null, "sql" => 'obj_key', "default_value" => '', "is_null_allowed" => true, "depends_on" => []])); - MetaModel::Init_AddAttribute(new AttributeString("token", ["allowed_values" => null, "sql" => 'token', "default_value" => '', "is_null_allowed" => true, "depends_on" => []])); - MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", ["targetclass" => "User", "jointype" => '', "allowed_values" => null, "sql" => "user_id", "is_null_allowed" => true, "on_target_delete" => DEL_SILENT, "depends_on" => []])); - - MetaModel::Init_SetZListItems('details', ['obj_class', 'obj_key', 'last_seen', 'token']); - MetaModel::Init_SetZListItems('standard_search', ['obj_class', 'obj_key', 'last_seen', 'token']); - MetaModel::Init_SetZListItems('list', ['obj_class', 'obj_key', 'last_seen', 'token']); - - } -} - -/** - * Utility class to acquire/extend/release/kill an exclusive lock on a given persistent object, - * for example to prevent concurrent edition of the same object. - * Each lock has an expiration delay of 120 seconds (tunable via the configuration parameter 'concurrent_lock_expiration_delay') - * A watchdog (called twice during this delay) is in charge of keeping the lock "alive" while an object is being edited. - */ -class iTopOwnershipLock -{ - protected $sObjClass; - protected $iObjKey; - protected $oToken; - - /** - * Acquires an exclusive lock on the specified DBObject. Once acquired, the lock is identified - * by a unique "token" string. - * @param string $sObjClass The class of the object for which to acquire the lock - * @param integer $iObjKey The identifier of the object for which to acquire the lock - * @return multitype:boolean iTopOwnershipLock Ambigous - */ - public static function AcquireLock($sObjClass, $iObjKey) - { - $oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey); - - $oMutex->Lock(); - $oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey); - $token = $oOwnershipLock->Acquire(); - $oMutex->Unlock(); - - return ['success' => $token !== false, 'token' => $token, 'lock' => $oOwnershipLock, 'acquired' => $oOwnershipLock->oToken->Get('acquired')]; - } - - /** - * Extends the ownership lock or acquires it if none exists - * Returns a hash array with 3 elements: - * 'status': either true or false, tells if the lock is still owned - * 'owner': is status is false, the User object currently owning the lock - * 'operation': whether the lock was 'renewed' (i.e. the lock was valid, its duration has been extended) or 'acquired' (there was no valid lock for this object and a new one was created) - * @param string $sToken - * @return multitype:boolean string User - */ - public static function ExtendLock($sObjClass, $iObjKey, $sToken) - { - $oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey); - - $oMutex->Lock(); - $oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey); - $aResult = $oOwnershipLock->Extend($sToken); - $oMutex->Unlock(); - - return $aResult; - } - - /** - * Releases the given lock for the specified object - * - * @param string $sObjClass The class of the object - * @param int $iObjKey The identifier of the object - * @param string $sToken The string identifying the lock - * @return boolean - */ - public static function ReleaseLock($sObjClass, $iObjKey, $sToken) - { - $oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey); - - $oMutex->Lock(); - $oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey); - $bResult = $oOwnershipLock->Release($sToken); - self::DeleteExpiredLocks(); // Cleanup orphan locks - $oMutex->Unlock(); - - return $bResult; - } - - /** - * Kills the lock for the specified object - * - * @param string $sObjClass The class of the object - * @param int $iObjKey The identifier of the object - * @return boolean - */ - public static function KillLock($sObjClass, $iObjKey) - { - $oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey); - - $oMutex->Lock(); - $sOQL = "SELECT iTopOwnershipToken WHERE obj_class = :obj_class AND obj_key = :obj_key"; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, ['obj_class' => $sObjClass, 'obj_key' => $iObjKey])); - while ($oLock = $oSet->Fetch()) { - $oLock->DBDelete(); - } - $oMutex->Unlock(); - } - - /** - * Checks if an exclusive lock exists on the specified DBObject. - * @param string $sObjClass The class of the object for which to acquire the lock - * @param integer $iObjKey The identifier of the object for which to acquire the lock - * @return multitype:boolean iTopOwnershipLock Ambigous - */ - public static function IsLocked($sObjClass, $iObjKey) - { - $bLocked = false; - $oMutex = new iTopMutex('lock_'.$sObjClass.'::'.$iObjKey); - - $oMutex->Lock(); - $oOwnershipLock = new iTopOwnershipLock($sObjClass, $iObjKey); - if ($oOwnershipLock->IsOwned()) { - $bLocked = true; - } - $oMutex->Unlock(); - - return ['locked' => $bLocked, 'owner' => $oOwnershipLock->GetOwner()]; - } - - /** - * Get the current owner of the lock - * @return User - */ - public function GetOwner() - { - if ($this->IsTokenValid()) { - return MetaModel::GetObject('User', $this->oToken->Get('user_id'), false, true); - } - return null; - } - - /** - * The constructor is protected. Use the static methods AcquireLock / ExtendLock / ReleaseLock / KillLock - * which are protected against concurrent access by a Mutex. - * @param string $sObjClass The class of the object for which to create a lock - * @param integer $iObjKey The identifier of the object for which to create a lock - */ - protected function __construct($sObjClass, $iObjKey) - { - $sOQL = "SELECT iTopOwnershipToken WHERE obj_class = :obj_class AND obj_key = :obj_key"; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, ['obj_class' => $sObjClass, 'obj_key' => $iObjKey])); - $this->oToken = $oSet->Fetch(); - $this->sObjClass = $sObjClass; - $this->iObjKey = $iObjKey; - // IssueLog::Info("iTopOwnershipLock::__construct($sObjClass, $iObjKey) oToken::".($this->oToken ? $this->oToken->GetKey() : 'null')); - } - - protected function IsOwned() - { - return $this->IsTokenValid(); - } - - protected function Acquire($sToken = null) - { - if ($this->IsTokenValid()) { - // IssueLog::Info("Acquire($sToken) returns false"); - return false; - } else { - $sToken = $this->TakeOwnership($sToken); - // IssueLog::Info("Acquire($sToken) returns $sToken"); - return $sToken; - } - } - - /** - * Extends the ownership lock or acquires it if none exists - * Returns a hash array with 3 elements: - * 'status': either true or false, tells if the lock is still owned - * 'owner': is status is false, the User object currently owning the lock - * 'operation': whether the lock was 'renewed' (i.e. the lock was valid, its duration was extended) or 'expired' (there was no valid lock for this object) or 'lost' (someone else grabbed it) - * 'acquired': date at which the lock was initially acquired - * @param string $sToken - * @return multitype:boolean string User - */ - protected function Extend($sToken) - { - $aResult = ['status' => true, 'owner' => '', 'operation' => 'renewed']; - - if ($this->IsTokenValid()) { - if ($sToken === $this->oToken->Get('token')) { - $this->oToken->Set('last_seen', date(AttributeDateTime::GetSQLFormat())); - $this->oToken->DBUpdate(); - $aResult['acquired'] = $this->oToken->Get('acquired'); - } else { - // IssueLog::Info("Extend($sToken) returns false"); - $aResult['status'] = false; - $aResult['operation'] = 'lost'; - $aResult['owner'] = $this->GetOwner(); - $aResult['acquired'] = $this->oToken->Get('acquired'); - } - } else { - $aResult['status'] = false; - $aResult['operation'] = 'expired'; - } - // IssueLog::Info("Extend($sToken) returns true"); - return $aResult; - } - - protected function HasOwnership($sToken) - { - $bRet = false; - if ($this->IsTokenValid()) { - if ($sToken === $this->oToken->Get('token')) { - $bRet = true; - } - } - // IssueLog::Info("HasOwnership($sToken) return $bRet"); - return $bRet; - } - - protected function Release($sToken) - { - $bRet = false; - // IssueLog::Info("Release... begin [$sToken]"); - if (($this->oToken) && ($sToken === $this->oToken->Get('token'))) { - // IssueLog::Info("oToken::".$this->oToken->GetKey().' ('.$sToken.') to be deleted'); - $this->oToken->DBDelete(); - // IssueLog::Info("oToken deleted"); - $this->oToken = null; - $bRet = true; - } elseif ($this->oToken == null) { - // IssueLog::Info("Release FAILED oToken == null !!!"); - } else { - // IssueLog::Info("Release FAILED inconsistent tokens: sToken=\"".$sToken.'", oToken->Get(\'token\')="'.$this->oToken->Get('token').'"'); - } - // IssueLog::Info("Release... end"); - return $bRet; - } - - protected function IsTokenValid() - { - $bRet = false; - if ($this->oToken != null) { - $sToken = $this->oToken->Get('token'); - $sDate = $this->oToken->Get('last_seen'); - if (($sDate != '') && ($sToken != '')) { - $oLastSeenTime = new DateTime($sDate); - $iNow = date('U'); - if (($iNow - $oLastSeenTime->format('U')) < MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay')) { - $bRet = true; - } - } - } - return $bRet; - } - - protected function TakeOwnership($sToken = null) - { - if ($this->oToken == null) { - $this->oToken = new iTopOwnershipToken(); - $this->oToken->Set('obj_class', $this->sObjClass); - $this->oToken->Set('obj_key', $this->iObjKey); - } - $this->oToken->Set('acquired', date(AttributeDateTime::GetSQLFormat())); - $this->oToken->Set('user_id', UserRights::GetUserId()); - $this->oToken->Set('last_seen', date(AttributeDateTime::GetSQLFormat())); - if ($sToken === null) { - $sToken = sprintf('%X', microtime(true)); - } - $this->oToken->Set('token', $sToken); - $this->oToken->DBWrite(); - return $this->oToken->Get('token'); - } - - protected static function DeleteExpiredLocks() - { - $sOQL = "SELECT iTopOwnershipToken WHERE last_seen < :last_seen_limit"; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL, ['last_seen_limit' => date(AttributeDateTime::GetSQLFormat(), time() - MetaModel::GetConfig()->Get('concurrent_lock_expiration_delay'))])); - while ($oToken = $oSet->Fetch()) { - $oToken->DBDelete(); - } - - } -}