Skip to content

Commit cf2fa8d

Browse files
committed
Removing evaluate
Removed the use of the evaluate function and replaced it with the custom function EvaluateConditionString. Added the EvaluateConditionString function to resolve the condition provided. It can handle function calls, conditional logic, scope resolution and comparisons. Also added the resolveOperator function to perform the comparisons
1 parent 864e93f commit cf2fa8d

File tree

1 file changed

+132
-6
lines changed

1 file changed

+132
-6
lines changed

vendor/wheels/model/validations.cfc

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -515,20 +515,16 @@ component {
515515
*/
516516
public boolean function $evaluateCondition() {
517517
local.rv = false;
518-
// since cf8 can't handle cfscript operators (==, != etc) inside an Evaluate() call we replace them with eq, neq etc in a try / catch
519518
local.evaluate = "condition,unless";
520519
local.iEnd = ListLen(local.evaluate);
521520
for (local.i = 1; local.i <= local.iEnd; local.i++) {
522521
local.item = ListGetAt(local.evaluate, local.i);
523522
if (StructKeyExists(arguments, local.item) && Len(arguments[local.item])) {
524523
local.key = local.item & "Evaluated";
525524
try {
526-
local[local.key] = Evaluate(arguments[local.item]);
525+
local[local.key] = EvaluateConditionString(arguments[local.item]);
527526
} catch (any e) {
528-
// cfformat-ignore-start
529-
arguments[local.item] = Replace(ReplaceList(arguments[local.item], "==,!=,<,<=,>,>=", " eq , neq , lt , lte , gt , gte "), " ", " ", "all");
530-
// cfformat-ignore-end
531-
local[local.key] = Evaluate(arguments[local.item]);
527+
return false;
532528
}
533529
}
534530
}
@@ -755,4 +751,134 @@ component {
755751
}
756752
return local.rv;
757753
}
754+
755+
/**
756+
* Function to evaluate a condition string without using Evaluate()
757+
* @condition The condition to resolve
758+
*/
759+
public any function EvaluateConditionString(required string condition) {
760+
// Replace CFScript operators with CFScript equivalents
761+
local.operatorList = "eq,neq,lt,lte,gt,gte";
762+
local.normalizedCondition = ReplaceList(condition, "==,!=,<,<=,>,>=", local.operatorList);
763+
local.normalizedCondition = Replace(local.normalizedCondition, " ", " ", "all"); // Normalize spaces
764+
local.before = local.normalizedCondition;
765+
766+
for(value in local.operatorList){
767+
local.position = FindNoCase(value, local.normalizedCondition);
768+
if(local.position){
769+
local.middle = value;
770+
local.before = trim(Mid(local.normalizedCondition,1,local.position - 1));
771+
local.after = trim(Mid(local.normalizedCondition, local.position + len(local.middle), len(local.normalizedCondition)));
772+
}
773+
}
774+
775+
// Handle cases where the condition references `this`, logical comparisons, or function calls
776+
if (Left(local.before, 5) == "this.") {
777+
778+
// Handle `this.someMethod()` or `this.someVariable`
779+
var key = Mid(local.before, 6); // Extract the part after "this."
780+
local.beforeRegexPattern = "^(.*?)\(";
781+
local.beforeMatch = REFindNoCase(local.beforeRegexPattern, key, 1, true);
782+
783+
if (local.beforeMatch.pos[1] > 0) {
784+
local.beforeParenthesis = Mid(key, 1, local.beforeMatch.len[1] - 1);
785+
local.afterParenthesis = Mid(key, local.beforeMatch.len[1] + 1, len(key));
786+
};
787+
788+
if(structKeyExists(local, 'afterParenthesis')){
789+
local.afterRegexPattern = "^(.*?)\)";
790+
local.afterMatch = REFindNoCase(local.afterRegexPattern, local.afterParenthesis, 1, true);
791+
if (local.afterMatch.pos[1] > 0) {
792+
local.afterParenthesis = Mid(local.afterParenthesis, 1, local.afterMatch.len[1] - 1);
793+
}
794+
795+
if(structKeyExists(this, local.beforeParenthesis)){
796+
797+
if (IsCustomFunction(this[local.beforeParenthesis])) {
798+
for (argString in local.afterParenthesis) {
799+
// Split the string on "="
800+
local.splitArg = ListToArray(argString, "=");
801+
local.variableName = Trim(local.splitArg[1]); // Left-hand side (variable name)
802+
local.variableValue = Replace(local.splitArg[2], "'", "", "all"); // Right-hand side (value without quotes)
803+
804+
// Add to the arguments collection
805+
local.argumentsCollection[local.variableName] = local.variableValue;
806+
}
807+
local.rv = this[local.beforeParenthesis](argumentCollection=local.argumentsCollection); // Call the function
808+
} else {
809+
local.rv = this[local.beforeParenthesis]; // Return the variable value
810+
}
811+
}
812+
}
813+
814+
if (StructKeyExists(this, key)) {
815+
if (IsCustomFunction(this[key])) {
816+
local.rv = this[key](); // Call the function
817+
} else {
818+
local.rv = this[key]; // Return the variable value
819+
}
820+
}
821+
822+
} else if(right(local.normalizedCondition, 2) eq '()'){
823+
if(find("!", local.normalizedCondition)){
824+
local.normalizedCondition = replace(local.normalizedCondition, "!", "", "one");
825+
return !this[replace(local.normalizedCondition, '()', '')]();
826+
} else{
827+
return this[replace(local.normalizedCondition, '()', '')]();
828+
}
829+
} else {
830+
// Handle logical expressions (e.g., "1 eq 0", "5 gt 3", "myFunction() eq true")
831+
local.tokens = ListToArray(local.normalizedCondition, " ");
832+
if (ArrayLen(local.tokens) < 3) {
833+
return false; // Invalid condition
834+
}
835+
836+
local.leftOperand = local.tokens[1];
837+
local.operator = local.tokens[2];
838+
local.rightOperand = local.tokens[3];
839+
840+
// Resolve variables or cast to numeric values
841+
local.leftOperand = isNumeric(local.leftOperand) ? JavaCast("double", local.leftOperand) : local.leftOperand;
842+
local.rightOperand = isNumeric(local.rightOperand) ? JavaCast("double", local.rightOperand) : local.rightOperand;
843+
// Evaluate the logical expression
844+
return resolveOperator(local.leftOperand, local.rightOperand, local.operator);
845+
}
846+
847+
if(structKeyExists(local, 'after') && structKeyExists(local, 'middle')){
848+
local.singleRegexPattern = "^'.*'$";
849+
local.doubleRegexPattern = '^".*"$';
850+
if (REFindNoCase(local.singleRegexPattern, local.after)) {
851+
local.after = Replace(local.after, "'", "", "all");
852+
}
853+
if (REFindNoCase(local.doubleRegexPattern, local.after)) {
854+
local.after = Replace(local.after, '"', "", "all");
855+
}
856+
857+
return resolveOperator(local.rv, local.after, local.middle);
858+
}
859+
860+
return structKeyExists(local, rv)? local.rv : false;
861+
}
862+
863+
/**
864+
* Evaluate a logical expression based on the operator.
865+
*/
866+
public boolean function resolveOperator(required any firstOperator, required any secondOperator, required any operator){
867+
switch (arguments.operator) {
868+
case "eq":
869+
return (arguments.firstOperator == arguments.secondOperator);
870+
case "neq":
871+
return (arguments.firstOperator != arguments.secondOperator);
872+
case "lt":
873+
return (arguments.firstOperator < arguments.secondOperator);
874+
case "lte":
875+
return (arguments.firstOperator <= arguments.secondOperator);
876+
case "gt":
877+
return (arguments.firstOperator > arguments.secondOperator);
878+
case "gte":
879+
return (arguments.firstOperator >= arguments.secondOperator);
880+
default:
881+
throw("Unsupported operator in condition: " & arguments.operator);
882+
}
883+
}
758884
}

0 commit comments

Comments
 (0)