replace eval by iTop custom evaluation classes

This commit is contained in:
odain
2025-09-02 17:03:02 +02:00
parent f7b5091b39
commit 1962cd7a88
22 changed files with 357 additions and 314 deletions

View File

@@ -391,7 +391,7 @@ class ModuleDiscovery
{
$sBooleanExpr = str_replace(array_keys($aReplacements), array_values($aReplacements), $sDepString);
try{
$bResult = ModuleFileParser::GetInstance()->EvaluateBooleanExpression($sBooleanExpr);
$bResult = \evaluation\expression\PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($sBooleanExpr);
} catch(ModuleFileReaderException $e){
//logged already
echo "Failed to parse the boolean Expression = '$sBooleanExpr'<br/>";

View File

@@ -75,7 +75,7 @@ class ModuleFileParser {
throw new ModuleFileReaderException("2nd parameter to SetupWebPage::AddModule not a string: " . get_class($oModuleId->value), 0, null, $sModuleFilePath);
}
$sModuleId = $this->EvaluateExpression($oModuleId->value);
$sModuleId = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oModuleId->value);
$oModuleConfigInfo = $aArgs[2];
if (false === ($oModuleConfigInfo instanceof PhpParser\Node\Arg)) {
@@ -87,7 +87,7 @@ class ModuleFileParser {
throw new ModuleFileReaderException("3rd parameter to SetupWebPage::AddModule not an array: " . get_class($oModuleConfigInfo->value), 0, null, $sModuleFilePath);
}
$aModuleConfig = $this->EvaluateExpression($oModuleConfigInfo->value);
$aModuleConfig = PhpExpressionEvaluator::GetInstance()->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);
@@ -109,7 +109,7 @@ class ModuleFileParser {
*/
public function GetModuleInformationFromIf(string $sModuleFilePath, \PhpParser\Node\Stmt\If_ $oNode) : ?array
{
$bCondition = $this->EvaluateExpression($oNode->cond);
$bCondition = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oNode->cond);
if ($bCondition) {
foreach ($oNode->stmts as $oSubNode) {
if ($oSubNode instanceof \PhpParser\Node\Stmt\Expression) {
@@ -126,7 +126,7 @@ class ModuleFileParser {
if (! is_null($oNode->elseifs)) {
foreach ($oNode->elseifs as $oElseIfSubNode) {
/** @var \PhpParser\Node\Stmt\ElseIf_ $oElseIfSubNode */
$bCondition = $this->EvaluateExpression($oElseIfSubNode->cond);
$bCondition = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oElseIfSubNode->cond);
if ($bCondition) {
return $this->GetModuleConfigurationFromStatement($sModuleFilePath, $oElseIfSubNode->stmts);
}
@@ -153,20 +153,4 @@ class ModuleFileParser {
return null;
}
/**
* @param string $sBooleanExpr
*
* @return bool
* @throws ModuleFileReaderException
*/
public function EvaluateBooleanExpression(string $sBooleanExpr) : bool
{
return PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($sBooleanExpr);
}
private function EvaluateExpression(Expr $oExpression) : mixed
{
return PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpression);
}
}

View File

@@ -0,0 +1,28 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
class ArrayDimFetchEvaluator implements iExprEvaluator {
public function GetHandledExpressionType(): string {
return ArrayDimFetch::class;
}
public function Evaluate(Expr $oExpr): mixed {
/** @var ArrayDimFetch $oExpr */
$var = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpr->var);
if (is_null($var)){
return null;
}
$dim = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpr->dim);
if (is_null($var)){
return $dim;
}
return $var[$dim] ?? null;
}
}

View File

@@ -6,6 +6,7 @@ use ModuleFileReaderException;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
class ArrayEvaluator implements iExprEvaluator {
@@ -20,7 +21,7 @@ class ArrayEvaluator implements iExprEvaluator {
$aModuleInformation=[];
/** @var \PhpParser\Node\Expr\ArrayItem $oValue */
foreach ($oExpr->items as $oArrayItem){
if ($oArrayItem->key instanceof String_||$oArrayItem->key instanceof ConstFetch) {
if ($oArrayItem->key instanceof Int_||$oArrayItem->key instanceof String_||$oArrayItem->key instanceof ConstFetch) {
//dictionnary
$sKey = PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oArrayItem->key);
if (is_null($sKey)){

View File

@@ -2,10 +2,8 @@
namespace evaluation\expression;
use ModuleFileReaderException;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp;
use Throwable;
abstract class BinaryOpEvaluator implements iExprEvaluator {
abstract function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed;

View File

@@ -0,0 +1,28 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\Concat;
class ConcatEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return Concat::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
if (is_null($left) && is_null($right)){
return null;
}
if (is_null($left)){
return $right;
}
if (is_null($right)){
return $left;
}
return "$left" . "$right";
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\Equal;
class EqualEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return Equal::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
return $left == $right;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\Greater;
class GreaterEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return Greater::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
return $left > $right;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual;
class GreaterOrEqualEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return GreaterOrEqual::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
return $left >= $right;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\NotEqual;
class NotEqualEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return NotEqual::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
return $left != $right;
}
}

View File

@@ -1,5 +1,6 @@
<?php
#Combodo\\iTop\\
namespace evaluation\expression;
use PhpParser\Node\Expr;
@@ -61,7 +62,7 @@ class PhpExpressionEvaluator {
return $this->ParseAndEvaluateExpression($sBooleanExpr);
}
public function ParseAndEvaluateExpression(string $sExpr) : bool
public function ParseAndEvaluateExpression(string $sExpr) : mixed
{
$sPhpContent = <<<PHP
<?php

View File

@@ -0,0 +1,16 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\Smaller;
class SmallerEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return Smaller::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
return $left < $right;
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual;
class SmallerOrEqualEvaluator extends BinaryOpEvaluator {
public function GetHandledExpressionType(): string {
return SmallerOrEqual::class;
}
function EvaluateBinaryOperation(mixed $left, mixed $right) : mixed
{
return $left <= $right;
}
}

View File

@@ -18,7 +18,7 @@ class StaticCallEvaluator implements iExprEvaluator {
$sClassName = $oExpr->class->name;
$sMethodName = $oExpr->name->name;
$aWhiteList = ["SetupInfo::ModuleIsSelected"];
$aWhiteList = ["SetupInfo::ModuleIsSelected", "utils::GetItopVersionWikiSyntax"];
$sStaticCallDescription = "$sClassName::$sMethodName";
if (! in_array($sStaticCallDescription, $aWhiteList)){
throw new ModuleFileReaderException("StaticCall $sStaticCallDescription not supported");

View File

@@ -0,0 +1,19 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\UnaryMinus;
class UnaryMinusEvaluator implements iExprEvaluator {
public function GetHandledExpressionType(): string {
return UnaryMinus::class;
}
public function Evaluate(Expr $oExpr): mixed {
/** @var UnaryMinus $oExpr */
return - PhpExpressionEvaluator::GetInstance()->EvaluateExpression($oExpr->expr);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace evaluation\expression;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\Variable;
class VariableEvaluator implements iExprEvaluator {
public function GetHandledExpressionType(): string {
return Variable::class;
}
public function Evaluate(Expr $oExpr): mixed {
/** @var Variable $oExpr */
if (is_null($oExpr->name)){
return null;
}
if (! isset($oExpr->name)) {
return null;
}
$sVarname=$oExpr->name;
$bResult = null;
@eval('$bResult = $'.$sVarname.';');
return $bResult;
}
}

View File

@@ -459,7 +459,7 @@ class RunTimeEnvironment
{
SetupInfo::SetSelectedModules($aRet);
try{
$bSelected = ModuleFileParser::GetInstance()->EvaluateBooleanExpression($oModule->GetAutoSelect());
$bSelected = \evaluation\expression\PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($oModule->GetAutoSelect());
if ($bSelected)
{
$aRet[$oModule->GetName()] = $oModule; // store the Id of the selected module

View File

@@ -270,7 +270,7 @@ class InstallationFileService {
{
try {
SetupInfo::SetSelectedModules($this->aSelectedModules);
$bSelected = ModuleFileParser::GetInstance()->EvaluateBooleanExpression($aModule['auto_select']);
$bSelected =\evaluation\expression\PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aModule['auto_select']);
if ($bSelected)
{
// Modules in data/production-modules/ are considered as mandatory and always installed

View File

@@ -1787,7 +1787,7 @@ EOF
// Check the module selection
try {
SetupInfo::SetSelectedModules($aModules);
$bSelected = ModuleFileParser::GetInstance()->EvaluateBooleanExpression($aInfo['auto_select']);
$bSelected = \evaluation\expression\PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aInfo['auto_select']);
}
catch (ModuleFileReaderException $e) {
//logged already
@@ -1865,7 +1865,7 @@ EOF
try
{
SetupInfo::SetSelectedModules($aModules);
$bSelected = ModuleFileParser::GetInstance()->EvaluateBooleanExpression($aModule['auto_select']);
$bSelected = \evaluation\expression\PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($aModule['auto_select']);
if ($bSelected)
{
$aModules[$sModuleId] = true; // store the Id of the selected module

View File

@@ -1,277 +0,0 @@
<?php
namespace Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use ModuleFileParser;
use ModuleFileReader;
use PhpParser\ParserFactory;
use SetupUtils;
class ModuleFileParserTest extends ItopDataTestCase
{
public static $STATIC_PROPERTY = 123;
private static $PRIVATE_STATIC_PROPERTY = 123;
private const PRIVATE_CONSTANT = 123;
protected function setUp(): void
{
parent::setUp();
$this->RequireOnceItopFile('setup/modulediscovery/ModuleFileReader.php');
}
public static function EvaluateBooleanExpressionProvider()
{
return [
"true" => [ "expr" => "true", "expected" => true],
"(true)" => [ "expr" => "(true)", "expected" => true],
"(false|true)" => [ "expr" => "(false|true)", "expected" => true],
"(false||true)" => [ "expr" => "(false||true)", "expected" => true],
"false" => [ "expr" => "false", "expected" => false],
"(false)" => [ "expr" => "(false)", "expected" => false],
"(false&&true)" => [ "expr" => "(false&&true)", "expected" => false],
"(false&true)" => [ "expr" => "(false&true)", "expected" => false],
"10 * 10" => [ "expr" => "10 * 10", "expected" => 100],
];
}
/**
* @dataProvider EvaluateBooleanExpressionProvider
*/
public function testEvaluateBooleanExpression(string $sBooleanExpression, $expected){
$this->assertEquals($expected, ModuleFileParser::GetInstance()->EvaluateBooleanExpression($sBooleanExpression), $sBooleanExpression);
}
public function testEvaluateBooleanExpression_BrokenBooleanExpression(){
$this->expectException(\ModuleFileReaderException::class);
$this->expectExceptionMessage('Eval of \'(a || true)\' caused an error');
$this->assertTrue(ModuleFileParser::GetInstance()->EvaluateBooleanExpression("(a || true)"));
}
public static function EvaluateBooleanExpressionAutoselectProvider()
{
$sSimpleCallToModuleIsSelected = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt\")";
$sSimpleCallToModuleIsSelected2 = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt-notselected\")";
$sCallToModuleIsSelectedCombinedWithAndOperator = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt\") || SetupInfo::ModuleIsSelected(\"itop-virtualization-mgmt\")";
$sCallToModuleIsSelectedCombinedWithAndOperator2 = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt-notselected\") || SetupInfo::ModuleIsSelected(\"itop-virtualization-mgmt\")";
return [
"simple call to SetupInfo::ModuleIsSelected SELECTED" => [
"expr" => $sSimpleCallToModuleIsSelected,
"expected" => true,
],
"simple call to SetupInfo::ModuleIsSelected NOT SELECTED" => [
"expr" => $sSimpleCallToModuleIsSelected2,
"expected" => false,
],
"call to SetupInfo::ModuleIsSelected + OR => SELECTED" => [
"expr" => $sCallToModuleIsSelectedCombinedWithAndOperator,
"expected" => true,
],
"simple call to SetupInfo::ModuleIsSelected + OR => NOT SELECTED" => [
"expr" => $sCallToModuleIsSelectedCombinedWithAndOperator2,
"expected" => false,
],
];
}
/**
* @dataProvider EvaluateBooleanExpressionAutoselectProvider
*/
public function testEvaluateBooleanExpression_Autoselect(string $sBooleanExpression, bool $expected){
\SetupInfo::SetSelectedModules(["itop-storage-mgmt" => "123"]);
$this->assertEquals($expected, ModuleFileParser::GetInstance()->EvaluateBooleanExpression($sBooleanExpression), $sBooleanExpression);
}
public function testEvaluateConstantExpression()
{
$sPHP = <<<PHP
<?php
APPROOT;
PHP;
$aNodes = ModuleFileParser::GetInstance()->ParsePhpCode($sPHP);
/** @var \PhpParser\Node\Expr $oExpr */
$oExpr = $aNodes[0];
$val = $this->InvokeNonPublicMethod(ModuleFileParser::class, "EvaluateConstantExpression", ModuleFileParser::GetInstance(), [$oExpr->expr]);
$this->assertEquals(APPROOT, $val);
}
public function testEvaluateClassConstantExpression_PublicConstant()
{
$this->validateEvaluateClassConstantExpression('SetupUtils::PHP_MIN_VERSION', SetupUtils::PHP_MIN_VERSION);
}
public function testEvaluateClassConstantExpression_PrivateConstantShouldNotBeFound()
{
$this->validateEvaluateClassConstantExpression('Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery\ModuleFileParserTest::PRIVATE_CONSTANT', null);
}
public function testEvaluateClassConstant_UnknownConstant()
{
$this->validateEvaluateClassConstantExpression('SetupUtils::UNKOWN_CONSTANT', null);
}
public function testEvaluateClassConstant_UnknownClass()
{
$this->validateEvaluateClassConstantExpression('UnknownGaBuZoMeuClass::PHP_MIN_VERSION', null);
}
public function testEvaluateClassConstant_UnknownClassGetClass()
{
$this->validateEvaluateClassConstantExpression('UnknownGaBuZoMeuClass::class', 'UnknownGaBuZoMeuClass');
}
public function validateEvaluateClassConstantExpression($sExpression, $expected)
{
$sPHP = <<<PHP
<?php
$sExpression;
PHP;
$aNodes = ModuleFileParser::GetInstance()->ParsePhpCode($sPHP);
/** @var \PhpParser\Node\Expr $oExpr */
$oExpr = $aNodes[0];
$val = $this->InvokeNonPublicMethod(ModuleFileParser::class, "EvaluateClassConstantExpression", ModuleFileParser::GetInstance(), [$oExpr->expr]);
$this->assertEquals($expected, $val, "$sExpression");
}
public function testEvaluateClassConstant_PublicGetStaticProperty()
{
$this->validateEvaluateStaticPropertyExpression('Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery\ModuleFileParserTest::$STATIC_PROPERTY', ModuleFileParserTest::$STATIC_PROPERTY);
}
public function testEvaluateClassConstant_PrivateGetStaticPropertyShouldNotBeFound()
{
$this->validateEvaluateStaticPropertyExpression('Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery\ModuleFileParserTest::$PRIVATE_STATIC_PROPERTY', null);
}
public function validateEvaluateStaticPropertyExpression($sExpression, $expected)
{
$sPHP = <<<PHP
<?php
$sExpression;
PHP;
$aNodes = ModuleFileParser::GetInstance()->ParsePhpCode($sPHP);
/** @var \PhpParser\Node\Expr $oExpr */
$oExpr = $aNodes[0];
$val = $this->InvokeNonPublicMethod(ModuleFileParser::class, "EvaluateStaticPropertyExpression", ModuleFileParser::GetInstance(), [$oExpr->expr]);
$this->assertEquals($expected, $val, "$sExpression");
}
public static function EvaluateExpressionBooleanProvider() {
$sTruePHP = <<<PHP
<?php
if (COND){
echo "toto";
}
PHP;
return [
'"true"' => [
"code" => str_replace("COND", '"true"', $sTruePHP),
"bool_expected" => "true",
],
"true" => [
"code" => str_replace("COND", "true", $sTruePHP),
"bool_expected" => true,
],
"false" => [
"code" => str_replace("COND", "false", $sTruePHP),
"bool_expected" => false,
],
'"false"' => [
"code" => str_replace("COND", '"false"', $sTruePHP),
"bool_expected" => "false",
],
"not ok" => [
"code" => str_replace("COND", "! false", $sTruePHP),
"bool_expected" => true,
],
"not ko" => [
"code" => str_replace("COND", "! (true)", $sTruePHP),
"bool_expected" => false,
],
"AND ko" => [
"code" => str_replace("COND", "true && false", $sTruePHP),
"bool_expected" => false,
],
"AND ok1" => [
"code" => str_replace("COND", "true && true", $sTruePHP),
"bool_expected" => true,
],
"AND ko2" => [
"code" => str_replace("COND", "true && true && false", $sTruePHP),
"bool_expected" => false,
],
"OR ko" => [
"code" => str_replace("COND", "false || false", $sTruePHP),
"bool_expected" => false,
],
"OR ok" => [
"code" => str_replace("COND", "false ||true", $sTruePHP),
"bool_expected" => true,
],
"OR ok2" => [
"code" => str_replace("COND", "false ||false||true", $sTruePHP),
"bool_expected" => true,
],
"function_exists('ldap_connect')" => [
"code" => str_replace("COND", "function_exists('ldap_connect')", $sTruePHP),
"bool_expected" => function_exists('ldap_connect'),
],
"function_exists('gabuzomeushouldnotexist')" => [
"code" => str_replace("COND", "function_exists('gabuzomeushouldnotexist')", $sTruePHP),
"bool_expected" => function_exists('gabuzomeushouldnotexist'),
],
"1 > 2" => [
"code" => str_replace("COND", "1 > 2", $sTruePHP),
"bool_expected" => false,
],
"1 == 1" => [
"code" => str_replace("COND", "1 == 1", $sTruePHP),
"bool_expected" => true,
],
"1 < 2" => [
"code" => str_replace("COND", "1 < 2", $sTruePHP),
"bool_expected" => true,
],
"PHP_VERSION_ID == PHP_VERSION_ID" => [
"code" => str_replace("COND", "PHP_VERSION_ID == PHP_VERSION_ID", $sTruePHP),
"bool_expected" => true,
],
"PHP_VERSION_ID != PHP_VERSION_ID" => [
"code" => str_replace("COND", "PHP_VERSION_ID != PHP_VERSION_ID", $sTruePHP),
"bool_expected" => false,
],
];
}
/**
* @dataProvider EvaluateExpressionBooleanProvider
*/
public function testEvaluateExpression($sPHP, $bExpected)
{
$aNodes = ModuleFileParser::GetInstance()->ParsePhpCode($sPHP);
/** @var \PhpParser\Node\Expr $oExpr */
$oExpr = $aNodes[0];
$val = $this->InvokeNonPublicMethod(ModuleFileParser::class, "EvaluateExpression", ModuleFileParser::GetInstance(), [$oExpr->cond]);
$this->assertEquals($bExpected, $val);
}
}

View File

@@ -30,16 +30,28 @@ class ModuleFileReaderTest extends ItopDataTestCase
/*public function testAllReadModuleFileConfiguration()
{
$_SERVER=[
'SERVER_NAME' => 'titi'
];
$aErrors=[];
foreach (glob(__DIR__.'/resources/all_factory/module.*.php') as $sModuleFilePath){
$aRes = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath);
foreach (glob(__DIR__.'/resources/all_designer/**.php') as $sModuleFilePath){
//var_dump($sModuleFilePath);
try{
$aRes = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath);
} catch(\Exception $e){
$aErrors[]=basename($sModuleFilePath);
continue;
}
$aExpected = ModuleFileReader::GetInstance()->ReadModuleFileInformationUnsafe($sModuleFilePath);
if ($aExpected !== $aRes){
$aErrors[]=basename($sModuleFilePath);
continue;
}
//$this->assertEquals($aExpected, $aRes);
//break;
//$this->assertEquals($aExpected, $aRes, $sModuleFilePath);
}
$this->assertEquals([], $aErrors);

View File

@@ -5,9 +5,130 @@ namespace Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery;
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
use evaluation\expression\PhpExpressionEvaluator;
class PhpExpressionEvaluatorTest extends ItopDataTestCase{
public function testEvaluateExpression(){
$res = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateExpression('false');
$this->assertEquals(false, $res);
class PhpExpressionEvaluatorTest extends ItopDataTestCase {
public static $STATIC_PROPERTY = 123;
private static $PRIVATE_STATIC_PROPERTY = 123;
private const PRIVATE_CONSTANT = 123;
public static function EvaluateExpressionProvider() {
return [
'ConstFetch: false' => [ 'sExpression' => 'false'],
'ConstFetch: (false)' => [ 'sExpression' => 'false'],
'ConstFetch: true' => [ 'sExpression' => 'true'],
//'ConstFetch: __FILE__' => [ 'sExpression' => __FILE__],
'ConstFetch: (true)' => [ 'sExpression' => 'true'],
'ClassConstFetch: public existing constant' => [ 'sExpression' => 'SetupUtils::PHP_MIN_VERSION'],
'ClassConstFetch: unknown constant' => [ 'sExpression' => 'SetupUtils::UNKNOWN_CONSTANT'],
'ClassConstFetch: unknown class:constant' => [ 'sExpression' => 'GabuZomeuUnknownClass::UNKNOWN_CONSTANT'],
'ClassConstFetch: unknown class:class' => [ 'sExpression' => 'GabuZomeuUnknownClass::class'],
'ClassConstFetch: private existing constant' => [
'sExpression' => 'Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery\PhpExpressionEvaluatorTest::PRIVATE_CONSTANT',
'forced_expected' => null
],
'StaticProperty: public existing constant' => [ 'sExpression' => 'Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery\PhpExpressionEvaluatorTest::$STATIC_PROPERTY'],
'StaticProperty: private existing constant' => [
'sExpression' => 'Combodo\iTop\Test\UnitTest\Setup\ModuleDiscovery\PhpExpressionEvaluatorTest::$PRIVATE_STATIC_PROPERTY',
'forced_expected' => null
],
'BinaryOperator: false|true' => [ 'sExpression' => 'false|true'],
'BinaryOperator: false||true' => [ 'sExpression' => 'false||true'],
'BinaryOperator: false&&true' => [ 'sExpression' => 'false&&true'],
'BinaryOperator: true&&true&&true&&false' => [ 'sExpression' => 'true&&true&&true&&false'],
'BinaryOperator: false&true' => [ 'sExpression' => 'false&true'],
'BinaryOperator: ! true' => [ 'sExpression' => '! true'],
'BinaryOperator: 10 * 5' => [ 'sExpression' => '10 * 5'],
'BinaryOperator: 1 > 2' => [ 'sExpression' => '1 > 2'],
'BinaryOperator: 1 >= 1' => [ 'sExpression' => '1 >= 1'],
'BinaryOperator: 1 <= 1' => [ 'sExpression' => '1 <= 1'],
'BinaryOperator: PHP_VERSION_ID == PHP_VERSION_ID' => [ 'sExpression' => 'PHP_VERSION_ID == PHP_VERSION_ID'],
'BinaryOperator: PHP_VERSION_ID != PHP_VERSION_ID' => [ 'sExpression' => 'PHP_VERSION_ID != PHP_VERSION_ID'],
'FuncCall: function_exists(\'ldap_connect\')' => [ 'sExpression' => 'function_exists(\'ldap_connect\')'],
'FuncCall: function_exists(\'gabuzomeushouldnotexist\')' => [ 'sExpression' => 'function_exists(\'gabuzomeushouldnotexist\')'],
'UnaryMinus: -1' => ['sExpression' => '-1'],
'Concat: "a"."b"' => ['sExpression' => '"a"."b"'],
'ArrayDimFetch: $_SERVER[\'toto\']' => ['sExpression' => '$_SERVER[\'toto\']'],
'Variable: $_SERVER' => ['sExpression' => '$_SERVER'],
'Array: [1000 => "a"]' => ['sExpression' => '[1000 => "a"]'],
'Array: ["a"]' => ['sExpression' => '["a"]'],
'Array dict: ["a"=>"b"]' => ['sExpression' => '["a"=>"b"]'],
'StaticCall utils::GetItopVersionWikiSyntax()' => ['sExpression' => 'utils::GetItopVersionWikiSyntax()']
];
}
/**
* @dataProvider EvaluateExpressionProvider
*/
public function testEvaluateExpression($sExpression, $forced_expected="NOTPROVIDED")
{
$_SERVER=[
'toto' => 'titi'
];
$res = PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateExpression($sExpression);
if ($forced_expected === "NOTPROVIDED"){
$this->assertEquals($this->UnprotectedComputeExpression($sExpression), $res, $sExpression);
} else {
$this->assertEquals($forced_expected, $res, $sExpression);
}
}
/**
* @param string $sBooleanExpr
*
* @return mixed
* @throws \ModuleFileReaderException
*/
private function UnprotectedComputeExpression(string $sExpr) : mixed
{
try {
$bResult = null;
@eval('$bResult = '.$sExpr.';');
return $bResult;
} catch (\Throwable $t){
return null;
}
}
public function testParseAndEvaluateBooleanExpression_BrokenBooleanExpression(){
$this->expectException(\ModuleFileReaderException::class);
$this->expectExceptionMessage('Eval of \'(a || true)\' caused an error');
$this->assertTrue(PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression("(a || true)"));
}
public static function ParseAndEvaluateBooleanExpression_AutoselectProvider()
{
$sSimpleCallToModuleIsSelected = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt\")";
$sSimpleCallToModuleIsSelected2 = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt-notselected\")";
$sCallToModuleIsSelectedCombinedWithAndOperator = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt\") || SetupInfo::ModuleIsSelected(\"itop-virtualization-mgmt\")";
$sCallToModuleIsSelectedCombinedWithAndOperator2 = "SetupInfo::ModuleIsSelected(\"itop-storage-mgmt-notselected\") || SetupInfo::ModuleIsSelected(\"itop-virtualization-mgmt\")";
return [
"simple call to SetupInfo::ModuleIsSelected SELECTED" => [
"expr" => $sSimpleCallToModuleIsSelected,
"expected" => true,
],
"simple call to SetupInfo::ModuleIsSelected NOT SELECTED" => [
"expr" => $sSimpleCallToModuleIsSelected2,
"expected" => false,
],
"call to SetupInfo::ModuleIsSelected + OR => SELECTED" => [
"expr" => $sCallToModuleIsSelectedCombinedWithAndOperator,
"expected" => true,
],
"simple call to SetupInfo::ModuleIsSelected + OR => NOT SELECTED" => [
"expr" => $sCallToModuleIsSelectedCombinedWithAndOperator2,
"expected" => false,
],
];
}
/**
* @dataProvider ParseAndEvaluateBooleanExpression_AutoselectProvider
*/
public function testEvaluateBooleanExpression_Autoselect(string $sBooleanExpression, bool $expected){
\SetupInfo::SetSelectedModules(["itop-storage-mgmt" => "123"]);
$this->assertEquals($expected, PhpExpressionEvaluator::GetInstance()->ParseAndEvaluateBooleanExpression($sBooleanExpression), $sBooleanExpression);
}
}