Skip to content

About

Framework-neutral PHP eligibility rules and typed decisions with reusable rule management, evaluation, PDO persistence, and transaction-safe integrations.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

24 Commits

Folders and files

Repository files navigation

Maatify Eligibility

Maatify.dev

Package status:
Release state Version PHP License PHPStan

Registry and usage:
Packagist Monthly Downloads Total Downloads Maatify Ecosystem Install

Documentation:
Changelog Package Reference Usage Guide Examples Schema Security Policy Contributing Guide

Framework-neutral eligibility rules and typed decisions that answer one reusable business question about an external Subject in a supplied Context.


Package Summary

maatify/php-eligibility is a standalone, framework-neutral PHP library that answers "Is a given Subject eligible in the supplied Context?" for domains such as products, categories, payment methods, shipping, and promotions, so those domains do not each re-invent a customer/country restriction subsystem.

Host Input -> Public API -> Domain Service -> Integration Boundary -> Observable Result

The package knows external identities only. It does not know the database model, lifecycle, or implementation of the domains that own those identities.

The Package Reference documents the current API. See the RC.3 tag for the documentation shipped with the published release.

Status

Key Features

  • Immutable, typed Subject, Context, Rule, and EligibilityDecision values with canonical UTF-8 string validation and bytewise canonical order.
  • Deterministic rule evaluation: ALLOW/DENY effects, active/inactive lifecycle, DENY precedence, ALLOW-list and DENY-only semantics, AND across dimensions, and no inferred cross-dimension or cross-Subject behavior.
  • Fully typed decision output: EligibilityDecision, per-dimension DimensionOutcome, complete RuleReference traces, and stable machine reason codes.
  • Single and ordered batch evaluation with duplicate-Subject rejection, empty batch support, and a bounded bulk load path (no canonical N+1 query pattern).
  • Management lifecycle: create, paginated inspect/list (including inactive Rules, with optional dimension/lifecycle/effect filters), paginated active-dimension introspection, a Rule lifecycle count summary, effect and lifecycle mutations, atomic replaceDimensionRules(), and idempotent Subject cleanup.
  • Direct-PDO MySQL-compatible persistence with separate command, management query, and evaluation-read adapters, plus shared Persistence transaction, savepoint, and pagination mechanics and Eligibility-owned coordination locking; no Host foreign keys or joins.
  • Typed package exceptions on the shared maatify/exceptions hierarchy, including classification of malformed persisted Rule state, with unknown external throwables propagated unchanged.

Requirements

Requirement Constraint
PHP 8.4.27+ on PHP 8.4 or 8.5.12+ on PHP 8.5
PHP extensions ext-pdo, ext-pdo_mysql, ext-pcre
Runtime packages maatify/exceptions (^1.0), maatify/php-persistence (1.0.0-rc.1)
Database MySQL-compatible database-server semantics through direct PDO (capability-based; no minimum product version is declared — see Persistence and Schema)

Published Eligibility 1.0.0-rc.3 uses maatify/php-persistence 1.0.0-rc.1 for SavepointTransactionRunnerInterface and PdoSavepointTransactionRunner. Eligibility delegates transaction ownership, operation-local savepoints, cleanup, and original-Throwable preservation to that shared API.

The database contract requires transactional InnoDB-style package-owned table behavior, binary-safe exact-value storage/comparison, the bounded indexed-key capacity of this package's schema, and the uniqueness/index semantics the schema uses. Being labelled MySQL-compatible is not enough; the server must provide those capabilities. MariaDB compatibility is not claimed without executed MariaDB verification.

Installation

Install 1.0.0-rc.3 from Packagist. The root project explicitly allows the Persistence RC while keeping Composer's default minimum stability.

composer require maatify/php-eligibility:1.0.0-rc.3@RC maatify/php-persistence:1.0.0-rc.1@RC

For RC.2, use the published RC.2 guide and its PHP ^8.4 requirements.

Development access

For development access only, use a local checkout of the development branch through a Composer path repository. The checkout examples below use the new shared Persistence API. From your consumer project:

git clone https://github.com/Maatify-ltd/php-eligibility.git .tools/php-eligibility
# Check out the development revision you want to test.
composer config repositories.php-eligibility '{"type":"path","url":".tools/php-eligibility","options":{"symlink":true,"versions":{"maatify/php-eligibility":"dev-local"}}}'
composer require maatify/php-eligibility:dev-local maatify/php-persistence:1.0.0-rc.1@RC

The dev-local identity comes from the path repository configuration. For developing the library itself (running the full local parity suite), clone into a working directory and follow Development and Testing.

Installation (both paths) requires ext-pdo, ext-pdo_mysql, and ext-pcre. The package performs no automatic setup: the schema asset is an install asset (schema/eligibility_rules.sql), not a migration run at install time.

Quick Usage

Evaluation

use Maatify\Eligibility\Factory\Pdo\PdoEligibilityRuntimeFactory;
use Maatify\Eligibility\Enum\RuleEffectEnum;
use Maatify\Eligibility\Evaluation\ValueObject\Context;
use Maatify\Eligibility\Evaluation\ValueObject\ContextDimension;
use Maatify\Eligibility\ValueObject\Subject;

$pdo = new PDO('mysql:host=127.0.0.1;dbname=app;charset=utf8mb4', 'app', 'secret', [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES => false,
]);
$pdo->exec(file_get_contents(__DIR__ . '/vendor/maatify/php-eligibility/schema/eligibility_rules.sql'));

$factory = new PdoEligibilityRuntimeFactory($pdo);
$management = $factory->createManagementService();
$evaluation = $factory->createEvaluationService();

$subject = new Subject('product', '150');
$management->createRule(new \Maatify\Eligibility\Management\Command\CreateRuleCommand(
    $subject,
    'country',
    'EG',
    RuleEffectEnum::ALLOW,
));
$management->createRule(new \Maatify\Eligibility\Management\Command\CreateRuleCommand(
    $subject,
    'customer_type',
    'blocked',
    RuleEffectEnum::DENY,
));

$decision = $evaluation->decide($subject, new Context(
    ContextDimension::fromStrings('country', 'EG'),
    ContextDimension::fromStrings('customer_type', 'retail'),
));

if ($decision->eligible) {
    // $decision->reasonCode is DecisionReasonEnum::ELIGIBLE
}

See the Usage Guide for batch evaluation and management, including pagination and atomic rule replacement.

Public Runtime API

Public surface (see the Package Reference for the full contract):

  • Values: Subject, SubjectCollection, Context, ContextDimension, ContextValue, ContextValueCollection, Rule, RuleIdentity, RuleCollection, RuleEffectEnum, RuleLifecycleEnum, CanonicalString.
  • Decisions: EligibilityDecision, DimensionOutcome, DimensionOutcomeCollection, RuleReference, RuleReferenceCollection, DecisionReasonEnum (UNRESTRICTED, ELIGIBLE, DENIED), DimensionReasonEnum.
  • Evaluation service: EligibilityEvaluationService / EligibilityEvaluationServiceInterface — decide() and decideMany().
  • Management service: EligibilityManagementService / EligibilityManagementServiceInterface — createRule(), inspectRule(), inspectRules() (paginated), inspectActiveDimensionKeys() (paginated), inspectRuleLifecycleSummary(), updateRuleEffect(), deactivateRule(), reactivateRule(), replaceDimensionRules(), cleanupSubject().
  • Persistence contracts: RuleCommandRepositoryInterface owns command mutations, RuleManagementQueryInterface owns paginated management reads and the lifecycle summary aggregate, and ActiveRuleReaderInterface owns active bulk reads for evaluation. The internal RuleMutationSupportInterface owns only the coordination lock, complete Subject + dimension mutation read, and coordination cleanup needed by atomic replacement/cleanup. Generic transaction/savepoint and pagination mechanics are owned by maatify/php-persistence (SavepointTransactionRunnerInterface, PageRequest, PageResultDTO, PdoPaginator); Eligibility owns only its domain filter/count SQL and row mapping.
  • Default PDO construction: PdoEligibilityRuntimeFactory accepts one caller-owned PDO and returns the Evaluation and Management service interfaces. It creates all default adapters and the savepoint runner over that same PDO; it does not create connections, apply schema, own credentials, or expose the internal mutation-support contract.
  • PDO adapters: PdoRuleCommandRepository, PdoRuleManagementQuery, PdoActiveRuleReader, and PdoSavepointTransactionRunner remain available for advanced explicit composition. The command adapter implements the Eligibility command and mutation-support contracts; ordinary consumers should use the factory.
  • Commands / queries / results: CreateRuleCommand, UpdateRuleEffectCommand, DeactivateRuleCommand, ReactivateRuleCommand, DesiredRule, DesiredRuleCollection, ReplaceDimensionRulesCommand, CleanupSubjectCommand, RuleCriteria (subject/dimension/lifecycle/effect filters), ActiveDimensionKeysCriteria, ActiveDimensionKeyDTO, RuleLifecycleSummaryCriteria, RuleLifecycleSummaryDTO, SubjectDecisionDTO, SubjectDecisionCollectionDTO.
  • Exceptions: EligibilityExceptionInterface, InvalidEligibilityInputException, RuleNotFoundException, RuleIdentityConflictException, RuleConcurrencyConflictException, InvalidPersistedRuleStateException (backed by the shared maatify/exceptions hierarchy). Unknown/external storage failures (for example an unclassified PDOException) propagate unchanged rather than being forced into the Eligibility marker.

Host responsibilities

Apply the schema before creating the runtime. The Host owns access control, tenant isolation, PDO configuration, and any outer transaction. See the Package Reference for rule evaluation, transaction participation, and the typed errors returned by the package.

Documentation

The Usage Guide contains the capability decision map and helps a consumer choose the smallest suitable public API surface before installing or trying the package.

Document Purpose
Package Reference Canonical package contract: identity, Context, Rule, Decision, lifecycle, ordering, pagination, lifecycle summary, persistence, transaction, concurrency, error, batch, and 60-scenario coverage.
Usage Guide Consumer-facing API guide, capability decision map, input/output types, transaction notes, and links to runnable examples.
Runnable Examples Standalone public-API examples for evaluation, batch evaluation, management, replacement, PDO wiring, and typed exception handling.
Schema Persistence contract, tables, bounds, applying/reapplying, and the local MySQL fixture.
CHANGELOG Published releases and compatibility changes.
Security Policy Support state, vulnerability reporting, and scope.
Contributing Guide Contribution expectations, local verification, and PR requirements.
Code of Conduct Community rules and reporting.

Persistence and Schema

The package owns two tables with the maa_eligibility_ prefix:

  • maa_eligibility_rules — Rules with the exact-string-safe VARBINARY columns and a 574-byte natural-identity unique key.
  • maa_eligibility_subject_locks — package-owned coordination rows used by replacement and cleanup to serialize per-Subject mutations.

See schema/README.md for bounds, storage guarantees, transaction/savepoint behavior, and the local mysql:8.4.11 reproducibility fixture. The fixture version is not a minimum supported product version.

The default runtime composition keeps command, management-query, evaluation-read, and internal mutation-support responsibilities explicit inside PdoEligibilityRuntimeFactory. The Host creates/configures the PDO, applies the schema, and may own an outer transaction; the factory builds both service graphs over that exact PDO. Direct adapter/service construction remains an advanced extension path. Generic transaction/savepoint mechanics belong to maatify/php-persistence; per-Subject coordination locking remains Eligibility-owned.

Quality Status

  • PHPStan level max, zero errors, no baseline and no suppressions.
  • Unit, Golden (60 canonical acceptance scenarios with an executable evidence map), real-MySQL Integration, and concurrency/invariant suites.
  • Real-service Integration is run on both supported PHP minors with a repeated run for cleanup/repeatability evidence; there is no SQLite or mock substitute.
  • Consumer Verification Harness: two clean external-consumer runs using production autoload, public contracts, and the real persistence boundary.
  • Fail-closed CI with stable aggregate gates: ci-quality, ci-tests, ci-integration.

Development and Testing

Prerequisites: PHP 8.4.27+ on PHP 8.4 or 8.5.12+ on PHP 8.5, Composer, Docker (for the real MySQL fixture).

The repository development and verification workflow requires Composer 2.10.x; this is a tooling policy only and is not a runtime or consumer install requirement. Verify it before resolving dependencies:

composer --version | grep -Eq '^Composer version 2\.10\.'

Resolve the latest-compatible dependencies and run the local aggregate gate:

composer update --no-interaction --prefer-dist --no-progress
tools/check-local.sh                     # Docker-free local gates, Unit, and Golden
tools/check-local.sh --with-integration  # adds fresh Integration, Harness, and examples lifecycles
composer test:integration                # focused fresh real-MySQL Integration lifecycle
composer test:harness                     # focused fresh Consumer Harness lifecycle
composer test:examples                     # focused fresh standalone-example smoke lifecycle

The maintained verification commands require Composer 2.10.x, a working Docker daemon with docker compose, PHP 8.4.27+ on PHP 8.4 or 8.5.12+ on PHP 8.5, and pdo_mysql. Each real service command provisions and tears down its own disposable Compose project with a dynamic loopback-only MySQL port; manual service startup is not needed.

CI checks the actual PHP patch floor with php tools/check-php-floor.php on PHP 8.4 and 8.5. The contributing guide documents the latest-compatible and lowest-supported dependency runs.

License

Use requires a separate written license from Maatify. See LICENSE.

Author

Engineered by Mohamed Abdulalim (@megyptm)
Backend Lead & Technical Architect
https://www.maatify.dev


About

Framework-neutral PHP eligibility rules and typed decisions with reusable rule management, evaluation, PDO persistence, and transaction-safe integrations.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages