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

Note: Naming Convention: All fluent API methods can be called in either 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 function function($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 function function($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:

  1. Callback-based: reads from the meta set at registration time (register_action()->scope()).
  2. 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_type against Rules::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, or null if 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 for choice/choices types; 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 function function($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()RequestUrlCondition
  • is_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 all set_ttl calls
  • 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(): string
  • get_namespaces(): array
  • is_available(): bool

Can Override:

  • get_required_packages(): array - Defaults to []
  • build_context(): array - Defaults to []
  • get_placeholder_resolver() - Defaults to null

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!


Ready for complete examples? Continue to Real-World Examples to see the API in action with full working code.