cleanup after rebasing

This commit is contained in:
odain
2025-09-19 15:10:41 +02:00
parent 996b98c4c7
commit 8c30199e9f
5 changed files with 861 additions and 1 deletions

View File

@@ -0,0 +1,503 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
* This file is part of iTop.
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Setup\ModuleDependency\Validation;
require_once(APPROOT.'setup/modulediscovery.class.inc.php');
require_once(APPROOT.'setup/moduledependency/validation/xmlmodulemetainfo.php');
require_once(APPROOT.'setup/moduledependency/validation/xmlmodule.php');
class ModulesDependencyValidationService {
private static ?ModulesDependencyValidationService $oInstance;
public static array $aModulesDataByModuleName=[];
public array $aModules=[];
private string $sCurrentModule;
private array $aDefineNodes;
private array $aDependencyNodes;
private array $aAllDmFiles=[];
private array $aSoftDependencyNodes=[];
private array $aSubfoldersByModulename=[];
public static function GetInstance(): ModulesDependencyValidationService
{
if (!isset(self::$oInstance)) {
self::$oInstance = new ModulesDependencyValidationService();
self::ReadModuleMetaInfo();
}
return self::$oInstance;
}
public static function SetInstance(?ModulesDependencyValidationService $instance): void {
self::$oInstance = $instance;
}
protected static function ListModuleFiles(string $sDirectory) : array
{
$aModules=[];
if ($hDir = opendir($sDirectory))
{
// This is the correct way to loop over the directory. (according to the documentation)
while (($sFile = readdir($hDir)) !== false)
{
$aMatches = [];
if (is_dir($sDirectory.'/'.$sFile))
{
if (($sFile != '.') && ($sFile != '..') && ($sFile != '.svn') && ($sFile != 'vendor'))
{
$aModules = array_merge($aModules, self::ListModuleFiles($sDirectory.'/'.$sFile));
}
}
else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
{
$aModules[] = $sDirectory.'/'.$sFile;
}
}
closedir($hDir);
}
return $aModules;
}
public static function ReadModuleMetaInfo(): void
{
if (count(self::$aModulesDataByModuleName)>0){
return;
}
$aDirsToScan = [
APPROOT.'datamodels/2.x',
APPROOT.'extensions',
APPROOT.'data/production-modules',
];
foreach ($aDirsToScan as $sDir){
foreach (self::ListModuleFiles($sDir) as $sFile) {
try{
$aModuleData = \ModuleFileReader::GetInstance()->ReadModuleFileInformation($sFile);
} catch(\ModuleFileReaderException $e){
\IssueLog::Error("Cannot read module file", null, [$sFile]);
}
$sModuleId=$aModuleData[1];
list($sModuleName, $sVersion) = \ModuleDiscovery::GetModuleName($sModuleId);
self::$aModulesDataByModuleName[$sModuleName] = $aModuleData;
}
ksort(self::$aModulesDataByModuleName);
}
}
public function GetModuleMetainfo($sModuleName)
{
return self::$aModulesDataByModuleName[$sModuleName] ?? [];
}
public function ListDatamodelFiles() : array
{
if (count($this->aAllDmFiles)==0){
$aGlobPAtterns = [
APPROOT.'datamodels/2.x/*',
APPROOT.'application',
APPROOT.'core',
APPROOT.'extensions',
APPROOT.'extensions/*',
APPROOT.'data/production-modules',
APPROOT.'data/production-modules/*',
];
foreach ($aGlobPAtterns as $sPattern) {
$this->aAllDmFiles = array_merge($this->aAllDmFiles, glob("$sPattern/datamodel.*.xml"));
}
}
return $this->aAllDmFiles;
}
private function CreateIfNeededAndGetXmlModule(string $sModuleName) : XmlModule {
/** @var XmlModule $oCurrentXmlModule */
$oCurrentXmlModule = $this->aModules[$sModuleName] ?? null;
if (is_null($oCurrentXmlModule)){
$oCurrentXmlModule = new XmlModule($sModuleName);
$this->aModules[$sModuleName] = $oCurrentXmlModule;
}
return $oCurrentXmlModule;
}
public function FetchAllDependenciesViaDM()
{
foreach ($this->ListDatamodelFiles() as $sFile) {
$this->FetchXmlMetaInfo($sFile);
}
foreach ($this->aDefineNodes as $sKey => $aModules){
foreach ($aModules as $sModuleName){
$this->CreateIfNeededAndGetXmlModule($sModuleName);
}
$aSoftlyDependentModules = $this->aSoftDependencyNodes[$sKey] ?? null;
if (is_null($aSoftlyDependentModules)){
continue;
}
foreach ($aSoftlyDependentModules as $sModuleName){
$oCurrentXmlModule = $this->CreateIfNeededAndGetXmlModule($sModuleName);
$oCurrentXmlModule->AddDependency($sKey, $aModules, $this->aModules);
}
}
foreach ($this->aDependencyNodes as $sKey => $aModules){
foreach ($aModules as $sModuleName){
$aDefiningModules = $this->aDefineNodes[$sKey] ?? null;
if (is_null($aDefiningModules)){
continue;
}
$oCurrentXmlModule = $this->CreateIfNeededAndGetXmlModule($sModuleName);
$oCurrentXmlModule->AddDependency($sKey, $aDefiningModules, $this->aModules);
}
}
$this->OrderModules();
$this->CompleteModuleDependencies();
}
public function FetchAllDependenciesViaModulesFiles()
{
$aFullnameClassesByModuleName=[];
foreach (self::$aModulesDataByModuleName as $sModuleName => $aModuleData){
//echo "$sModuleName\n";
$aFiles = $aModuleData[2]['datamodel'] ?? [];
$sDir = dirname($aModuleData[0]);
$aDeps=[];
foreach ($aFiles as $sFile){
if (preg_match("|.*model\.$sModuleName\.php|", $sFile)){
continue;
}
//echo "$sDir/$sFile\n";
$aDeps=array_merge($aDeps, $this->ListDeclaredFullnameClassesFromPhpFile("$sDir/$sFile"));
}
$aFullnameClassesByModuleName[$sModuleName]=$aDeps;
}
foreach ($aFullnameClassesByModuleName as $sModuleName => $aFullnameClasses){
foreach (self::$aModulesDataByModuleName as $sModuleName2 => $aModuleData){
if ($sModuleName2 === $sModuleName){
continue;
}
$sDir = dirname($aModuleData[0]);
if (count($aFullnameClassesByModuleName)==0){
continue;
}
$sStr = "";
foreach ($aFullnameClasses as $sClass){
$sStr .= <<<TXT
-e "$sClass"
TXT;
}
if (strlen($sStr)==0){
continue;
}
$bFound=false;
foreach ($this->GetFolders($sModuleName2, $sDir) as $sFolderDir => $sGrepRecursiveCliOption) {
$sCliCmd = str_replace('\\', '\\\\\\\\', sprintf("grep -l%s %s $sFolderDir", $sGrepRecursiveCliOption, $sStr));
$sOutput = exec($sCliCmd);
if (strlen($sOutput) != 0) {
$bFound=true;
$sCliCmd = str_replace('\\', '\\\\\\\\', sprintf("grep -o%s %s $sFolderDir", $sGrepRecursiveCliOption, $sStr));
$sOutput = exec($sCliCmd);
//echo "|$sOutput|\n";
break;
}
}
if ($bFound){
$oCurrentXmlModule = $this->CreateIfNeededAndGetXmlModule($sModuleName);
$oCurrentXmlModule2 = $this->CreateIfNeededAndGetXmlModule($sModuleName2);
try{
$sKey = $this->GetFirstFoundDepsUID($sOutput);
} catch(\Exception $e){
var_dump($aFullnameClassesByModuleName);
var_dump(
[ $sStr, $sCliCmd, $sModuleName, $sModuleName2, $sOutput]
);
throw $e;
}
$oCurrentXmlModule2->AddDependency($sKey, [$sModuleName], $this->aModules);
//echo "$sModuleName2 => $sModuleName\n";
}
}
}
}
public function GetFirstFoundDepsUID(string $sOutput) : string {
if (preg_match_all('|.*:(.*)|m', $sOutput, $aMatches)){
//var_dump($aMatches);
return $aMatches[1][0];
}
throw new \Exception('no match: ' . $sOutput);
}
private function GetFolders($sModuleName2, $sDir) : array
{
if (array_key_exists($sModuleName2, $this->aSubfoldersByModulename)){
return $this->aSubfoldersByModulename[$sModuleName2];
}
$aRes=[];
$aFiles=[];
foreach (glob("$sDir/*") as $sPath){
if (! is_dir($sPath)){
$aFiles[]=$sPath;
continue;
}
if (strpos($sPath, '\.git') !== false){
continue;
}
if (strpos($sPath, 'vendor') !== false){
continue;
}
if (strpos($sPath, 'test') !== false){
continue;
}
$aRes[$sPath]="r";
}
$aRes=[ implode(' ', $aFiles) => "" ];
$this->aSubfoldersByModulename[$sModuleName2]=$aRes;
return $aRes;
}
private function GetModuleSuffix($sFile) : string
{
if (! preg_match('|.*datamodel\.([^\.]+)\.xml|', $sFile, $aMatches)){
throw new \Exception("Regexp issue: $sFile");
}
return $aMatches[1];
}
public function FetchXmlMetaInfo($sFile) : void {
$oDomDoc = new \DOMDocument('1.0', 'UTF-8');
libxml_clear_errors();
$oDomDoc->loadXml(file_get_contents($sFile));
$aErrors = libxml_get_errors();
if (count($aErrors) > 0)
{
throw new \Exception("Malformed XML");
}
$this->sCurrentModule = $this->GetModuleSuffix($sFile);
if (! isset($this->aDefineNodes)){
$this->aDefineNodes=[];
}
if (! isset($this->aDependencyNodes)){
$this->aDependencyNodes=[];
}
foreach ($oDomDoc->childNodes as $oDomNode){
$this->FetchMetaInfo($oDomDoc->childNodes);
}
}
private function FetchMetaInfo(\DOMNodeList $oDomNodeList, ?string $sPath=null)
{
/** @var \DOMNode $oDomNode */
foreach ($oDomNodeList as $oDomNode) {
/** @var \DOMAttr $oDelta */
$oDelta = $oDomNode->attributes['_delta'] ?? null;
/** @var \DOMAttr $oId */
$oId = $oDomNode->attributes['id'] ?? null;
if (! is_null($oId)) {
$sId = $oId->nodeValue;
$sCurrentPath = $sPath ? $sPath."->".$sId : $sId;
if (!is_null($oDelta)) {
$oXmlModuleMetaInfo = new XmlModuleMetaInfo($sId, $oDomNode->nodeName, $sCurrentPath, $oDelta->nodeValue);
$sKey = $oXmlModuleMetaInfo->GetUID();
if ($oXmlModuleMetaInfo->IsDefine()) {
if (array_key_exists($sKey, $this->aDefineNodes)) {
$this->aDefineNodes[$sKey][] = $this->sCurrentModule;
} else {
$this->aDefineNodes[$sKey] = [$this->sCurrentModule];
}
} else {
if (array_key_exists($sKey, $this->aDependencyNodes)) {
$this->aDependencyNodes[$sKey][] = $this->sCurrentModule;
} else {
$this->aDependencyNodes[$sKey] = [$this->sCurrentModule];
}
}
} else {
$oXmlModuleMetaInfo = new XmlModuleMetaInfo($sId, $oDomNode->nodeName, $sCurrentPath, "nodelta");
$sKey = $oXmlModuleMetaInfo->GetUID();
if (array_key_exists($sKey, $this->aSoftDependencyNodes)) {
$this->aSoftDependencyNodes[$sKey][] = $this->sCurrentModule;
} else {
$this->aSoftDependencyNodes[$sKey] = [$this->sCurrentModule];
}
}
} else if ($oDomNode instanceof \DOMElement){
$sCurrentPath = $sPath ? $sPath . '->' . $oDomNode->nodeName : $oDomNode->nodeName;
} else{
$sCurrentPath = $sPath;
}
$this->FetchMetaInfo($oDomNode->childNodes, $sCurrentPath);
}
}
private function OrderModules()
{
$aModuleDepsCount = [];
/** @var XmlModule $oXmlModule */
foreach ($this->aModules as $oXmlModule) {
$aModuleDepsCount[$oXmlModule->sModuleName] = count($oXmlModule->GetExpandedModuleNames());
}
$aOrderModules=[];
while (count($aModuleDepsCount)>0) {
asort($aModuleDepsCount);
foreach ($aModuleDepsCount as $sModuleName => $iCount){
//echo "=== OK === $sModuleName\n";
if ($iCount>0){
/*foreach ($aModuleDepsCount as $sStillToProcessModuleName => $c) {
/** @var XmlModule $oXmlStillToProcessModule */
/*$oXmlStillToProcessModule = $this->aModules[$sStillToProcessModuleName];
echo "=== NOK ($c)=== $sStillToProcessModuleName: ".$oXmlStillToProcessModule."\n";
}*/
throw new \Exception("still deps with $sModuleName: ");
}
unset($aModuleDepsCount[$sModuleName]);
$aOrderModules[$sModuleName] = $this->aModules[$sModuleName];
break;
}
//echo "$sModuleName\n";
foreach ($aModuleDepsCount as $sStillToProcessModuleName => $c){
/** @var XmlModule $oXmlStillToProcessModule */
$oXmlStillToProcessModule = $this->aModules[$sStillToProcessModuleName];
if (in_array($sModuleName, $oXmlStillToProcessModule->GetExpandedModuleNames())){
$aModuleDepsCount[$sStillToProcessModuleName] = $c - 1 ;
}
}
}
$this->aModules = $aOrderModules;
}
private function CompleteModuleDependencies()
{
/** @var XmlModule $oXmlModule */
foreach ($this->aModules as $oXmlModule) {
$oXmlModule->CompleteModuleDependencies($this->aModules);
}
}
/**
* Read declared classes/interfaces in modules.php file (either directly listed files or inside autoload)
* @param string : module file path
*
* @return array: list of fullname classes
*/
public function ListDeclaredFullnameClassesFromPhpFile(string $sPath) : array
{
if (false !== strpos($sPath, 'autoload.php')){
return $this->ListDeclaredFullnameClassesFromAutoloadFile($sPath);
}
$aRes=[];
$sContent = file_get_contents($sPath);
$sNamespace='';
if (preg_match('|namespace (.*)[ ]*;|', $sContent, $aMatches)){
$sNamespace=trim($aMatches[1]) . '\\';
}
if (preg_match_all('|^class ([a-zA-Z]*) |m', $sContent, $aMatches)){
foreach($aMatches[1] as $sClass){
$aRes[]=$sNamespace.$sClass;
}
}
if (preg_match_all('|^interface ([a-zA-Z]*)|m', $sContent, $aMatches)){
foreach($aMatches[1] as $sInterface){
$aRes[]=$sNamespace.$sInterface;
}
}
return $aRes;
}
/**
* Read declared classes/interfaces autoload file
*
* @param string : module file path
*
* @return array: list of fullname classes
*/
public function ListDeclaredFullnameClassesFromAutoloadFile(string $sPath) : array
{
$sAutoloadClassMap = dirname($sPath) . "/composer/autoload_classmap.php";
if (!is_file($sAutoloadClassMap)) {
return [];
}
$sTempfile = tempnam(sys_get_temp_dir(), 'autoload_');
$sContent = file_get_contents($sAutoloadClassMap);
$sReplace=<<<TXT
\$vendorDir = 'VENDOR';
\$baseDir = 'BASEDIR';
\$aModuleFiles=
TXT;
$sContent = preg_replace('|return|', $sReplace, $sContent);
file_put_contents($sTempfile, $sContent);
require_once $sTempfile;
@unlink($sTempfile);
$aRes=[];
foreach ($aModuleFiles as $sClass => $sClassPath){
if (strpos($sClass, 'InstalledVersions')){
continue;
}
if (false !== strpos($sClassPath, 'VENDOR')){
continue;
}
$aRes[]=$sClass;
}
return $aRes;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace Combodo\iTop\Setup\ModuleDependency\Validation;
class XmlModule {
public string $sModuleName;
public array $aDependencyModulesNames=[];
public array $aExpandedDependencyModulesNames=[];
public array $aAllDependencyModulesNames=[];
public array $aXMlMetaInfosByModuleNames=[];
public function __construct(string $sModuleName)
{
$this->sModuleName = $sModuleName;
}
public function AddDependency(string $sXmlMetaInfoUID, array $aDefiningModuleNames, array $aModules)
{
$aRemainingModules=[];
foreach ($aDefiningModuleNames as $sDefiningModuleName) {
if ($sDefiningModuleName === $this->sModuleName) {
continue;
}
if ($sDefiningModuleName === "core" || $sDefiningModuleName === "application") {
continue;
}
$aRemainingModules[]=$sDefiningModuleName;
}
if (count($aRemainingModules)==0){
return;
}
/*$aLog = ['itop-bridge-datacenter-mgmt-services', 'itop-datacenter-mgmt'];
if (in_array($sDefiningModuleName, $aLog) && in_array($this->sModuleName, $aLog)){
echo $this->sModuleName . " => $sDefiningModuleName === " . $sXmlMetaInfoUID . "\n";
}*/
$sKey=implode(' || ', $aRemainingModules);
if (! array_key_exists($sKey, $this->aXMlMetaInfosByModuleNames)){
$this->aXMlMetaInfosByModuleNames[$sKey]=[$sXmlMetaInfoUID];
} else {
if (! in_array($sXmlMetaInfoUID, $this->aXMlMetaInfosByModuleNames[$sKey])){
$this->aXMlMetaInfosByModuleNames[$sKey][]=$sXmlMetaInfoUID;
}
}
if (! array_key_exists($sKey, $this->aDependencyModulesNames)){
$aCurrentModules=[];
foreach ($aRemainingModules as $sDefiningModuleName) {
/** @var XmlModule $oXmlModule */
$oXmlModule = $aModules[$sDefiningModuleName];
$aCurrentModules[]=$oXmlModule;
}
$this->aDependencyModulesNames[$sKey]=$aCurrentModules;
}
}
public function Depends(string $sModuleName) : bool
{
return array_key_exists($sModuleName, $this->aDependencyModulesNames) || array_key_exists($sModuleName, $this->aAllDependencyModulesNames);
}
public function __toString(): string
{
return sprintf("%s (%s)", $this->sModuleName, implode(' & ', array_keys($this->aDependencyModulesNames)));
}
public function CompleteModuleDependencies(array $aAllModules) : void
{
foreach ($this->aDependencyModulesNames as $sDirectDependency => $oXmlModules){
/** @var \Combodo\iTop\Test\UnitTest\XmlModule $oDirectDepXmlModule */
$oDirectDepXmlModule = $aAllModules[$sDirectDependency] ?? null;
if (! is_null($oDirectDepXmlModule)) {
foreach ($oDirectDepXmlModule->aDependencyModulesNames as $sDirectDependency2 => $oXmlModules2) {
if (!array_key_exists($sDirectDependency2, $this->aDependencyModulesNames) && !in_array($sDirectDependency2, $this->aAllDependencyModulesNames)) {
$this->aAllDependencyModulesNames[] = $sDirectDependency2;
}
}
}
}
}
public function GetExpandedModuleNames() : array {
if (count($this->aExpandedDependencyModulesNames) ==0){
return $this->aExpandedDependencyModulesNames;
}
$aRes= [];
foreach ($this->aDependencyModulesNames as $sDependency){
$oiTopCoreModuleDependency = new iTopCoreModuleDependency($sDependency);
$aRes = array_merge($aRes, $oiTopCoreModuleDependency->GetPotentialPrerequisiteModuleNames());
}
$this->aExpandedDependencyModulesNames = array_unique($aRes);
return $this->aExpandedDependencyModulesNames;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Combodo\iTop\Setup\ModuleDependency\Validation;
class XmlModuleMetaInfo {
public string $sLastNodeId;
public string $sNodeName;
public string $sPath;
public string $sDelta;
public function __construct(string $sLastNodeId, string $sNodeName, string $sPath, string $sDelta)
{
$this->sLastNodeId = $sLastNodeId;
$this->sNodeName = $sNodeName;
$this->sPath = $sPath;
$this->sDelta = $sDelta;
}
public function IsDefine() : bool {
return
$this->sDelta === 'define_if_not_exists'
|| $this->sDelta === 'define';
}
public function GetUID() : string
{
return $this->sNodeName . '_' . $this->sPath;
}
}

View File

@@ -396,7 +396,6 @@ class ModuleDiscovery
*/
protected static function ListModuleFiles($sRelDir, $sRootDir)
{
static $iDummyClassIndex = 0;
$sDirectory = $sRootDir.'/'.$sRelDir;
if ($hDir = opendir($sDirectory))

View File

@@ -0,0 +1,229 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
* This file is part of iTop.
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Test\UnitTest\ModuleDependency\Validation;
use Combodo\iTop\Setup\ModuleDependency\ModuleDependency;
use Combodo\iTop\Setup\ModuleDependency\Validation\ModulesDependencyValidationService;
use Combodo\iTop\Setup\ModuleDependency\Validation\XmlModule;
use Combodo\iTop\Test\UnitTest\ItopTestCase;
class ModulesDependencyValidationServiceTest extends ItopTestCase {
private ModulesDependencyValidationService $oModulesDependencyValidationService;
private array $aFilesToRemove = [];
protected function setUp(): void
{
parent::setUp();
$this->RequireOnceItopFile('setup/moduledependency/validation/modulesdependencyvalidationservice.php');
if (substr(PHP_OS, 0, 3) == 'WIN') {
$this->markTestSkipped("run under Linux only");
}
}
protected function tearDown(): void
{
parent::tearDown();
foreach ($this->aFilesToRemove as $sTmpFile){
@unlink($sTmpFile);
}
ModulesDependencyValidationService::SetInstance(null);
}
/**
* Module dependency validation: make sure dependencies are correct toward classes/interfaces coming from PHP/Xml datamodel files
*/
public function testReadModuleFileData()
{
ModulesDependencyValidationService::GetInstance()->FetchAllDependenciesViaModulesFiles();
ModulesDependencyValidationService::GetInstance()->FetchAllDependenciesViaDM();
$aErrors=[];
/** @var XmlModule $oXmlModule */
foreach (ModulesDependencyValidationService::GetInstance()->aModules as $sModuleName => $oXmlModule) {
$aCurrentDeps = ModulesDependencyValidationService::GetInstance()::$aModulesDataByModuleName[$sModuleName][2]['dependencies'] ?? [];
$aModuleErrors=[];
foreach ($oXmlModule->aDependencyModulesNames as $sDepModuleName => $oXmlModule2){
$sXmlUIDs = implode('|', $oXmlModule->aXMlMetaInfosByModuleNames[$sDepModuleName]);
$bResolved=false;
foreach ($aCurrentDeps as $sDepString){
$oModuleDependency = new ModuleDependency($sDepString);
if (in_array($sDepModuleName, $oModuleDependency->GetPotentialPrerequisiteModuleNames())) {
$bResolved=true;
break;
}
if (false !== strpos($sDepModuleName, '|')){
$aDepModules = explode('|', $sDepModuleName);
foreach ($aDepModules as $sDepModule){
$sDepModule = trim($sDepModule);
if (in_array($sDepModule, $oModuleDependency->GetPotentialPrerequisiteModuleNames())) {
$bResolved=true;
break;
}
}
if ($bResolved){
break;
}
}
foreach ($oModuleDependency->GetPotentialPrerequisiteModuleNames() as $sPotentialDepModuleName){
/** @var XmlModule $oXmlModule2 */
$oXmlModule2 = ModulesDependencyValidationService::GetInstance()->aModules[$sPotentialDepModuleName]??null;
if (! is_null($oXmlModule2) && $oXmlModule2->Depends($sDepModuleName)){
$bResolved=true;
break;
}
}
if ($bResolved) {
break;
}
}
if (! $bResolved){
$aModuleErrors []= "$sModuleName depends on $sDepModuleName but missing in module dependencies: " . implode(' & ', $aCurrentDeps) . ". ($sXmlUIDs)";
}
}
if (count($aModuleErrors)){
$aErrors[$sModuleName]=$aModuleErrors;
}
}
$this->assertEquals(0, count($aErrors), var_export($aErrors, true));
}
public function testListDeclaredFullnameClassesFromPhpFile()
{
$aExpected = [
'CMDBChangeOp',
'CMDBChangeOpCreate',
'CMDBChangeOpDelete',
'CMDBChangeOpSetAttribute',
'CMDBChangeOpSetAttributeScalar',
'CMDBChangeOpSetAttributeTagSet',
'CMDBChangeOpSetAttributeURL',
'CMDBChangeOpSetAttributeBlob',
'CMDBChangeOpSetAttributeOneWayPassword',
'CMDBChangeOpSetAttributeEncrypted',
'CMDBChangeOpSetAttributeText',
'CMDBChangeOpSetAttributeLongText',
'CMDBChangeOpSetAttributeHTML',
'CMDBChangeOpSetAttributeCaseLog',
'CMDBChangeOpPlugin',
'CMDBChangeOpSetAttributeLinksAddRemove',
'CMDBChangeOpSetAttributeLinksTune',
'CMDBChangeOpSetAttributeCustomFields',
'iCMDBChangeOp',
];
$this->assertEquals($aExpected, ModulesDependencyValidationService::GetInstance()->ListDeclaredFullnameClassesFromPhpFile(APPROOT . 'core/cmdbchangeop.class.inc.php'));
}
public function testListDeclaredFullnameClassesFromAutoloadFile()
{
$aExpected = [
'Combodo\iTop\OAuthClient\Controller\AjaxOauthClientController',
'Combodo\iTop\OAuthClient\Controller\OAuthClientController',
'Combodo\iTop\OAuthClient\Service\ApplicationUIExtension',
'Combodo\iTop\OAuthClient\Service\PopupMenuExtension',
];
$this->assertEquals($aExpected, ModulesDependencyValidationService::GetInstance()->ListDeclaredFullnameClassesFromPhpFile(APPROOT . 'datamodels/2.x/itop-oauth-client/vendor/autoload.php'));
}
public function testListDeclaredFullnameClassesFromAutoloadFile_itopfence()
{
$sContent=<<<PHP
<?php
// autoload_classmap.php @generated by Composer
\$vendorDir = dirname(__DIR__);
\$baseDir = dirname(\$vendorDir);
return array(
'Attribute' => \$vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Combodo\\iTop\\Fence\\Checker\\IpRangeChecker' => \$baseDir . '/src/Checker/IpRangeChecker.php',
'Combodo\\iTop\\Fence\\Checker\\LoginFailedListener' => \$baseDir . '/src/Checker/LoginFailedListener.php',
);
PHP;
$sDir = sys_get_temp_dir().'/'.uniqid();
mkdir($sDir);
$sComposerDir = $sDir.'/composer';
mkdir($sComposerDir);
$sPath = "$sComposerDir/autoload_classmap.php";
file_put_contents($sPath, $sContent);
$aExpected = [
'Combodo\iTop\\Fence\Checker\IpRangeChecker',
'Combodo\iTop\\Fence\Checker\LoginFailedListener',
];
$aRes = ModulesDependencyValidationService::GetInstance()->ListDeclaredFullnameClassesFromAutoloadFile("$sDir/autoload.php");
@unlink($sPath);
rmdir($sComposerDir);
rmdir($sDir);
$this->assertEquals($aExpected, $aRes);
}
public function testReadModuleMetaInfo()
{
$aExpected = [
APPROOT . 'datamodels/2.x/itop-portal-base/module.itop-portal-base.php',
'itop-portal-base/3.3.0',
[
'label' => 'Portal Development Library',
'category' => 'Portal',
'dependencies' => [ 'itop-attachments/3.2.1' ],
'mandatory' => true,
'visible' => false,
'datamodel' => [ 'portal/vendor/autoload.php' ],
'webservice' => [],
'dictionary' => [],
'data.struct' => [],
'data.sample' => [],
'doc.manual_setup' => '',
'doc.more_information' => '',
'settings' => [],
'module_file_path' => '/var/www/html/iTopMFA/datamodels/2.x/itop-portal-base/module.itop-portal-base.php'
],
];
$this->assertEquals($aExpected, ModulesDependencyValidationService::GetInstance()->GetModuleMetainfo('itop-portal-base'));
}
public function testGetFirstFoundDepsUID() {
$sOutput=<<<TXT
/var/www/html/Professional-3.2.1-16428/web/datamodels/2.x/authent-token/src/Hook/MyAccountSectionTabContentExtension.php:Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension
TXT;
$this->assertEquals('Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension', ModulesDependencyValidationService::GetInstance()->GetFirstFoundDepsUID($sOutput));
$sOutput=<<<TXT
/var/www/html/Professional-3.2.1-16428/web/datamodels/2.x/authent-token/src/Hook/MyAccountSectionTabContentExtension.php:Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension
/var/www/html/Professional-3.2.1-16428/web/datamodels/2.x/authent-token/src/Hook/MyAccountSectionTabContentExtension2.php:Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension2
/var/www/html/Professional-3.2.1-16428/web/datamodels/2.x/authent-token/src/Hook/MyAccountSectionTabContentExtension3.php:Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension3
TXT;
$this->assertEquals('Combodo\iTop\MyAccount\Hook\iMyAccountTabContentExtension', ModulesDependencyValidationService::GetInstance()->GetFirstFoundDepsUID($sOutput));
}
}