定制 aegisora/guardian 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

aegisora/guardian

最新稳定版本:v1.0.0

Composer 安装命令:

composer require aegisora/guardian

包简介

Lightweight validation rule execution orchestrator for the Aegisora ecosystem

README 文档

README

Code Coverage Badge Software License PHPStan Badge

Aegisora Guardian is a lightweight and extensible validation orchestrator for the Aegisora ecosystem.

The package provides a fluent and predictable way to execute validation rules against arbitrary values using the standardized architecture defined in aegisora/rule-contract.

Guardian focuses on:

  • centralized validation execution
  • structured validation results
  • custom exception handling
  • sequential rule pipelines
  • clean and framework-agnostic architecture

The library is intentionally minimalistic while still being powerful enough to build complex domain validation layers.

✨ Features

Fluent Validation Pipeline

Guardian allows validation rules to be chained together in a readable and expressive way.

$guardian
    ->that($value)
    ->must(FirstRule::create())
    ->must(SecondRule::create())
    ->validate();

This makes complex validation scenarios easy to maintain and extend.

Rule-Based Validation

Validation logic is completely separated into independent rule objects implementing:

Aegisora\RuleContract\RuleInterface

This promotes:

  • single responsibility
  • reusable validation logic
  • domain-oriented architecture
  • testability

Structured Validation Flow

Guardian follows a strict validation flow:

Value → Context → Rule → Result → Exception

Each rule receives a Context object and returns a standardized Result object.

No raw booleans are used internally.

Custom Exception Handling

Every rule can define its own exception.

This enables precise domain-level error handling:

->must(
    EmailRule::create(),
    new InvalidEmailException()
)

Lightweight Architecture

The package has:

  • no framework dependencies
  • minimal memory overhead
  • simple object graph
  • predictable behavior

Guardian can be used in:

  • Laravel
  • Symfony
  • Slim
  • custom PHP applications
  • microservices
  • domain-driven projects

Safe Rule Execution

Internal rule execution is protected from unexpected contract-level failures.

If a rule throws RuleException, Guardian converts it into:

GuardianExecutingRuleException

This keeps the validation layer consistent and isolated.

📦 Installation

Install the package via Composer:

composer require aegisora/guardian

🔗 Related Packages

Guardian is part of the broader Aegisora ecosystem.

Several ready-to-use validation rules are already implemented as separate packages and can be found in the official Aegisora repositories:

https://github.com/orgs/Aegisora/repositories

Rule repositories follow a consistent naming convention:

*-rule

Examples:

These packages provide reusable validation logic fully compatible with Guardian and aegisora/rule-contract.

The ecosystem is designed to allow combining independent validation rules into flexible and composable validation pipelines.

📚 Requirements

Required Package

Guardian relies on:

aegisora/rule-contract

which provides:

  • RuleInterface
  • Context
  • Result
  • RuleException

🚀 Core Concept

Guardian acts as a validation execution engine.

Instead of manually invoking validation logic throughout the application, Guardian centralizes the process and provides a consistent execution model.

Validation flow:

  1. A value is passed into Guardian
  2. Validation rules are attached
  3. Guardian creates a Context
  4. Rules are executed sequentially
  5. Every rule returns a Result
  6. Validation failures trigger exceptions

This architecture makes validation:

  • predictable
  • composable
  • extensible
  • easy to debug
  • easy to test

🏗️ Basic Usage

Simple Validation

The simplest way to validate a value is using check().

use Aegisora\Guardian\Guardian;

$guardian = new Guardian();

$guardian->check(
    $value,
    FooRule::create()
);

If validation passes, execution continues normally.

If validation fails, Guardian throws:

GuardianValidationException

Validation with Custom Exception

You may provide a custom exception for validation failures.

use Aegisora\Guardian\Guardian;
use App\Exceptions\InvalidValueException;

$guardian = new Guardian();

$guardian->check(
    $value,
    FooRule::create(),
    new InvalidValueException()
);

If validation fails, the provided exception will be thrown instead of the default Guardian exception.

This is especially useful in:

  • domain-driven design
  • business validation layers
  • API validation
  • application services

🔗 Fluent Validation API

Guardian supports fluent rule chaining through GuardianExecutor.

Example:

use Aegisora\Guardian\Guardian;

$guardian = new Guardian();

$guardian
    ->that($value)
    ->must(FirstRule::create())
    ->must(SecondRule::create())
    ->validate();

Execution behavior:

  • rules execute sequentially
  • validation stops on first failure
  • exceptions are thrown immediately
  • successful rules continue pipeline execution

Multiple Rules with Different Exceptions

Each rule may define its own exception.

$guardian
    ->that($value)
    ->must(
        EmailRule::create(),
        new InvalidEmailException()
    )
    ->must(
        PasswordRule::create(),
        new WeakPasswordException()
    )
    ->validate();

This enables extremely precise validation semantics.

🧩 Real-World Example: User Registration Validation

Guardian can be used in an application service to validate incoming data before executing business logic.

In this example, a user registration command is validated by three independent rules:

  • email must have a valid format
  • password must be strong enough
  • user must be at least 18 years old

Guardian does not contain validation logic itself. It receives a value, passes it into rules through Context, executes rules one by one, and stops on the first failed rule.

Registration Command

The command contains the data required to register a user.

<?php

namespace App\Registration;

final class RegisterUserCommand
{
    private string $email;
    private string $password;
    private int $age;

    public function __construct(
        string $email,
        string $password,
        int $age
    ) {
        $this->email = $email;
        $this->password = $password;
        $this->age = $age;
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function getPassword(): string
    {
        return $this->password;
    }

    public function getAge(): int
    {
        return $this->age;
    }
}

Email Rule

This rule checks that the command contains a valid email address.

<?php

namespace App\Registration\Rules;

use Aegisora\RuleContract\Models\Context;
use Aegisora\RuleContract\Models\Result;
use Aegisora\RuleContract\Rule;
use App\Registration\RegisterUserCommand;

final class ValidEmailRule extends Rule
{
    protected function executeValidate(Context $context): Result
    {
        $command = $context->getValue();

        if (!$command instanceof RegisterUserCommand) {
            return Result::invalid('invalid_registration_command');
        }

        if (filter_var($command->getEmail(), FILTER_VALIDATE_EMAIL) === false) {
            return Result::invalid('invalid_email');
        }

        return Result::valid();
    }
}

Password Rule

This rule checks that the password is at least 12 characters long and contains both an uppercase letter and a number.

<?php

namespace App\Registration\Rules;

use Aegisora\RuleContract\Models\Context;
use Aegisora\RuleContract\Models\Result;
use Aegisora\RuleContract\Rule;
use App\Registration\RegisterUserCommand;

final class StrongPasswordRule extends Rule
{
    protected function executeValidate(Context $context): Result
    {
        $command = $context->getValue();

        if (!$command instanceof RegisterUserCommand) {
            return Result::invalid('invalid_registration_command');
        }

        $password = $command->getPassword();

        if (strlen($password) < 12) {
            return Result::invalid('weak_password');
        }

        if (preg_match('/[A-Z]/', $password) !== 1) {
            return Result::invalid('weak_password');
        }

        if (preg_match('/[0-9]/', $password) !== 1) {
            return Result::invalid('weak_password');
        }

        return Result::valid();
    }
}

Age Rule

This rule checks that the user is old enough to register.

<?php

namespace App\Registration\Rules;

use Aegisora\RuleContract\Models\Context;
use Aegisora\RuleContract\Models\Result;
use Aegisora\RuleContract\Rule;
use App\Registration\RegisterUserCommand;

final class AdultUserRule extends Rule
{
    private const MINIMUM_AGE = 18;

    protected function executeValidate(Context $context): Result
    {
        $command = $context->getValue();

        if (!$command instanceof RegisterUserCommand) {
            return Result::invalid('invalid_registration_command');
        }

        if ($command->getAge() < self::MINIMUM_AGE) {
            return Result::invalid('user_is_too_young');
        }

        return Result::valid();
    }
}

Application Service

The service describes the validation pipeline. Each rule may have its own domain exception.

<?php

namespace App\Registration;

use Aegisora\Guardian\Guardian;
use App\Registration\Exceptions\InvalidEmailException;
use App\Registration\Exceptions\UserIsTooYoungException;
use App\Registration\Exceptions\WeakPasswordException;
use App\Registration\Rules\AdultUserRule;
use App\Registration\Rules\StrongPasswordRule;
use App\Registration\Rules\ValidEmailRule;

final class RegisterUserService
{
    private Guardian $guardian;
    private UserRepository $users;

    public function __construct(
        Guardian $guardian,
        UserRepository $users
    ) {
        $this->guardian = $guardian;
        $this->users = $users;
    }

    public function register(RegisterUserCommand $command): User
    {
        $this->guardian
            ->that($command)
            ->must(
                new ValidEmailRule(),
                new InvalidEmailException()
            )
            ->must(
                new StrongPasswordRule(),
                new WeakPasswordException()
            )
            ->must(
                new AdultUserRule(),
                new UserIsTooYoungException()
            )
            ->validate();

        return $this->users->create(
            $command->getEmail(),
            password_hash($command->getPassword(), PASSWORD_DEFAULT),
            $command->getAge()
        );
    }
}

Usage

<?php

use Aegisora\Guardian\Exceptions\GuardianExecutingRuleException;
use App\Registration\Exceptions\InvalidEmailException;
use App\Registration\Exceptions\UserIsTooYoungException;
use App\Registration\Exceptions\WeakPasswordException;
use App\Registration\RegisterUserCommand;

try {
    $user = $registerUserService->register(
        new RegisterUserCommand(
            'john@example.com',
            'SecurePassword123',
            25
        )
    );

    return [
        'status' => 201,
        'user_id' => $user->getId(),
    ];
} catch (InvalidEmailException $exception) {
    return [
        'status' => 422,
        'error' => 'Invalid email address.',
    ];
} catch (WeakPasswordException $exception) {
    return [
        'status' => 422,
        'error' => 'Password is too weak.',
    ];
} catch (UserIsTooYoungException $exception) {
    return [
        'status' => 422,
        'error' => 'User must be at least 18 years old.',
    ];
} catch (GuardianExecutingRuleException $exception) {
    return [
        'status' => 500,
        'error' => 'Validation rule execution failed.',
    ];
}

If all rules pass, the user is created.

If one rule fails, Guardian immediately stops execution and throws the exception attached to that rule.

For example, if ValidEmailRule fails, InvalidEmailException is thrown. StrongPasswordRule and AdultUserRule will not be executed.

If a rule fails internally and throws RuleException, Guardian converts it into GuardianExecutingRuleException, keeping rule execution errors separate from business validation errors.

🏛️ Architecture

Guardian contains several small and focused components.

Guardian

Main entry point for validation operations.

Responsibilities:

  • starts validation process
  • creates validation pipeline
  • provides simple API
  • delegates execution to GuardianExecutor

Methods:

check($value, RuleInterface $rule, Throwable $exception = null): void
that($value): GuardianExecutor

GuardianExecutor

Core validation execution engine.

Responsibilities:

  • stores validation rules
  • creates validation context
  • executes rules sequentially
  • analyzes validation results
  • throws exceptions

Methods:

must(RuleInterface $rule, Throwable $exception = null): self
validate(): void

GuardRule

Immutable transport object containing:

  • validation rule
  • optional exception

Purpose:

  • encapsulates validation configuration
  • keeps executor implementation clean

GuardValue

Simple wrapper around arbitrary validated value.

Purpose:

  • encapsulates raw input value
  • centralizes value transport

⚙️ Validation Lifecycle

Complete internal validation lifecycle:

Guardian::check()
        ↓
GuardianExecutor
        ↓
Context::create($value)
        ↓
Rule::validate(Context)
        ↓
Result
        ↓
Validation Analysis
        ↓
Exception or Success

🚨 Exceptions

Guardian defines dedicated exceptions for predictable error handling.

GuardianValidationException

Thrown when validation fails and no custom exception is provided.

Example:

throw new GuardianValidationException(
    $failedRuleCode
);

The exception contains the failed rule code returned by Result.

GuardianExecutingRuleException

Thrown when internal rule execution fails due to contract-level exceptions.

Example scenario:

RuleException → GuardianExecutingRuleException

This protects application code from low-level rule implementation failures.

🧠 Validation Philosophy

Guardian follows several important principles.

Explicit Validation

Validation should be visible and explicit.

Bad:

if (!$value) {
}

Good:

$guardian->check($value, SomeRule::create());

Domain-Oriented Exceptions

Validation errors should communicate domain meaning.

Example:

new InvalidUserEmailException()

instead of generic runtime errors.

Structured Result Objects

Rules should return standardized objects instead of booleans.

This enables:

  • extensibility
  • debugging
  • metadata support
  • consistent validation behavior

Separation of Concerns

Guardian separates:

  • validation execution
  • validation rules
  • result analysis
  • exception handling

This keeps code maintainable and scalable.

⚖️ 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 of the Aegisora ecosystem.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-11

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固