When this, then that. The PHP Rules Engine
Define complex logic using a fluent, chainable API. Framework-agnostic core with platform-specific extensions.
Fluent Syntax
Chain conditions and actions with expressive, readable syntax that mirrors how you think about rules.
Framework Agnostic
Core engine works with any PHP application. No framework lock-in, just clean PHP.
Extensible
Add custom conditions, actions, and packages to fit your unique requirements.
Complex Logic, Simple Code
Define rules declaratively. No nested conditionals, no callback spaghetti — just clean, reusable logic that keeps your codebase DRY.
* Not built-in — you can create your own conditions and actions. Learn how below
How It Works
Pure PHP. No framework required. Rules collect as your code runs, then execute together on demand — works with any PHP application.
Rules Collect
After
MilliRules::init()
bootstraps the system, call
Rules::create()->register()
from anywhere in your code. Rules accumulate in the registry until you're ready
to run them.
Register a rule with the same ID to replace an existing one. Use
Rules::unregister()
to remove unwanted rules entirely.
Trigger Execution
Call
MilliRules::execute_rules()
to run all registered rules. Rules are sorted by
order()
before evaluation begins.
Rules with
->on('init')
bind to WordPress hooks and run automatically — no manual trigger needed.
Conditions Check
Each rule's conditions are evaluated. Context data is loaded lazily — only fetched when a condition actually needs it.
Actions Run
Matched rules execute their actions
in order
. Actions with
->lock()
prevent later rules from overriding the same action type.
Built to Extend
A minimal core that does one thing well. Framework packages add the rest.
Core Package
Pure PHP — No Dependencies
The rules engine itself: define, register, collect, and execute rules. Works with any PHP application — Laravel, Symfony, or vanilla PHP.
Provides
-
•
Fluent rule builder (
Rules::create(),->when(),->then()) - • HTTP-focused conditions (URL, method, headers, cookies)
-
•
Lazy-loaded
Contextobject for request data - • Package loading and auto-discovery
You call the shots:
Without a framework package, you decide when to execute rules via
MilliRules::execute_rules()
.
WordPress Package
Bundled & Ready to Use
Extends the core with WordPress integration. Includes useful conditions like
user_can()
and supports all WordPress
is_*()
and
has_*()
conditionals out of the box.
Adds
-
•
Hook binding
—
->on('init')attaches rules to WP hooks automatically -
•
WP conditions
—
is_singular(),user_can(), and many more -
•
Context providers
— user, post, query data via
$ctx->get('user.id') -
•
Placeholder resolvers
— use
{user.login}in action arguments
Zero setup:
The WP package is bundled and only loads when WordPress is detected. Use
->on()
or any WP condition — MilliRules registers it automatically.
Create Your Own Packages
The same architecture powers both the core and WP packages. Build a WooCommerce package, a Laravel package, or anything else.
Build Your Own Logic
Start with simple callbacks. Grow into reusable modules. Ship as packages.
Inline Callbacks
Define one-off conditions and actions directly in your rules. No registration needed — perfect for prototyping or logic you won't reuse.
Inline Condition
Inline Action
Instead of calling
$_GET
,
wp_get_current_user()
, or other globals scattered throughout your code, access everything through one consistent
interface:
$ctx->get('key')
.
Lazy-loaded:
data is only fetched when you actually access it. A rule checking
request.ip
won't trigger a database query for
user.roles
.
Example keys from built-in providers
request.method
"POST"
request.ip
"192.168.1.1"
param.action
"save"
env.APP_ENV
"production"
user.roles
["editor"]
user.login
"johndoe"
post.type
"product"
query.is_archive
true
See Rules in Action
MilliRules is the rule engine behind MilliCache, our Redis-powered full-page cache for WordPress. Every cache rule, TTL setting, and flag assignment uses the same fluent API.
- Define cache rules with WordPress conditionals
- Tag content with flags for surgical cache invalidation
- Control TTL per page type, user role, or any condition