456) */ class DependencyExpression { private static PhpExpressionEvaluator $oPhpExpressionEvaluator; private string $sDependencyExpression; private bool $bValid = true; private bool $bResolved = false; /** * @var array $aRemainingModuleNamesToResolve */ private array $aRemainingModuleNamesToResolve; /** * @var array $aParamsPerModuleId */ private array $aParamsPerModuleId; public function __construct(string $sDependencyExpression) { $this->sDependencyExpression = $sDependencyExpression; $this->aParamsPerModuleId = []; $this->aRemainingModuleNamesToResolve = []; if (preg_match_all('/([^\(\)&| ]+)/', $sDependencyExpression, $aMatches)) { foreach ($aMatches as $aMatch) { foreach ($aMatch as $sModuleId) { if (!array_key_exists($sModuleId, $this->aParamsPerModuleId)) { // $sModuleId in the dependency string is made of a / // where the operator is < <= = > >= (by default >=) $aModuleMatches = []; if (preg_match('|^([^/]+)/(?=?)([^><=]+)$|', $sModuleId, $aModuleMatches)) { $sModuleName = $aModuleMatches[1]; $this->aRemainingModuleNamesToResolve[$sModuleName] = true; $sOperator = $aModuleMatches[2]; if ($sOperator == '') { $sOperator = '>='; } $sExpectedVersion = $aModuleMatches[3]; $this->aParamsPerModuleId[$sModuleId] = [$sModuleName, $sOperator, $sExpectedVersion]; } } } } } else { $this->bValid = false; } } private static function GetPhpExpressionEvaluator(): PhpExpressionEvaluator { if (!isset(static::$oPhpExpressionEvaluator)) { static::$oPhpExpressionEvaluator = new PhpExpressionEvaluator([], RunTimeEnvironment::STATIC_CALL_AUTOSELECT_WHITELIST); } return static::$oPhpExpressionEvaluator; } /** * Return module names potentially required by current dependency * * @return array */ public function GetRemainingModuleNamesToResolve(): array { return array_keys($this->aRemainingModuleNamesToResolve); } public function IsResolved(): bool { return $this->bResolved; } /** * Check if dependency is resolved with current list of module versions * * @param array $aResolvedModuleVersions : versions by module names dict * @param array $aAllModuleNames : modules names dict * * @return void */ public function UpdateModuleResolutionState(array $aResolvedModuleVersions, array $aAllModuleNames): void { if (!$this->bValid) { return; } $aReplacements = []; $bDelayEvaluation = false; foreach ($this->aParamsPerModuleId as $sModuleId => list($sModuleName, $sOperator, $sExpectedVersion)) { if (array_key_exists($sModuleName, $aResolvedModuleVersions)) { // module is resolved, check the version $sCurrentVersion = $aResolvedModuleVersions[$sModuleName]; if (version_compare($sCurrentVersion, $sExpectedVersion, $sOperator)) { if (array_key_exists($sModuleName, $this->aRemainingModuleNamesToResolve)) { unset($this->aRemainingModuleNamesToResolve[$sModuleName]); } $aReplacements[$sModuleId] = '(true)'; // Add parentheses to protect against invalid condition causing // 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 } } else { // module is not resolved yet if (array_key_exists($sModuleName, $aAllModuleNames)) { //Weird piece of code that covers below usecase: //module B dependency: 'moduleA || true' // if moduleA not present on disk, whole expression can be evaluated and may be resolved // if moduleA present on disk, we need to sort moduleB after moduleA. expression cannot be resolved yet $bDelayEvaluation = true; } else { $aReplacements[$sModuleId] = '(false)'; // Add parentheses to protect against invalid condition causing } } } if ($bDelayEvaluation) { return; } $bResult = false; $sBooleanExpr = str_replace(array_keys($aReplacements), array_values($aReplacements), $this->sDependencyExpression); try { $bResult = self::GetPhpExpressionEvaluator()->ParseAndEvaluateBooleanExpression($sBooleanExpr); } catch (ModuleFileReaderException $e) { //logged already echo "Failed to parse the boolean Expression = '$sBooleanExpr'
"; } $this->bResolved = $bResult; } public function IsValid(): bool { return $this->bValid; } }