PHP code example of phpro / agent-rules

1. Go to this page and download the library: Download phpro/agent-rules library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

phpro / agent-rules example snippets


final class OrderRequest
{
    public function __construct(
        public readonly ?string $email,
        public readonly ?string $productId,
        public readonly ?int $quantity,
    ) {}
}

use Phpro\AgentRules\RuleInterface;
use Phpro\AgentRules\RuleEvaluation;
use Phpro\AgentRules\Result\IncompleteResult;

/**
 * @implements RuleInterface<OrderRequest>
 */
class EmailRule implements RuleInterface
{
    public function name(): string
    {
        return 'email_validation';
    }

    public function dependencies(): array
    {
        return []; // No dependencies
    }

    public function check(mixed $subject): RuleEvaluation
    {
        // Missing email? Ask for it!
        if ($subject->email === null) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: 'Please provide your email address to proceed.'
                )
            );
        }

        // Invalid format? Request correction!
        if (!filter_var($subject->email, FILTER_VALIDATE_EMAIL)) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: 'The email address is invalid. Please provide a valid email.'
                )
            );
        }

        // All good!
        return RuleEvaluation::pass();
    }
}

use Phpro\AgentRules\RuleEngine;

$engine = new RuleEngine(
    new EmailRule(),
    new ProductRule(),
    new QuantityRule(),
);

$request = new OrderRequest(
    email: null,
    productId: 'PROD-123',
    quantity: 2
);

$evaluation = $engine->evaluate($request);

if (!$evaluation->isPass()) {
    $result = $evaluation->result;
    echo $result->message; // "Please provide your email address to proceed."
    echo $result->missingField; // "email"
}

return RuleEvaluation::respond(
    new CompleteResult(
        message: 'Order validated successfully!'
    )
);

return RuleEvaluation::respond(
    new IncompleteResult(
        missingField: 'shippingAddress',
        message: 'We need your shipping address to complete the order.'
    )
);

return RuleEvaluation::respond(
    new BlockedResult(
        reason: 'Product out of stock',
        message: 'This product is currently unavailable.'
    )
);

return RuleEvaluation::respond(
    new ErrorResult(
        message: 'Unable to validate product availability.',
        resolution: 'Please try again later or contact support.'
    )
);

use Phpro\AgentRules\Sequence;

$allRequired = new Sequence(
    new EmailRule(),
    new ProductRule(),
    new QuantityRule(),
);

use Phpro\AgentRules\Any;

$paymentMethod = new Any(
    new CreditCardRule(),
    new PayPalRule(),
    new BankTransferRule(),
);

use Phpro\AgentRules\Either;

$authentication = new Either(
    new OAuthRule(),
    new PasswordRule(),
);

class DiscountRule implements RuleInterface
{
    public function dependencies(): array
    {
        return ['product_validation', 'user_authentication'];
    }

    public function name(): string
    {
        return 'discount_calculation';
    }

    public function check(mixed $subject): RuleEvaluation
    {
        // This runs only after dependencies pass
        // ...
    }
}

use Phpro\AgentRules\Source\Source;

$result = new IncompleteResult(
    missingField: 'productId',
    message: 'Please provide the product ID.'
);

$result->sources()->add(
    new Source(
        name: 'Product Catalog',
        reference: 'https://example.com/products',
        content: 'Browse our product catalog to find product IDs.'
    )
);

return RuleEvaluation::respond($result);

use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag('order.validation.rule', ['priority' => 100])]
class EmailRule implements RuleInterface
{
    // ...
}

use Symfony\AI\Platform\Contract\JsonSchema\Attribute\With;

final class RegistrationRequest
{
    public function __construct(
        public readonly ?string $email = null,
        #[With(minLength: 10, maxLength: 255)]
        #[\SensitiveParameter]
        public readonly ?string $password = null,
        public readonly ?string $fullName = null,
        public readonly ?bool $termsAccepted = null,
    ) {}
}

#[AutoconfigureTag('registration.rule', ['priority' => 100])]
class EmailRule implements RuleInterface
{
    public function name(): string { return 'email'; }
    public function dependencies(): array { return []; }

    public function check(mixed $subject): RuleEvaluation
    {
        if (!$subject->email) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: "What's your email address?"
                )
            );
        }

        if (!filter_var($subject->email, FILTER_VALIDATE_EMAIL)) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'email',
                    message: 'Please provide a valid email address.'
                )
            );
        }

        return RuleEvaluation::pass();
    }
}

#[AutoconfigureTag('registration.rule', ['priority' => 90])]
class PasswordRule implements RuleInterface
{
    public function name(): string { return 'password'; }
    public function dependencies(): array { return ['email']; }

    public function check(mixed $subject): RuleEvaluation
    {
        if (!$subject->password) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'password',
                    message: 'Please create a password (minimum 8 characters).'
                )
            );
        }

        if (strlen($subject->password) < 8) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'password',
                    message: 'Password must be at least 8 characters long.'
                )
            );
        }

        return RuleEvaluation::pass();
    }
}

#[AsTool(
    name: 'validate_registration',
    description: 'Validates user registration data and requests missing information.'
)]
final class RegistrationTool implements HasSourcesInterface
{
    use HasSourcesTrait;

    public function __construct(
        #[Autowire(service: 'RegistrationEngine')]
        private RuleEngine $ruleEngine,
    ) {}

    public function __invoke(
        RegistrationRequest $request,
    ): ResultInterface {
        $evaluation = $this->ruleEngine->evaluate($request);
        $result = $evaluation->result ?? new CompleteResult(
            message: 'Registration validated! Your account has been created.'
        );
        
        foreach ($result->sources() as $source) {
            $this->addSource(new \Symfony\AI\Agent\Toolbox\Source\Source(
                name: $source->name,
                reference: $source->reference,
                content: $source->content
            ));
        }
        
        return $result;
    }
}

class ConditionalShippingRule implements RuleInterface
{
    public function check(mixed $subject): RuleEvaluation
    {
        // Only check shipping if physical product
        if ($subject->productType !== 'physical') {
            return RuleEvaluation::pass();
        }

        if (!$subject->shippingAddress) {
            return RuleEvaluation::respond(
                new IncompleteResult(
                    missingField: 'shippingAddress',
                    message: 'Physical products 

$checkoutValidation = new Sequence(
    new UserAuthenticationRule(),
    new Any(
        new GuestCheckoutRule(),
        new AccountRequiredRule(),
    ),
    new Either(
        new ExpressCheckoutRule(),
        new Sequence(
            new ShippingRule(),
            new PaymentRule(),
            new BillingRule(),
        ),
    ),
);

class InventoryRule implements RuleInterface
{
    public function __construct(
        private ProductRepository $products,
    ) {}

    public function check(mixed $subject): RuleEvaluation
    {
        $product = $this->products->find($subject->productId);

        if (!$product || $product->stock < $subject->quantity) {
            return RuleEvaluation::respond(
                new BlockedResult(
                    reason: 'Insufficient inventory',
                    message: 'This product is out of stock.'
                )
            );
        }
        
        // You could even save products based on the subject input.

        return RuleEvaluation::pass();
    }
}

use PHPUnit\Framework\TestCase;

class EmailRuleTest extends TestCase
{
    public function test_it_requests_missing_email(): void
    {
        $rule = new EmailRule();
        $request = new OrderRequest(email: null, productId: 'P1', quantity: 1);

        $evaluation = $rule->check($request);

        $this->assertFalse($evaluation->isPass());
        $this->assertInstanceOf(IncompleteResult::class, $evaluation->result);
        $this->assertEquals('email', $evaluation->result->missingField);
    }

    public function test_it_validates_email_format(): void
    {
        $rule = new EmailRule();
        $request = new OrderRequest(email: 'invalid-email', productId: 'P1', quantity: 1);

        $evaluation = $rule->check($request);

        $this->assertFalse($evaluation->isPass());
        $this->assertStringContainsString('valid email', $evaluation->result->message);
    }

    public function test_it_passes_with_valid_email(): void
    {
        $rule = new EmailRule();
        $request = new OrderRequest(email: '[email protected]', productId: 'P1', quantity: 1);

        $evaluation = $rule->check($request);

        $this->assertTrue($evaluation->isPass());
    }
}