mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-24 02:58:43 +02:00
N°4789 - Parse datamodel module.xxx.php files instead of interpreting them (#746)
* N°4789 - Parse datamodel module.xxx.php files instead of interpreting them - refactoring all in a dedicated service first * N°4789 - fix broken setup + tests * N°4789 - replace legacy eval by module file parsing * N°4789 - handle constants and if conditional structures * N°4789 - compute boolean expressions * N°4789 - make autoselect and dependencies work as well * cleanup * N°4789 - fix BeforeWritingConfig calls during setup * N°4789 - refactor and split in ModuleDiscoveryEvaluationService + handle ModuleInstallerAPI methods calls during setup * N°4789 - PR review changes with Romain * PR review + code cleanup + added usecases and test cover * temp evaluation work * replace eval by iTop custom evaluation classes * move PhpParser/Evaluation classes in a specific namespave + composer dumpautoload * fix broken setup * fix broken setup * complete Evaluators list + autoload * cleanup useless testing resources * cleanup + replace last eval call in VariableEvaluator * fix few Evaluators code * enhance nikic evaluators + test with/without nikic lib * Evaluator fixes/enhancements + tests * bump to nikic fork temporarly * bump nikic-parser fork + use only nikic fork evaluation + cleanup itop redondant evaluators * review with Romain: use distinct whitelists in setup time/runtime + move ModuleFileParser internal logic into ModuleFileReader * PhpExpressionEvaluator used via constructor and not as a service * dumpautoload again after rebase
This commit is contained in:
@@ -19,6 +19,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
use Combodo\iTop\PhpParser\Evaluation\PhpExpressionEvaluator;
|
||||
|
||||
require_once(APPROOT.'setup/modulediscovery/ModuleFileReader.php');
|
||||
|
||||
class MissingDependencyException extends CoreException
|
||||
{
|
||||
/**
|
||||
@@ -91,6 +95,9 @@ class ModuleDiscovery
|
||||
|
||||
// ModulePath is used by AddModule to get the path of the module being included (in ListModuleFiles)
|
||||
protected static $m_sModulePath = null;
|
||||
|
||||
private static PhpExpressionEvaluator $oPhpExpressionEvaluator;
|
||||
|
||||
protected static function SetModulePath($sModulePath)
|
||||
{
|
||||
self::$m_sModulePath = $sModulePath;
|
||||
@@ -105,6 +112,9 @@ class ModuleDiscovery
|
||||
*/
|
||||
public static function AddModule($sFilePath, $sId, $aArgs)
|
||||
{
|
||||
if (is_null($aArgs)||! is_array($aArgs)){
|
||||
throw new ModuleFileReaderException("Error parsing module file args", 0, null, $sFilePath);
|
||||
}
|
||||
if (!array_key_exists('itop_version', $aArgs))
|
||||
{
|
||||
// Assume 1.0.2
|
||||
@@ -115,7 +125,7 @@ class ModuleDiscovery
|
||||
if (!array_key_exists($sArgName, $aArgs))
|
||||
{
|
||||
throw new Exception("Module '$sId': missing argument '$sArgName'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aArgs['root_dir'] = dirname($sFilePath);
|
||||
@@ -218,7 +228,7 @@ class ModuleDiscovery
|
||||
* @param array $aModulesToLoad List of modules to search for, defaults to all if omitted
|
||||
* @return array
|
||||
* @throws \MissingDependencyException
|
||||
*/
|
||||
*/
|
||||
public static function OrderModulesByDependencies($aModules, $bAbortOnMissingDependency = false, $aModulesToLoad = null)
|
||||
{
|
||||
// Order the modules to take into account their inter-dependencies
|
||||
@@ -303,6 +313,15 @@ class ModuleDiscovery
|
||||
return $aModules;
|
||||
}
|
||||
|
||||
private static function GetPhpExpressionEvaluator(): PhpExpressionEvaluator
|
||||
{
|
||||
if (!isset(static::$oPhpExpressionEvaluator)) {
|
||||
static::$oPhpExpressionEvaluator = new PhpExpressionEvaluator([], RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST);
|
||||
}
|
||||
|
||||
return static::$oPhpExpressionEvaluator;
|
||||
}
|
||||
|
||||
protected static function DependencyIsResolved($sDepString, $aOrderedModules, $aSelectedModules)
|
||||
{
|
||||
$bResult = false;
|
||||
@@ -349,19 +368,19 @@ class ModuleDiscovery
|
||||
if (version_compare($sCurrentVersion, $sExpectedVersion, $sOperator))
|
||||
{
|
||||
$aReplacements[$sModuleId] = '(true)'; // Add parentheses to protect against invalid condition causing
|
||||
// a function call that results in a runtime fatal error
|
||||
// a function call that results in a runtime fatal error
|
||||
}
|
||||
else
|
||||
{
|
||||
$aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing
|
||||
// a function call that results in a runtime fatal error
|
||||
// a function call that results in a runtime fatal error
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// module is not present
|
||||
$aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing
|
||||
// a function call that results in a runtime fatal error
|
||||
// a function call that results in a runtime fatal error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -385,10 +404,10 @@ class ModuleDiscovery
|
||||
else
|
||||
{
|
||||
$sBooleanExpr = str_replace(array_keys($aReplacements), array_values($aReplacements), $sDepString);
|
||||
$bOk = @eval('$bResult = '.$sBooleanExpr.'; return true;');
|
||||
if ($bOk == false)
|
||||
{
|
||||
SetupLog::Warning("Eval of '$sBooleanExpr' returned false");
|
||||
try{
|
||||
$bResult = self::GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($sBooleanExpr);
|
||||
} catch(ModuleFileReaderException $e){
|
||||
//logged already
|
||||
echo "Failed to parse the boolean Expression = '$sBooleanExpr'<br/>";
|
||||
}
|
||||
}
|
||||
@@ -496,42 +515,12 @@ class ModuleDiscovery
|
||||
else if (preg_match('/^module\.(.*).php$/i', $sFile, $aMatches))
|
||||
{
|
||||
self::SetModulePath($sRelDir);
|
||||
try
|
||||
{
|
||||
$sModuleFileContents = file_get_contents($sDirectory.'/'.$sFile);
|
||||
$sModuleFileContents = str_replace(array('<?php', '?>'), '', $sModuleFileContents);
|
||||
$sModuleFileContents = str_replace('__FILE__', "'".addslashes($sDirectory.'/'.$sFile)."'", $sModuleFileContents);
|
||||
preg_match_all('/class ([A-Za-z0-9_]+) extends ([A-Za-z0-9_]+)/', $sModuleFileContents, $aMatches);
|
||||
//print_r($aMatches);
|
||||
$idx = 0;
|
||||
foreach($aMatches[1] as $sClassName)
|
||||
{
|
||||
if (class_exists($sClassName))
|
||||
{
|
||||
// rename the class inside the code to prevent a "duplicate class" declaration
|
||||
// and change its parent class as well so that nobody will find it and try to execute it
|
||||
$sModuleFileContents = str_replace($sClassName.' extends '.$aMatches[2][$idx], $sClassName.'_'.($iDummyClassIndex++).' extends DummyHandler', $sModuleFileContents);
|
||||
}
|
||||
$idx++;
|
||||
}
|
||||
$bRet = eval($sModuleFileContents);
|
||||
|
||||
if ($bRet === false)
|
||||
{
|
||||
SetupLog::Warning("Eval of $sRelDir/$sFile returned false");
|
||||
}
|
||||
|
||||
//echo "<p>Done.</p>\n";
|
||||
}
|
||||
catch(ParseError $e)
|
||||
{
|
||||
// PHP 7
|
||||
SetupLog::Warning("Eval of $sRelDir/$sFile caused an exception: ".$e->getMessage()." at line ".$e->getLine());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// Continue...
|
||||
SetupLog::Warning("Eval of $sRelDir/$sFile caused an exception: ".$e->getMessage());
|
||||
$sModuleFilePath = $sDirectory.'/'.$sFile;
|
||||
try {
|
||||
$aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sDirectory.'/'.$sFile);
|
||||
SetupWebPage::AddModule($sModuleFilePath, $aModuleInfo[1], $aModuleInfo[2]);
|
||||
} catch(ModuleFileReaderException $e){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user