aegisora / rule-contract
Contracts for rule-based validation in Aegisora ecosystem
Requires
- php: >=7.4
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^9.6
- squizlabs/php_codesniffer: ^4.0
README
Rule Contract defines the core abstractions for building validation rules in the Aegisora ecosystem.
It provides:
- a minimal, stable, and framework-agnostic contract that allows rules to be shared across packages and projects
- a strict contract for implementing rules
- consistent result handling
- unified exception management
✨ Features
- 🔹 Lightweight, framework-agnostic design and dependency-free
- 🔹 Stable contract for validation rules
- 🔹 Unified validation result structure
- 🔹 Safe exception handling with execution wrapping
- 🔹 Automatic rule code generation
- 🔹 Supports both simple and complex rules
- 🔹 Designed for extensibility
- 🔹 Compatible with Aegisora ecosystem (
guardian,rules, etc.)
📦 Installation
composer require aegisora/rule-contract
🚀 Core Concept
Each rule:
- receives a
Context - performs validation logic
- returns a
Result - never returns raw booleans
- never throws unstructured exceptions
This ensures predictable and testable validation flow.
🏗️ Basic Usage
Creating a Rule
Extend the abstract Rule class (simple example):
class UserAgeRule extends Rule { protected function executeValidate(Context $context): Result { $age = $context->getValue(); if ($age < 18) { return $this->getDefaultInvalidResult(); } return $this->getDefaultValidResult(); } }
Running a Rule
$rule = new UserAgeRule(); $result = $rule->validate(Context::create(20)); if ($result->isValid()) { // valid }
🏛️ Architecture
RuleInterface
Defines the contract for all rules:
validate(Context $context): Result
May throw:
InvalidRuleContextExceptionRuleExceptionRuleExecutionException
Rule (Abstract Class)
Base implementation that provides:
- Safe execution layer
- wraps execution in
try/catch - rethrows domain exceptions as-is
- wraps unexpected errors into
RuleExecutionException
- wraps execution in
- Default helpers
getDefaultValidResult()getDefaultInvalidResult()
- Automatic rule code generation - generates
snake_casecode from class name:UserAgeRule→user_age_rule
Execution Flow
validate()is calledexecuteValidate()runs- Result handling:
RuleException→ rethrownThrowable→ wrapped intoRuleExecutionException
Resultis returned
Context
Encapsulates input data for rule execution.
Context::create($value);
- stores
mixedvalue - provides
getValue()access
Used to decouple rules from application structures.
Result
Standardized validation result object.
Structure
isValid: boolfailedRuleCode: ?string
Factory methods
Result::valid()Result::invalid('rule_code')
Exception Handling
RuleException
Base exception for all rule-related errors.
InvalidRuleContextException
Thrown when context is invalid for a rule.
RuleExecutionException
Thrown when unexpected runtime error occurs during rule execution.
Contains:
- rule class name (
getRuleClassName()) - original exception
Design Principles
This package enforces:
- predictable execution flow
- strict separation of concerns
- consistent validation results
- safe error boundaries
- framework independence
- testable business rules
⚖️ License
This package is open-source and licensed under the MIT License. See the LICENSE for details.
🌱 Contributing
Contributions are welcome and greatly appreciated!. See the CONTRIBUTING for details.
🌟 Support
If you find this project useful, please consider giving it a star on GitHub!
It helps the project grow and motivates further development.