Added support for fact-to-fact comparison in the PHP Rules Engine, allowing rules to compare two dynamic facts at runtime instead of comparing a fact to a static value.
Added logic to resolve fact references in the value field:
// Resolve value if it's another fact (fact-to-fact comparison)
if (is_array($value) && isset($value['fact'])) {
$value = $facts->get($value['fact'], $value['path'] ?? null);
}This checks if the value is structured as a fact reference (an array with a fact key), and if so, retrieves that fact's value dynamically before performing the comparison.
Added logic to display fact-to-fact comparisons in human-readable format:
// Handle fact-to-fact comparison display
if (is_array($value) && isset($value['fact'])) {
$valueFact = $value['fact'];
$valuePath = $value['path'] ?? null;
$valueDisplay = $valueFact;
if ($valuePath) {
$valuePathParts = explode('.', ltrim($valuePath, '$.'));
$valueDisplay = end($valuePathParts);
}
return "$factDisplay $operatorText $valueDisplay";
}- Added "Fact-to-Fact Comparison" to the Features list
- Added comprehensive documentation with examples:
- Speed Limit Check example
- Age Verification with nested paths example
Created comprehensive test coverage with three test cases:
- Basic fact-to-fact comparison (distance vs limit)
- Fact-to-fact comparison with failure scenario
- Nested fact comparison with paths (user.age vs requirements.minimumAge)
Added a complete working example file demonstrating:
- Speed limit checking
- Age verification with nested paths
- Credit limit checking with multiple fact comparisons
$ruleConfig = [
"name" => "test.factToFact",
"conditions" => [
"all" => [
[
"fact" => "distance",
"operator" => "lessThanInclusive",
"value" => ["fact" => "limit"] // Reference another fact
]
]
],
"event" => ["type" => "passed", "params" => []],
"failureEvent" => ["type" => "failed", "params" => []]
];
$engine->addRule(new Rule($ruleConfig));
$engine->setTargetRule('test.factToFact');
$engine->addFact('distance', 40);
$engine->addFact('limit', 50);
$result = $engine->evaluate();
// Result: passed (40 <= 50)$ruleConfig = [
"conditions" => [
"all" => [
[
"fact" => "user",
"path" => "$.age",
"operator" => "greaterThanInclusive",
"value" => [
"fact" => "requirements",
"path" => "$.minimumAge"
]
]
]
]
];
$engine->addFact('user', ['age' => 25]);
$engine->addFact('requirements', ['minimumAge' => 18]);
// Compares: user.age (25) >= requirements.minimumAge (18)- Dynamic Thresholds: Comparison values can change based on context without modifying rule definitions
- Runtime Flexibility: Different scenarios can use different limits/thresholds
- Real-world Use Cases:
- Compare
currentSpeedtospeedLimit(where limit changes by zone) - Compare
userAgetominimumAge(where minimum might vary by country) - Compare
accountBalancetocreditLimit(personalized per user)
- Compare
All tests pass successfully:
./vendor/bin/phpunit tests/EngineTest.php
OK (8 tests, 32 assertions)This feature is fully backward compatible. Existing rules that use static values continue to work as before. The fact-to-fact comparison is only activated when the value is an array with a fact key.