diff --git a/setup/modulediscovery/ModuleFileParser.php b/setup/modulediscovery/ModuleFileParser.php index 7432a621b..65bf73238 100644 --- a/setup/modulediscovery/ModuleFileParser.php +++ b/setup/modulediscovery/ModuleFileParser.php @@ -1,8 +1,11 @@ value), 0, null, $sModuleFilePath); } - $sModuleId = $oModuleId->value->value; + $sModuleId = $this->EvaluateExpression($oModuleId->value); $oModuleConfigInfo = $aArgs[2]; if (false === ($oModuleConfigInfo instanceof PhpParser\Node\Arg)) { @@ -84,8 +87,7 @@ class ModuleFileParser { throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath); } - $aModuleConfig=[]; - $this->FillModuleInformationFromArray($oModuleConfigInfo->value, $aModuleConfig); + $aModuleConfig = $this->EvaluateExpression($oModuleConfigInfo->value); if (! is_array($aModuleConfig)){ throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath); @@ -98,46 +100,6 @@ class ModuleFileParser { ]; } - public function FillModuleInformationFromArray(PhpParser\Node\Expr\Array_ $oArray, array &$aModuleInformation) : void - { - $iIndex=0; - - /** @var \PhpParser\Node\Expr\ArrayItem $oValue */ - foreach ($oArray->items as $oArrayItem){ - if ($oArrayItem->key instanceof PhpParser\Node\Scalar\String_) { - //dictionnary - $sKey = $oArrayItem->key->value; - } else if ($oArrayItem->key instanceof \PhpParser\Node\Expr\ConstFetch) { - //dictionnary - $sKey = $this->EvaluateConstantExpression($oArrayItem->key); - if (is_null($sKey)){ - continue; - } - } else { - //array - $sKey = $iIndex++; - } - - $oValue = $oArrayItem->value; - if ($oValue instanceof PhpParser\Node\Expr\Array_) { - $aSubConfig=[]; - $this->FillModuleInformationFromArray($oValue, $aSubConfig); - $aModuleInformation[$sKey]=$aSubConfig; - continue; - } - - try { - $oEvaluatuedValue = $this->EvaluateExpression($oValue); - } catch(ModuleFileReaderException $e){ - //required to support legacy below dump dependency - //'dependencies' => ['itop-config-mgmt/2.0.0'||'itop-structure/3.0.0'] - continue; - } - - $aModuleInformation[$sKey]=$oEvaluatuedValue; - } - } - /** * @param string $sModuleFilePath * @param \PhpParser\Node\Stmt\If_ $oNode @@ -192,66 +154,6 @@ class ModuleFileParser { return null; } - public function EvaluateConstantExpression(\PhpParser\Node\Expr\ArrayItem|\PhpParser\Node\Expr\ConstFetch $oValue) : mixed - { - return $this->UnprotectedComputeBooleanExpression($oValue->name); - } - - public function EvaluateClassConstantExpression(\PhpParser\Node\Expr\ClassConstFetch $oValue) : mixed - { - $sClassName = $oValue->class->name; - $sProperty = $oValue->name->name; - if (class_exists($sClassName)){ - $class = new \ReflectionClass($sClassName); - if (array_key_exists($sProperty, $class->getConstants())) { - $oReflectionConstant = $class->getReflectionConstant($sProperty); - if ($oReflectionConstant->isPublic()){ - return $class->getConstant($sProperty); - } - } - } - - if ('class' === $sProperty){ - return $sClassName; - } - - return null; - } - - public function EvaluateStaticPropertyExpression(\PhpParser\Node\Expr\StaticPropertyFetch $oValue) : mixed - { - $sClassName = $oValue->class->name; - $sProperty = $oValue->name->name; - if (class_exists($sClassName)){ - $class = new \ReflectionClass($sClassName); - if (array_key_exists($sProperty, $class->getStaticProperties())) { - $oReflectionProperty = $class->getProperty($sProperty); - if ($oReflectionProperty->isPublic()){ - return $class->getStaticPropertyValue($sProperty); - } - } - } - - return null; - } - - /** - * @param string $sBooleanExpr - * - * @return mixed - * @throws ModuleFileReaderException - */ - private function UnprotectedComputeBooleanExpression(string $sBooleanExpr) : mixed - { - try{ - $bResult = null; - @eval('$bResult = '.$sBooleanExpr.';'); - return $bResult; - } catch (Throwable $t) { - throw new ModuleFileReaderException("Eval of '$sBooleanExpr' caused an error: ".$t->getMessage()); - } - } - /** * @param string $sBooleanExpr * @@ -260,121 +162,11 @@ class ModuleFileParser { */ public function EvaluateBooleanExpression(string $sBooleanExpr) : bool { - $sPhpContent = <<ParsePhpCode($sPhpContent); - $oExpr = $aNodes[0]; - $oRes = $this->EvaluateExpression($oExpr->expr); - - return (bool) $oRes; - - } catch (Throwable $t) { - throw new ModuleFileReaderException("Eval of '$sBooleanExpr' caused an error:".$t->getMessage()); - } + return PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($sBooleanExpr); } - private function GetMixedValueToString(mixed $oExpr) : string + private function EvaluateExpression(Expr $oExpression) : mixed { - if (false === $oExpr){ - return "false"; - } - - if (true === $oExpr){ - return "true"; - } - - return $oExpr; - } - - private function EvaluateExpression(\PhpParser\Node\Expr $oExpression) : mixed - { - if ($oExpression instanceof \PhpParser\Node\Expr\BinaryOp){ - $sExpr = sprintf("%s %s %s", - $this->GetMixedValueToString($this->EvaluateExpression($oExpression->left)), - $oExpression->getOperatorSigil(), - $this->GetMixedValueToString($this->EvaluateExpression($oExpression->right)) - ); - //return $this->UnprotectedComputeBooleanExpression($sBooleanExpr);; - return $this->UnprotectedComputeBooleanExpression($sExpr); - } - - if ($oExpression instanceof \PhpParser\Node\Expr\BooleanNot){ - return ! $this->EvaluateExpression($oExpression->expr); - } - - if ($oExpression instanceof \PhpParser\Node\Expr\FuncCall){ - return $this->EvaluateCallFunction($oExpression); - } - - if ($oExpression instanceof \PhpParser\Node\Expr\StaticCall){ - return $this->EvaluateStaticCallFunction($oExpression); - } - - if ($oExpression instanceof \PhpParser\Node\Expr\ConstFetch){ - return $this->EvaluateConstantExpression($oExpression); - } - - if ($oExpression instanceof \PhpParser\Node\Expr\ClassConstFetch) { - return $this->EvaluateClassConstantExpression($oExpression); - } - - if ($oExpression instanceof \PhpParser\Node\Expr\StaticPropertyFetch) { - return $this->EvaluateStaticPropertyExpression($oExpression); - } - - return $oExpression->value; - } - - private function EvaluateCallFunction(\PhpParser\Node\Expr\FuncCall $oFunct) : mixed - { - $sFunction = $oFunct->name->name; - $aWhiteList = ["function_exists", "class_exists", "method_exists"]; - if (! in_array($sFunction, $aWhiteList)){ - throw new ModuleFileReaderException("FuncCall $sFunction not supported"); - } - - $aArgs=[]; - foreach ($oFunct->args as $arg){ - /** @var \PhpParser\Node\Arg $arg */ - $aArgs[]=$arg->value->value; - } - - $oReflectionFunction = new ReflectionFunction($sFunction); - return $oReflectionFunction->invoke(...$aArgs); - } - - /** - * @param \PhpParser\Node\Expr\StaticCall $oStaticCall - * - * @return mixed - * @throws \ModuleFileReaderException - * @throws \ReflectionException - */ - private function EvaluateStaticCallFunction(\PhpParser\Node\Expr\StaticCall $oStaticCall) : mixed - { - $sClassName = $oStaticCall->class->name; - $sMethodName = $oStaticCall->name->name; - $aWhiteList = ["SetupInfo::ModuleIsSelected"]; - $sStaticCallDescription = "$sClassName::$sMethodName"; - if (! in_array($sStaticCallDescription, $aWhiteList)){ - throw new ModuleFileReaderException("StaticCall $sStaticCallDescription not supported"); - } - - $aArgs=[]; - foreach ($oStaticCall->args as $arg){ - /** @var \PhpParser\Node\Arg $arg */ - $aArgs[]=$arg->value->value; - } - - $class = new \ReflectionClass($sClassName); - $method = $class->getMethod($sMethodName); - if (! $method->isPublic()){ - throw new ModuleFileReaderException("StaticCall $sStaticCallDescription not public"); - } - - return $method->invokeArgs(null, $aArgs); + return PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpression); } } \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/ArrayEvaluator.php b/setup/modulediscovery/evaluation/expression/ArrayEvaluator.php new file mode 100644 index 000000000..cd3b76110 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/ArrayEvaluator.php @@ -0,0 +1,47 @@ +items as $oArrayItem){ + if ($oArrayItem->key instanceof String_||$oArrayItem->key instanceof ConstFetch) { + //dictionnary + $sKey = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oArrayItem->key); + if (is_null($sKey)){ + continue; + } + } else { + //array + $sKey = $iIndex++; + } + + try { + $oValue = $oArrayItem->value; + $oEvaluatuedValue = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oValue); + $aModuleInformation[$sKey]=$oEvaluatuedValue; + } catch(ModuleFileReaderException $e){ + //required to support legacy below dump dependency + //'dependencies' => ['itop-config-mgmt/2.0.0'||'itop-structure/3.0.0'] + continue; + } + } + + return $aModuleInformation; + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/BinaryOpEvaluator.php b/setup/modulediscovery/evaluation/expression/BinaryOpEvaluator.php new file mode 100644 index 000000000..95d9ad7b1 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/BinaryOpEvaluator.php @@ -0,0 +1,20 @@ +EvaluateBinaryOperation( + PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpr->left), + PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpr->right)); + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/BitwiseAndEvaluator.php b/setup/modulediscovery/evaluation/expression/BitwiseAndEvaluator.php new file mode 100644 index 000000000..68a7e4878 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/BitwiseAndEvaluator.php @@ -0,0 +1,16 @@ +EvaluateExpression($oExpr->expr); + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/BooleanOrEvaluator.php b/setup/modulediscovery/evaluation/expression/BooleanOrEvaluator.php new file mode 100644 index 000000000..38abe1b8e --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/BooleanOrEvaluator.php @@ -0,0 +1,16 @@ +class->name; + $sProperty = $oExpr->name->name; + + if (class_exists($sClassName)){ + $class = new \ReflectionClass($sClassName); + if (array_key_exists($sProperty, $class->getConstants())) { + $oReflectionConstant = $class->getReflectionConstant($sProperty); + if ($oReflectionConstant->isPublic()){ + return $class->getConstant($sProperty); + } + } + } + + if ('class' === $sProperty){ + return $sClassName; + } + + return null; + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/ConstFetchEvaluator.php b/setup/modulediscovery/evaluation/expression/ConstFetchEvaluator.php new file mode 100644 index 000000000..402778c06 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/ConstFetchEvaluator.php @@ -0,0 +1,21 @@ +name)){ + return constant($oExpr->name); + } + + return null; + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/FuncCallEvaluator.php b/setup/modulediscovery/evaluation/expression/FuncCallEvaluator.php new file mode 100644 index 000000000..b50cee75a --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/FuncCallEvaluator.php @@ -0,0 +1,33 @@ +name->name; + $aWhiteList = ["function_exists", "class_exists", "method_exists"]; + if (! in_array($sFunction, $aWhiteList)){ + throw new ModuleFileReaderException("FuncCall $sFunction not supported"); + } + + $aArgs=[]; + foreach ($oExpr->args as $arg){ + /** @var \PhpParser\Node\Arg $arg */ + $aArgs[]=$arg->value->value; + } + + $oReflectionFunction = new ReflectionFunction($sFunction); + return $oReflectionFunction->invoke(...$aArgs); + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/MulEvaluator.php b/setup/modulediscovery/evaluation/expression/MulEvaluator.php new file mode 100644 index 000000000..001f02001 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/MulEvaluator.php @@ -0,0 +1,16 @@ +isInstantiable() + && $oReflectionClass->implementsInterface("{$sNamespace}iExprEvaluator")){ + $oClass = new $sClass; + static::$aPhpParserEvaluators[$oClass->GetHandledExpressionType()] = $oClass; + } + } + } + + return static::$oInstance; + } + + final public static function SetInstance(?PhpExpressionEvaluator $oInstance): void { + static::$oInstance = $oInstance; + } + + public function EvaluateExpression(Expr $oExpression) : mixed + { + $sClass = get_class($oExpression); + $oPhpParserEvaluator = static::$aPhpParserEvaluators[$sClass] ?? null; + if (is_null($oPhpParserEvaluator)){ + return $oExpression->value; + } + + return $oPhpParserEvaluator->Evaluate($oExpression); + } + + /** + * @param string $sBooleanExpr + * + * @return bool + * @throws \ModuleFileReaderException + */ + public function ParseAndEvaluateBooleanExpression(string $sBooleanExpr) : bool + { + return $this->ParseAndEvaluateExpression($sBooleanExpr); + } + + public function ParseAndEvaluateExpression(string $sExpr) : bool + { + $sPhpContent = <<ParsePhpCode($sPhpContent); + $oExpr = $aNodes[0]; + return $this->EvaluateExpression($oExpr->expr); + + } catch (\Throwable $t) { + throw new \ModuleFileReaderException("Eval of '$sExpr' caused an error:".$t->getMessage()); + } + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/StaticCallEvaluator.php b/setup/modulediscovery/evaluation/expression/StaticCallEvaluator.php new file mode 100644 index 000000000..27985db39 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/StaticCallEvaluator.php @@ -0,0 +1,41 @@ +class->name; + $sMethodName = $oExpr->name->name; + + $aWhiteList = ["SetupInfo::ModuleIsSelected"]; + $sStaticCallDescription = "$sClassName::$sMethodName"; + if (! in_array($sStaticCallDescription, $aWhiteList)){ + throw new ModuleFileReaderException("StaticCall $sStaticCallDescription not supported"); + } + + $aArgs=[]; + foreach ($oExpr->args as $arg){ + /** @var \PhpParser\Node\Arg $arg */ + $aArgs[]=$arg->value->value; + } + + $class = new \ReflectionClass($sClassName); + $method = $class->getMethod($sMethodName); + if (! $method->isPublic()){ + throw new ModuleFileReaderException("StaticCall $sStaticCallDescription not public"); + } + + return $method->invokeArgs(null, $aArgs); + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/StaticPropertyFetchEvaluator.php b/setup/modulediscovery/evaluation/expression/StaticPropertyFetchEvaluator.php new file mode 100644 index 000000000..076854953 --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/StaticPropertyFetchEvaluator.php @@ -0,0 +1,31 @@ +class->name; + $sProperty = $oExpr->name->name; + + if (class_exists($sClassName)){ + $class = new \ReflectionClass($sClassName); + if (array_key_exists($sProperty, $class->getStaticProperties())) { + $oReflectionProperty = $class->getProperty($sProperty); + if ($oReflectionProperty->isPublic()){ + return $class->getStaticPropertyValue($sProperty); + } + } + } + + return null; + } +} \ No newline at end of file diff --git a/setup/modulediscovery/evaluation/expression/iExprEvaluator.php b/setup/modulediscovery/evaluation/expression/iExprEvaluator.php new file mode 100644 index 000000000..27932e26c --- /dev/null +++ b/setup/modulediscovery/evaluation/expression/iExprEvaluator.php @@ -0,0 +1,11 @@ +ReadModuleFileInformation($sModuleFilePath); $aExpected = ModuleFileReader::GetInstance()->ReadModuleFileInformationUnsafe($sModuleFilePath); - //do not check dumb conf on dependencies - $aDependencies=$aRes[2]['dependencies']; - $aDependencies= array_merge([true], $aDependencies); - $aRes[2]['dependencies']=$aDependencies; $this->assertEquals($aExpected, $aRes); } diff --git a/tests/php-unit-tests/unitary-tests/setup/modulediscovery/PhpExpressionEvaluatorTest.php b/tests/php-unit-tests/unitary-tests/setup/modulediscovery/PhpExpressionEvaluatorTest.php new file mode 100644 index 000000000..a2eaa9714 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/setup/modulediscovery/PhpExpressionEvaluatorTest.php @@ -0,0 +1,13 @@ +ParseAndEvaluateExpression('false'); + $this->assertEquals(false, $res); + } +} \ No newline at end of file