Conditions
A lightweight, framework-agnostic Business Rule Evaluation Engine for PHP.
Obelaw Conditions
A lightweight, framework-agnostic Business Rule Evaluation Engine for PHP.
obelaw/conditions gives you a clean, elegant, and highly readable way to encapsulate complex business logic and evaluate rules dynamically. It’s perfectly suited for ERP systems, large applications, and for decoupling UI logic from core business rules.
Design Patterns Under the Hood
The package is inspired by enterprise software architecture and implements several proven design patterns:
- Specification Pattern — The heart of the package. Every business rule lives in its own dedicated class that extends the
Conditionbase class and implementsisSatisfiedBy(). This keeps each rule focused and honors the Single Responsibility Principle. - Fluent Interface / Builder Pattern — The
ConditionBususes method chaining (is(),and(),or(),not()) to build complex logical queries that read like natural English. - Composite Pattern (Evaluator) — The
ConditionBusacts as a composite evaluator, combining multiple isolatedConditionobjects into a single executable logical tree.
Installation
Install the package via Composer:
composer require obelaw/conditions
Requirements:
- PHP 8.1 or higher
- Composer 2
Getting Started
1. Create a Condition
Every business rule is a dedicated class extending Obelaw\Conditions\Condition:
<?php
namespace App\Conditions;
use Obelaw\Conditions\Condition;
class IsPendingCondition extends Condition
{
public function __construct(
protected string $status = 'pending',
) {
}
public function isSatisfiedBy(mixed $candidate): bool
{
return $candidate->status === $this->status;
}
}
A condition is a single, focused, testable unit of business logic.
2. Evaluate Rules with the ConditionBus
Collect conditions and evaluate them against a candidate in one fluent chain:
use Obelaw\Conditions\ConditionBus;
use App\Conditions\IsPendingCondition;
use App\Conditions\HasValidVatNumberCondition;
use App\Conditions\IsOverdueCondition;
use App\Conditions\IsVoidCondition;
$canCancel = ConditionBus::make()
->is(new IsPendingCondition())
->and(new HasValidVatNumberCondition())
->or(new IsOverdueCondition())
->not(new IsVoidCondition())
->evaluate($order); // bool
3. Compose Conditions Directly
Condition itself also exposes is(), and(), or(), and not() so you can build standalone, reusable rule trees that read like English:
use App\Conditions\IsPendingCondition;
use App\Conditions\IsApprovedCondition;
use App\Conditions\IsOverdueCondition;
$actionable = (new IsPendingCondition())
->and(new IsApprovedCondition())
->or(new IsOverdueCondition())
->not();
$actionable->isSatisfiedBy($order); // bool
Because composed conditions are still Condition objects, you can freely mix both styles:
$actionable = (new IsPendingCondition())
->or(new IsOverdueCondition());
ConditionBus::make()
->is($actionable)
->not(new IsVoidCondition())
->evaluate($order);
Operators Reference
ConditionBus
| Method | Behaviour |
|---|---|
make() | Returns a new bus instance (an empty bus evaluates to true). |
is(Condition $c) | Adds a condition as the initial one, or as an AND. |
and(Condition $c) | Alias of is(). |
or(Condition $c) | Adds a condition as an OR. |
not(Condition $c) | Adds a condition as an AND NOT. |
evaluate(mixed $candidate) | Runs all conditions and returns the final bool. |
Condition Composition
| Method | Behaviour |
|---|---|
is(Condition $c) | Alias of and(). |
and(Condition $c) | Returns an AndCondition (both must pass). |
or(Condition $c) | Returns an OrCondition (either may pass). |
not() | Returns a NotCondition (negates the wrapped condition). |
Evaluation runs left-to-right, and both the bus and composite conditions short-circuit — later conditions are skipped once the result is already decided.
Testing
composer test
The test suite (written with Pest) verifies the truth tables of is, and, or, and not, fluent chaining, short-circuiting, and composition:
Tests: 12 passed (32 assertions)
Contributing
Feel free to open issues or submit pull requests on the repository.
License
This package is open-sourced software licensed under the MIT license.