Complete API Reference
This comprehensive API reference documents all public classes, methods, interfaces, and functions in MilliRules.
Table of Contents#Copied!
Core Classes#Copied!
MilliRules#Copied!
Main entry point for initializing and executing rules.
Namespace: MilliRules
Methods
init(?array $package_names = null, ?array $packages = null): array
Initialize MilliRules and load packages.
Parameters:
$package_names(array|null): Array of package names to load (null = auto-load all available)$packages(array|null): Array of PackageInterface instances to register (null = register defaults)
Returns: array - Array of loaded package names
Example:
1// Auto-load available packages
2MilliRules::init();
3
4// Load specific packages by name
5MilliRules::init(['PHP', 'WP']);
6
7// Load with custom package instances
8$custom = new CustomPackage();
9MilliRules::init(null, [$custom]);
execute_rules(?array $allowed_packages = null, array $context = []): array
Execute all registered rules.
Parameters:
$allowed_packages(array|null): Array of package names to use (null = all loaded)$context(array): Additional context data (merges with auto-built context)
Returns: array - Execution result with statistics
1[
2 'rules_processed' => int, // Total rules evaluated
3 'rules_skipped' => int, // Rules skipped
4 'rules_matched' => int, // Rules with matching conditions
5 'actions_executed' => int, // Actions executed
6 'context' => array, // Execution context
7]
Example:
1// Execute all rules
2$result = MilliRules::execute_rules();
3
4// Execute only PHP rules
5$result = MilliRules::execute_rules(['PHP']);
6
7// Execute with custom context
8$result = MilliRules::execute_rules(null, ['custom_data' => 'value']);
get_loaded_packages(): array
Get names of all loaded packages.
Returns: array - Array of package names
Example:
1$packages = MilliRules::get_loaded_packages();
2// ['PHP', 'WP']
build_context(): array
Build context from all loaded packages.
Returns: array - Aggregated context data
Example:
1$context = MilliRules::build_context();
2/*
3[
4 'request' => [...],
5 'wp' => [...],
6]
7*/
Rules#Copied!
Fluent interface for creating and registering rules.
Namespace: MilliRules
snake_case or camelCase. For example, ->when_all() and ->whenAll() are equivalent, as are ->set_conditions() and ->setConditions(). This applies to all builder methods on Rules, ConditionBuilder, and ActionBuilder. The documentation uses snake_case throughout, but use whichever style fits your project.Methods
create(string $id, ?string $type = null): Rules
Create a new rule.
Parameters:
$id(string): Unique rule identifier$type(string|null): Rule type ('php'or'wp'), auto-detected if null
Returns: Rules - Rule builder instance
Example:
1$rule = Rules::create('my_rule');
2$rule = Rules::create('wp_rule', 'wp');
title(string $title): Rules
Set rule title.
Parameters:
$title(string): Human-readable title
Returns: Rules - Fluent interface
Example:
1Rules::create('my_rule')
2 ->title('My Custom Rule')
3 ->register();
order(int $order): Rules
Set execution order.
Parameters:
$order(int): Order value (lower = executes first)
Returns: Rules - Fluent interface
Example:
1Rules::create('my_rule')
2 ->order(10) // Execute at priority 10
3 ->register();
enabled(bool $enabled): Rules
Enable or disable rule.
Parameters:
$enabled(bool): Whether rule is enabled
Returns: Rules - Fluent interface
Example:
1Rules::create('my_rule')
2 ->enabled(false) // Disable rule
3 ->register();
lock(): Rules
Lock the rule to prevent overwriting or unregistering.
Locked rules cannot be overwritten by another rule with the same ID, nor can they be unregistered. This guards the entire rule — conditions, actions, and metadata — from replacement.
Returns: Rules - Fluent interface
Example:
1// Lock a safety-critical rule
2Rules::create('no-cache-post')->lock()->order(0)
3 ->when_all()->request_method('POST')
4 ->then()->set_cache(false)->lock()
5 ->register();
6
7// Attempting to overwrite is silently rejected
8Rules::create('no-cache-post') // Same ID — rejected
9 ->when_all()
10 ->then()->set_cache(true)
11 ->register();
12
13// Attempting to unregister is also rejected
14Rules::unregister('no-cache-post'); // Returns false
Key Points:
- Protects the rule definition (conditions + actions + metadata)
- Separate from
ActionBuilder::lock()which locks action execution - Use both together for maximum protection on core rules
when(): ConditionBuilder
Start building conditions with match_all logic.
Returns: ConditionBuilder - Condition builder instance
Example:
1Rules::create('my_rule')
2 ->when()
3 ->request_url('/api/*')
4 ->request_method('GET')
5 ->then()
6 ->custom('action')
7 ->register();
when_all(): ConditionBuilder
Start building conditions with ALL logic (AND).
Returns: ConditionBuilder
Example:
1Rules::create('my_rule')
2 ->when_all()
3 ->condition1()
4 ->condition2()
5 ->then()->custom('action')
6 ->register();
when_any(): ConditionBuilder
Start building conditions with ANY logic (OR).
Returns: ConditionBuilder
Example:
1Rules::create('my_rule')
2 ->when_any()
3 ->condition1()
4 ->condition2()
5 ->then()->custom('action')
6 ->register();
when_none(): ConditionBuilder
Start building conditions with NONE logic (NOT).
Returns: ConditionBuilder
Example:
1Rules::create('my_rule')
2 ->when_none()
3 ->condition1()
4 ->condition2()
5 ->then()->custom('action')
6 ->register();
and(): Rules
Finalize the current condition group and prepare for the next one. Used to chain multiple condition groups with different match types. All groups are combined with AND logic.
Returns: Rules - Fluent interface (call when_all(), when_any(), or when_none() next)
Example:
1Rules::create('my_rule')
2 ->when_any()
3 ->post_type('page')
4 ->post_type('post')
5 ->and()->when_none()
6 ->user_role('subscriber')
7 ->then()
8 ->custom('action')
9 ->register();
then(?array $actions = null): ActionBuilder
Start building actions.
Parameters:
$actions(array|null): Array of action configurations (optional)
Returns: ActionBuilder - Action builder instance
Example:
1Rules::create('my_rule')
2 ->when()->request_url('*')
3 ->then()
4 ->custom('action1')
5 ->custom('action2')
6 ->register();
on(string $hook, int $priority = 10): Rules
Register rule on WordPress hook.
Parameters:
$hook(string): WordPress hook name$priority(int): Hook priority
Returns: Rules - Fluent interface
Example:
1Rules::create('my_rule', 'wp')
2 ->on('init', 10)
3 ->when()->is_user_logged_in()
4 ->then()->custom('action')
5 ->register();
register(): void
Register rule with MilliRules.
If a rule with the same ID already exists, it will be replaced.
Returns: void
Example:
1Rules::create('my_rule')
2 ->when()->request_url('*')
3 ->then()->custom('action')
4 ->register(); // Must call to activate rule
5
6// Registering again with same ID replaces the rule
7Rules::create('my_rule')
8 ->when()->request_url('/api/*')
9 ->then()->custom('different_action')
10 ->register(); // Replaces previous 'my_rule'
unregister(string $rule_id): bool
Remove a rule by its ID.
Parameters:
$rule_id(string): The ID of the rule to remove
Returns: bool - True if rule was found and removed, false otherwise
Example:
1// Remove a rule
2$removed = Rules::unregister('my_rule');
3
4// Check if removal was successful
5if ($removed) {
6 error_log('Rule was removed');
7} else {
8 error_log('Rule not found');
9}
10
11// Use case: Child theme disabling parent rule
12Rules::unregister('parent_theme_cache_rule');
13
14// Use case: Environment-specific disabling
15if (wp_get_environment_type() === 'production') {
16 Rules::unregister('debug_logging_rule');
17}
register_condition(string $type, callable $callback): ConditionMeta
Register custom condition callback.
Returns a ConditionMeta instance for fluent declaration of metadata (label, description, categories, operators, arguments).
Parameters:
$type(string): Condition type identifier$callback(callable): Callback functionfunction($args, Context $context): bool
Returns: ConditionMeta — fluent metadata declaration for the registered condition
Example:
1// Simple condition (return value can be ignored)
2Rules::register_condition('is_weekend', function($args, Context $context) {
3 return date('N') >= 6;
4});
5
6// With full metadata for UI introspection
7Rules::register_condition('is_weekend', function($args, Context $context) {
8 return date('N') >= 6;
9})
10 ->label('Is Weekend')
11 ->description('Matches on Saturdays and Sundays.')
12 ->categories('date')
13 ->operators('=', '!=');
For class-based conditions, override set_meta() on your BaseCondition subclass:
1class RequestUrl extends BaseCondition {
2 public static function set_meta(ConditionMeta $meta): void
3 {
4 $meta
5 ->label('Request URL')
6 ->description('Match the current request URL.')
7 ->categories('request')
8 ->operators('=', '!=', 'LIKE', 'REGEXP', 'IN', 'NOT IN')
9 ->args()
10 ->string('value')->label('URL Pattern')->required();
11 }
12}
get_condition_meta(string $type): ?ConditionMeta
Get the full metadata for a registered condition type.
Resolves metadata from either the callback-based registry (populated by register_condition()) or the class-based BaseCondition::set_meta() method. For class-based conditions, the argument mapping from BaseCondition::get_argument_mapping() is automatically included.
Results are cached per type.
Parameters:
$type(string): Condition type identifier
Returns: ConditionMeta|null — metadata for the condition, or null if not found
Example:
1$meta = Rules::get_condition_meta('request_url');
2if ($meta) {
3 $label = $meta->get_label(); // 'Request URL'
4 $operators = $meta->get_operators(); // ['=', '!=', 'LIKE', ...]
5 $mapping = $meta->get_argument_mapping(); // ['value']
6 $args = $meta->get_arguments(); // array<ArgumentSchema>
7 $data = $meta->to_array(); // For REST/JSON serialization
8}
ConditionMeta — fluent condition metadata
ConditionMeta is the metadata container for condition types. Parallel to ActionMeta but with operators instead of scope.
Core fields
->label(string $label)— human-readable name->description(string $description)— help text->categories(string ...$categories)— one or more UI grouping categories->operators(string ...$operators)— supported comparison operators (e.g.,'=','!=','LIKE','IN')->argument_mapping(array $mapping)— how builder args map to config keys (auto-set for class-based conditions)->args()→ArgumentsBuilder— same walking-builder pattern as ActionMeta->extend(string $key, $value)— plugin-specific metadata bag
->to_array(): array — wire format
1[
2 'type' => string,
3 'label' => string,
4 'description' => string,
5 'categories' => array<int, string>,
6 'operators' => array<int, string>,
7 'argument_mapping' => array<int, string>,
8 'arguments' => array<int, array>,
9 'extensions' => array<string, mixed>,
10]
register_action(string $type, callable $callback): ActionMeta
Register custom action callback.
Returns an ActionMeta instance for fluent declaration of action metadata (scope, label, description, category).
Parameters:
$type(string): Action type identifier$callback(callable): Callback functionfunction($args, Context $context): void
Returns: ActionMeta — fluent metadata declaration for the registered action
Example:
1// Simple action (return value can be ignored)
2Rules::register_action('log', function($args, Context $context) {
3 error_log($args['value'] ?? '');
4});
5
6// Paired actions with shared scope (value-level locking when locked)
7Rules::register_action('add_flag', $addCallback)->scope('flag');
8Rules::register_action('remove_flag', $removeCallback)->scope('flag');
9
10// With full metadata for UI introspection
11Rules::register_action('add_flag', $addCallback)
12 ->scope('flag')
13 ->label('Add Flag')
14 ->description('Tag the response with a flag for bulk invalidation.')
15 ->categories('flags')
16 ->args()
17 ->string(0)->label('Flag')->required();
For class-based actions, override two static methods on your BaseAction subclass:
get_scope()— returns the lock scope as a plain string. Must not use framework-specific functions (e.g., translation) because the engine calls it during rule execution, which may happen during early bootstrap.set_meta(ActionMeta $meta)— configures consumer-facing metadata (label, description, categories, args). Called only by consumer code like UIs or REST endpoints, which always run after the framework has initialized.
1class AddFlag extends BaseAction {
2 // Engine-relevant. Called during early bootstrap — plain strings only.
3 public static function get_scope(): string
4 {
5 return 'flag';
6 }
7
8 // Consumer-relevant. Called after framework initialization.
9 public static function set_meta(ActionMeta $meta): void
10 {
11 $meta
12 ->label('Add Flag')
13 ->description('Tag the response with a flag.')
14 ->categories('flags');
15 }
16
17 public function execute(Context $context): void { /* ... */ }
18 public function get_type(): string { return 'add_flag'; }
19}
Why this signature: the engine owns the action type string (from the registration lookup), so it constructs the ActionMeta and passes it in. Subclasses can't accidentally set the wrong type or forget to call parent::set_meta() — there's no boilerplate to forget.
Why scope is split from set_meta(): the engine reads scope during rule execution, which may happen during early bootstrap before the application framework has fully initialized. If scope were set inside set_meta() alongside framework-dependent calls (e.g., translation functions), the engine couldn't read it safely. The split keeps the hot path runtime-safe.
get_action_scope(string $type): string
Get the lock scope for an action type — engine hot path, runtime-safe.
Fast-path accessor that never calls set_meta(). Used internally by RuleEngine::build_lock_key(). Safe to call during early bootstrap before the application framework has initialized.
Parameters:
$type(string): Action type identifier
Returns: string — the scope identifier, or '' for unknown or unscoped actions
Example:
1$scope = Rules::get_action_scope('add_flag'); // 'flag'
2$scope = Rules::get_action_scope('set_ttl'); // '' (unscoped)
Resolution order:
- Callback-based: reads from the meta set at registration time (
register_action()->scope()). - Class-based: calls
$class::get_scope()directly.
Results are cached per type.
get_action_meta(string $type): ?ActionMeta
Get the full metadata for a registered action type.
Resolves metadata from either the callback-based registry (populated by register_action()) or the class-based BaseAction::set_meta() method. Results are cached per type.
May require framework functions: for class-based actions, this calls set_meta(), which may use framework-specific functions (e.g., translation). Do NOT call this during early bootstrap before the framework has initialized — use get_action_scope() instead if you only need the scope.
Parameters:
$type(string): Action type identifier
Returns: ActionMeta|null — metadata for the action, or null if not found
Example:
1$meta = Rules::get_action_meta('add_flag');
2if ($meta) {
3 $label = $meta->get_label(); // 'Add Flag'
4 $scope = $meta->get_scope(); // 'flag' (synced from $class::get_scope())
5 $categories = $meta->get_categories(); // ['flags']
6 $arguments = $meta->get_arguments(); // array<ArgumentSchema>
7 $icon = $meta->get_extension('millicache:icon'); // plugin-specific
8 $data = $meta->to_array(); // For REST/JSON serialization
9}
get_all_condition_metas(): array
Get metadata for all available condition types.
Discovers all condition types from both class-based (via namespace scanning) and callback-based registrations, and resolves their metadata.
Results are cached after first call. The cache is cleared when new conditions are registered.
Returns: array<string, ConditionMeta> — map of type string to ConditionMeta
Example:
1$conditions = Rules::get_all_condition_metas();
2
3foreach ($conditions as $type => $meta) {
4 echo $type; // 'post_type'
5 echo $meta->get_label(); // 'Post Type'
6 echo $meta->get_operators(); // ['=', '!=', 'IN', 'NOT IN']
7}
get_all_action_metas(): array
Get metadata for all available action types.
Discovers all action types from both class-based (via namespace scanning) and callback-based registrations, and resolves their metadata.
Results are cached after first call. The cache is cleared when new actions are registered.
Returns: array<string, ActionMeta> — map of type string to ActionMeta
Example:
1$actions = Rules::get_all_action_metas();
2
3foreach ($actions as $type => $meta) {
4 echo $type; // 'add_flag'
5 echo $meta->get_label(); // 'Add Flag'
6 echo $meta->get_scope(); // 'flag'
7}
validate(array $rule): array
Validate a rule configuration against the engine's registry.
Checks that the rule's match_type, condition types, operators, action types, and action arguments are all recognized by the engine. Returns an array of plain-English error strings (empty array = valid).
This validates engine-level concerns only. Storage-layer concerns (ID format, title length, order range) are the consumer's responsibility.
Parameters:
$rule(array): The rule configuration array
Returns: array<int, string> — error messages; empty if valid
Example:
1$errors = Rules::validate([
2 'match_type' => 'all',
3 'conditions' => [
4 ['type' => 'post_type', 'operator' => '=', 'value' => 'page'],
5 ],
6 'actions' => [
7 ['type' => 'set_ttl', 'ttl' => 3600],
8 ],
9]);
10
11if (! empty($errors)) {
12 // Handle validation errors.
13 foreach ($errors as $error) {
14 echo $error; // "Condition #1 has unknown type 'foo'."
15 }
16}
Validates:
match_typeagainstRules::MATCH_TYPES- Condition types exist in the registry
- Condition operators are in the condition's declared operators (if any)
- Condition groups recursively (match_type + nested conditions)
- Action types exist in the registry
- Action arguments pass
ArgumentSchema::validate()
Constants
Rules::MATCH_TYPES
Array of match types supported by the rule engine.
1Rules::MATCH_TYPES // ['all', 'any', 'none']
Use this instead of hardcoding match type values:
1// In validation
2if (! in_array($match_type, Rules::MATCH_TYPES, true)) { /* ... */ }
3
4// In UI dropdowns
5foreach (Rules::MATCH_TYPES as $type) { /* ... */ }
ActionMeta — fluent action metadata
ActionMeta is the declarative metadata container for an action type. Obtain it from Rules::register_action() (for callback-based actions) or override BaseAction::set_meta() (for class-based actions).
Core fields
->scope(string $scope)— lock scope (engine-relevant; see Scoped Locking)->label(string $label)— human-readable name->description(string $description)— help text->categories(string ...$categories)— one or more UI grouping categories
->args(): ArgumentsBuilder
Enter the arguments declaration context. Returns an internal ArgumentsBuilder instance that collects argument schemas via type factories. The builder is cached — calling args() multiple times returns the same instance.
1$meta->args()
2 ->integer('ttl')->format('seconds')->default(3600)->min(0)
3 ->string('reason')->default('');
Inside the builder, each type factory (->integer($key), ->string($key), etc.) creates a new ArgumentSchema and returns it for continued configuration. To declare another argument, just call another type factory — it "walks" back to the builder and starts a new one.
Preserves declaration order (no auto-sorting). Any meta-level methods called after ->args() (like ->extend() or ->categories()) are automatically forwarded back to the ActionMeta via __call(), so the chain can continue seamlessly:
1$meta
2 ->label(__('Set TTL'))
3 ->args()
4 ->integer('ttl')->default(3600)
5 ->string('reason')->default('')
6 ->extend('millicache:icon', 'clock'); // forwarded to $meta
See ArgumentSchema below for the per-argument API.
->extend(string $key, mixed $value): self
Attach plugin-specific metadata under a namespaced key. MilliRules stores the value but never interprets it. Use this for anything that isn't part of MilliRules core: icons, conditional visibility rules, documentation URLs, plugin-defined widgets.
1->extend('millicache:icon', 'clock')
2->extend('seo-redirects:default_status', 301)
3->extend('docs:url', 'https://example.com/actions/set-ttl')
Namespacing convention: use plugin-slug:field-name to avoid collisions. MilliRules does not enforce this — the convention is the contract.
Extension bag getters
->get_extension(string $key): mixed|null— returns the value, ornullif not set->has_extension(string $key): bool— distinguishes "set to null" from "not set"->get_extensions(): array<string, mixed>— returns the full keyed bag
->to_array(): array — wire format
1[
2 'type' => string,
3 'scope' => string,
4 'label' => string,
5 'description' => string,
6 'categories' => array<int, string>,
7 'arguments' => array<int, array>, // each via ArgumentSchema::to_array()
8 'extensions' => array<string, mixed>, // plugin-specific bag
9]
This is the stable, REST-serializable format for transmitting action metadata to consumers.
ArgumentSchema — argument metadata
ArgumentSchema is the declarative format for action arguments. Consumer code never references this class directly — schemas are obtained via $meta->args()->type($key). The class is internal but documented here so you understand what your ->args() chain is producing.
Every consumer that introspects actions (UIs, CLIs, docs generators, validators) reads the same schema. MilliRules' RuleEngine does not use ArgumentSchema at runtime — it's purely metadata.
Type system
Six core types cover all engine-level data shapes:
| Type | Coercion | min/max semantics | Default |
|---|---|---|---|
string |
(string) cast |
length bounds | '' |
integer |
(int) cast |
value bounds | 0 |
number |
(float) cast |
value bounds | 0.0 |
boolean |
truthy check | — | false |
choice |
pass-through | — | first option or null |
choices |
(array) + filter to valid options |
— | [] |
Everything else (url, email, seconds, regex, date, etc.) is expressible as a core type + format:
1$meta->args()
2 ->integer('ttl')->format('seconds') // TTL input
3 ->string('homepage')->format('url') // URL field
4 ->string('contact')->format('email') // email field
5 ->string('pattern')->format('regex'); // regex pattern
MilliRules stores format but never interprets it. Consumers pick their own vocabulary and handle format-specific rendering/validation.
Creating schemas
Schemas are created exclusively via the builder's type factories, obtained from $meta->args():
1$meta->args()
2 ->string($key) // $key is int|string
3 ->integer($key)
4 ->number($key)
5 ->boolean($key)
6 ->choice($key)
7 ->choices($key);
You never write new ArgumentSchema(...) yourself.
Walking: chain to the next argument
Type factories are also available on an existing schema and delegate back to the builder to start a new argument:
1$meta->args()
2 ->integer('ttl')->default(3600) // schema for 'ttl'
3 ->string('reason')->default(''); // ->string() walks back; new schema for 'reason'
This is why you can chain multiple arguments in a single fluent expression without restarting from $meta->args().
Fluent setters (config)
->format(string $format)— consumer-defined format hint->label(string $label)— human-readable name->description(string $description)— help text->required(bool $required = true)— mark as mandatory->default(mixed $value)— default value (rejects closures)->min(int $min)/->max(int $max)— length (string) or value (integer/number) bounds; throws on other types->options(array $options)— allowed values forchoice/choicestypes; throws on other types
options() format
Accepts either simple or structured form:
1// Simple — value == label
2->options(['GET', 'POST', 'PUT'])
3
4// Structured — separate value and label
5->options([
6 ['value' => 'get', 'label' => 'GET Request'],
7 ['value' => 'post', 'label' => 'POST Request'],
8])
Stored internally as the structured form. to_array() always emits the structured form.
Runtime guards
Calling incompatible setters throws InvalidArgumentException at declaration time (i.e., at class-load time for class-based actions):
1$meta->args()->string('k')->min(5); // OK: length bound
2$meta->args()->boolean('k')->min(5); // ✗ throws
3$meta->args()->integer('k')->options(['a', 'b']); // ✗ throws
Getters
->get_key(): int|string->get_type(): string->get_format(): string->get_label(): string->get_description(): string->get_default(): mixed->has_default(): bool— distinguishes "default is null" from "no default set"->is_required(): bool->get_min(): ?int->get_max(): ?int->get_options(): array
validate(mixed $value): ?string
Consumer utility. Returns null if the value is valid, or a plain English error message string if invalid. MilliRules ships no translation layer — consumers wrap the returned string in their own i18n if needed.
1// Retrieve schemas via the meta's get_arguments():
2$schemas = Rules::get_action_meta('set_ttl')->get_arguments();
3$schema = $schemas[0];
4
5$schema->validate(50); // null (valid)
6$schema->validate(150); // "Argument 'ttl' must be at most 100"
7$schema->validate('abc'); // "Argument 'ttl' must be an integer"
Note: RuleEngine does not call validate(). It's an opt-in utility for consumers (validators, UIs, CLIs).
sanitize(mixed $value): mixed
Consumer utility. Coerces a raw value to the declared type.
1$integer_schema->sanitize('3600'); // 3600
2$boolean_schema->sanitize('yes'); // true
3$choices_schema->sanitize(['a', 'invalid', 'b']); // ['a', 'b']
Null values are replaced with the default if set, otherwise the type's zero value ('', 0, 0.0, false, first option, or []).
to_array(): array — wire format
1[
2 'key' => int|string,
3 'type' => string,
4 'format' => string,
5 'label' => string,
6 'description' => string,
7 'default' => mixed,
8 'has_default' => bool,
9 'required' => bool,
10 'min' => int|null,
11 'max' => int|null,
12 'options' => array<int, array{value: mixed, label: string}>,
13]
register_placeholder(string $category, callable $resolver): void
Register custom placeholder resolver.
Parameters:
$category(string): Placeholder category$resolver(callable): Resolver functionfunction($context, $parts): string
Returns: void
Example:
1Rules::register_placeholder('custom', function($context, $parts) {
2 return $context['custom'][$parts[0]] ?? '';
3});
Builders#Copied!
ConditionBuilder#Copied!
Fluent builder for rule conditions.
Namespace: MilliRules
Methods
match_all(): ConditionBuilder
Use AND logic for conditions.
Returns: ConditionBuilder
match_any(): ConditionBuilder
Use OR logic for conditions.
Returns: ConditionBuilder
match_none(): ConditionBuilder
Use NOT logic for conditions.
Returns: ConditionBuilder
custom(string $type, $arg = null): ConditionBuilder
Add custom condition.
Parameters:
$type(string): Condition type$arg(mixed): Condition argument (value, config array, etc.)
Returns: ConditionBuilder
Example:
1->when()
2 ->custom('is_weekend')
3 ->custom('time_range', ['start' => 9, 'end' => 17])
add_namespace(string $namespace): ConditionBuilder
Add condition namespace for class resolution.
Parameters:
$namespace(string): Fully qualified namespace
Returns: ConditionBuilder
__call(string $method, array $args): mixed
Magic method for dynamic condition creation.
Converts method calls to condition types:
request_url()→RequestUrlConditionis_user_logged_in()→IsUserLoggedInCondition
ActionBuilder#Copied!
Fluent builder for rule actions.
Namespace: MilliRules
Methods
custom(string $type, $arg = null): ActionBuilder
Add custom action.
Parameters:
$type(string): Action type$arg(mixed): Action argument (config array, value, etc.)
Returns: ActionBuilder
Example:
1->then()
2 ->custom('log', ['value' => 'message'])
3 ->custom('send_email', ['to' => ''])
lock(): ActionBuilder
Mark the last action as locked.
Locked actions prevent subsequent actions from changing the same setting. How locking works depends on whether the action was registered with a scope:
- Unscoped actions (default): locks by action type —
set_ttl(300)->lock()blocks allset_ttlcalls - Scoped actions: locks by scope + value —
add_flag('x')->lock()only blocks operations on'x'within the same scope
Returns: ActionBuilder
Example — unscoped (type-level locking):
1// Rule 1 (order: 10) - Disable cache for logged-in users
2Rules::create('no-cache-logged-in')->order(10)
3 ->when()->is_user_logged_in()
4 ->then()->do_cache(false)->lock() // Lock the cache setting
5 ->register();
6
7// Rule 2 (order: 20) - This cache action will be IGNORED
8Rules::create('cache-api')->order(20)
9 ->when()->request_url('/api/*')
10 ->then()->do_cache(true) // Blocked - do_cache is locked
11 ->register();
Example — scoped (value-level locking):
1// Consumer registers paired actions with shared scope
2Rules::register_action('add_flag', $callback)->scope('flag');
3Rules::register_action('remove_flag', $callback)->scope('flag');
4
5// Lock a specific flag value
6->then()->add_flag('system-flag')->lock() // Locks 'flag:system-flag'
7
8// Later rules:
9->then()->add_flag('custom-flag') // Allowed — different lock key
10->then()->remove_flag('system-flag') // Blocked — same lock key
Key Points:
- Unscoped: locks are per action type
- Scoped: locks are per scope + value (cross-type within the same scope)
- Different action types/scopes can still execute
- Lock only applies if the rule's conditions match
- Locks reset on each rule execution
add_namespace(string $namespace): ActionBuilder
Add action namespace for class resolution.
Parameters:
$namespace(string): Fully qualified namespace
Returns: ActionBuilder
__call(string $method, array $args): mixed
Magic method for dynamic action creation.
Interfaces#Copied!
PackageInterface#Copied!
Interface for all packages.
Namespace: MilliRulesInterfaces
Methods
get_name(): string
Get unique package name.
Returns: string - Package name
get_namespaces(): array
Get condition and action namespaces.
Returns: array - Array of namespace strings
is_available(): bool
Check if package is available in current environment.
Returns: bool - True if available
get_required_packages(): array
Get required package names.
Returns: array - Array of package names
build_context(): array
Build context data for this package.
Returns: array - Context data
get_placeholder_resolver(array $context)
Get placeholder resolver for this package.
Parameters:
$context(array): Execution context
Returns: callable|null - Resolver function or null
register_rule(array $rule, array $metadata): void
Register rule with package.
Parameters:
$rule(array): Rule configuration$metadata(array): Rule metadata
Returns: void
execute_rules(array $rules, array $context): array
Execute rules for this package.
Parameters:
$rules(array): Rules to execute$context(array): Execution context
Returns: array - Execution result
ConditionInterface#Copied!
Interface for all conditions.
Namespace: MilliRulesInterfaces
Methods
matches(array $context): bool
Check if condition matches.
Parameters:
$context(array): Execution context
Returns: bool - True if matches
get_type(): string
Get condition type identifier.
Returns: string - Condition type
ActionInterface#Copied!
Interface for all actions.
Namespace: MilliRulesInterfaces
Methods
execute(Context $context): void
Execute action.
Parameters:
$context(array): Execution context
Returns: void
get_type(): string
Get action type identifier.
Returns: string - Action type
Base Classes#Copied!
BasePackage#Copied!
Abstract base class for packages.
Namespace: MilliRulesPackages
Provides default implementations for most PackageInterface methods.
Must Override:
get_name(): stringget_namespaces(): arrayis_available(): bool
Can Override:
get_required_packages(): array- Defaults to[]build_context(): array- Defaults to[]get_placeholder_resolver()- Defaults tonull
BaseCondition#Copied!
Abstract base class for conditions.
Namespace: MilliRulesConditions
Provides operator support and comparison logic.
Methods
__construct(array $config, Context $context)
Constructor.
Parameters:
$config(array): Condition configuration$context(array): Execution context
matches(array $context): bool
Check if condition matches (implemented).
Parameters:
$context(array): Execution context
Returns: bool
abstract protected function get_actual_value(Context $context)
Get actual value to compare (must implement).
Parameters:
$context(array): Execution context
Returns: mixed - Actual value
static public function compare_values($actual, $expected, string $operator = '='): bool
Compare values using operator.
Parameters:
$actual(mixed): Actual value$expected(mixed): Expected value$operator(string): Comparison operator
Returns: bool - True if comparison matches
BaseAction#Copied!
Abstract base class for actions.
Namespace: MilliRulesActions
Provides placeholder resolution.
Methods
__construct(array $config, Context $context)
Constructor.
Parameters:
$config(array): Action configuration$context(array): Execution context
protected function resolve_value(string $value): string
Resolve placeholders in value.
Parameters:
$value(string): Value with placeholders
Returns: string - Resolved value
abstract public function execute(Context $context): void
Execute action (must implement).
Parameters:
$context(array): Execution context
Returns: void
PackageManager#Copied!
Static manager for packages.
Namespace: MilliRules
Methods
static register_package(PackageInterface $package): void
Register package.
Parameters:
$package(PackageInterface): Package instance
Returns: void
static load_packages(?array $package_names = null): array
Load packages by name.
Parameters:
$package_names(array|null): Package names (null = load all available)
Returns: array - Loaded package names
static get_loaded_packages(): array
Get loaded package instances.
Returns: array - Array of PackageInterface instances
static get_loaded_package_names(): array
Get loaded package names.
Returns: array - Array of package names
static get_package(string $name): ?PackageInterface
Get package by name.
Parameters:
$name(string): Package name
Returns: PackageInterface|null - Package instance or null
static is_package_loaded(string $name): bool
Check if package is loaded.
Parameters:
$name(string): Package name
Returns: bool - True if loaded
static has_packages(): bool
Check if any packages are loaded.
Returns: bool - True if packages exist
static build_context(): array
Build context from all loaded packages.
Returns: array - Aggregated context
RuleEngine#Copied!
Rule execution engine.
Namespace: MilliRules
Methods
execute(array $rules, array $context, ?array $allowed_packages = null): array
Execute rules.
Parameters:
$rules(array): Rules to execute$context(array): Execution context$allowed_packages(array|null): Allowed package names
Returns: array - Execution result
static register_namespace(string $type, string $namespace): void
Register namespace for class resolution.
Parameters:
$type(string): Type ('condition'or'action')$namespace(string): Namespace string
Returns: void
Next Steps#Copied!
- Real-World Examples - See API usage in complete examples
- Getting Started - Basic usage guide
- Building Rules - Fluent API guide
Ready for complete examples? Continue to Real-World Examples to see the API in action with full working code.