Advanced Patterns

This guide covers advanced techniques for optimizing performance, debugging issues, implementing complex rule patterns, and integrating MilliRules deeply into your applications.

Early Execution#Copied!

Early execution runs rules before WordPress fully loads, enabling caching systems, redirects, and performance optimizations.

MU-Plugin Early Execution#Copied!

 1/**
 2 * Plugin Name: MilliRules Early Execution
 3 * Description: Runs MilliRules before WordPress loads
 4 */
 5
 6require_once WPMU_PLUGIN_DIR . '/millirules-vendor/autoload.php';
 7
 8use MilliRulesMilliRules;
 9use MilliRulesRules;
10use MilliRulesContext;
11
12// Initialize with PHP package only (WordPress not loaded yet)
13MilliRules::init(['PHP']);
14
15// Register early execution rules
16Rules::create('early_cache_check', 'php')
17    ->when()
18        ->request_url('/api/*')
19        ->request_method('GET')
20    ->then()
21        ->custom('check_cache')
22        ->custom('early_exit_if_cached')
23    ->register();
24
25// Execute early rules
26$result = MilliRules::execute_rules(['PHP']);

Custom Cache Integration#Copied!

 1Rules::register_action('check_cache', function($args, Context $context) {
 2    $cache_key = 'page_' . md5($context->get('request.uri', '') ?? '');
 3    $cached = get_transient($cache_key);
 4
 5    if ($cached !== false) {
 6        // Send cached response
 7        header('Content-Type: text/html; charset=UTF-8');
 8        header('X-Cache: HIT');
 9        echo $cached;
10        exit;
11    }
12});
13
14Rules::register_action('save_to_cache', function($args, Context $context) {
15    $cache_key = 'page_' . md5($context->get('request.uri', '') ?? '');
16    $duration = $args['duration'] ?? 3600;
17
18    ob_start(function($buffer) use ($cache_key, $duration) {
19        set_transient($cache_key, $buffer, $duration);
20        return $buffer;
21    });
22});
23
24Rules::create('api_caching')
25    ->when()
26        ->request_url('/api/*')
27        ->request_method('GET')
28    ->then()
29        ->custom('check_cache') // Check first
30        ->custom('save_to_cache', ['duration' => 3600]) // Save if not cached
31    ->register();

Performance Optimization#Copied!

Rule Ordering for Performance#Copied!

 1// Place most restrictive/fastest conditions first
 2Rules::create('optimized_rule')
 3    ->order(10)
 4    ->when()
 5        // Fast checks first
 6        ->request_method('POST')                    // Very fast
 7        ->request_url('/api/specific-endpoint')     // Fast
 8        ->cookie('session_id')                      // Fast
 9
10        // Slower checks last
11        ->custom('expensive_validation')            // Slow
12    ->then()
13        ->custom('process_request')
14    ->register();

Lazy Loading#Copied!

 1// Load rules only when needed
 2add_action('init', function() {
 3    MilliRules::init();
 4
 5    // Load admin rules only in admin
 6    if (is_admin()) {
 7        require_once __DIR__ . '/rules/admin-rules.php';
 8    }
 9
10    // Load frontend rules only on frontend
11    if (!is_admin()) {
12        require_once __DIR__ . '/rules/frontend-rules.php';
13    }
14
15    // Load API rules only for API requests
16    if (str_starts_with($_SERVER['REQUEST_URI'] ?? '', '/api/')) {
17        require_once __DIR__ . '/rules/api-rules.php';
18    }
19}, 1);

Debugging Strategies#Copied!

Debug Logging#Copied!

 1// Enable comprehensive debugging
 2define('MILLIRULES_DEBUG', true);
 3
 4Rules::register_action('debug_log', function($args, Context $context) {
 5    if (!defined('MILLIRULES_DEBUG') || !MILLIRULES_DEBUG) {
 6        return;
 7    }
 8
 9    $message = $args['message'] ?? '';
10    $data = $args['data'] ?? [];
11
12    error_log('=== MilliRules Debug ===');
13    error_log('Message: ' . $message);
14    error_log('Data: ' . print_r($data, true));
15    error_log('======================');
16});
17
18// Add debug actions to rules
19Rules::create('debuggable_rule')
20    ->when()
21        ->request_url('/api/*')
22    ->then()
23        ->custom('debug_log', [
24            'message' => 'API request started',
25            'data' => ['url' => '{request.uri}']
26        ])
27        ->custom('process_api')
28        ->custom('debug_log', [
29            'message' => 'API request completed'
30        ])
31    ->register();

Execution Statistics#Copied!

 1// Track execution statistics
 2$result = MilliRules::execute_rules();
 3
 4error_log('=== Execution Statistics ===');
 5error_log('Rules processed: ' . $result['rules_processed']);
 6error_log('Rules skipped: ' . $result['rules_skipped']);
 7error_log('Rules matched: ' . $result['rules_matched']);
 8error_log('Actions executed: ' . $result['actions_executed']);
 9error_log('===========================');
10
11// Performance tracking
12$start_time = microtime(true);
13$start_memory = memory_get_usage();
14
15$result = MilliRules::execute_rules();
16
17$execution_time = microtime(true) - $start_time;
18$memory_used = memory_get_usage() - $start_memory;
19
20error_log("Execution time: {$execution_time}s");
21error_log("Memory used: " . size_format($memory_used));

Debug Conditions#Copied!

 1Rules::register_condition('debug_context', function($args, Context $context) {
 2    error_log('=== Context Debug ===');
 3    error_log('Full context: ' . print_r($context, true));
 4    error_log('===================');
 5    return true; // Always matches
 6});
 7
 8Rules::create('debug_rule')
 9    ->when()
10        ->custom('debug_context')
11        ->your_actual_conditions()
12    ->then()
13        ->your_actions()
14    ->register();

Complex Rule Patterns#Copied!

Conditional Rule Groups#Copied!

 1// Environment-specific rules
 2$environment = wp_get_environment_type();
 3
 4if ($environment === 'local') {
 5    // Local development rules
 6    Rules::create('local_debug')
 7        ->when()->constant('WP_DEBUG', true, '=')
 8        ->then()->custom('enable_verbose_logging')
 9        ->register();
10
11    Rules::create('local_logging')
12        ->when()->request_url('*')
13        ->then()->custom('log_all_requests')
14        ->register();
15
16} elseif ($environment === 'staging') {
17    // Staging environment rules
18    Rules::create('staging_monitoring')
19        ->when()->request_url('*')
20        ->then()->custom('track_staging_metrics')
21        ->register();
22
23} elseif ($environment === 'production') {
24    // Production rules
25    Rules::create('prod_caching')
26        ->when()->request_url('/api/*')
27        ->then()->custom('enable_aggressive_cache')
28        ->register();
29
30    Rules::create('prod_security')
31        ->when()->request_method('POST')
32        ->then()->custom('enhanced_security_check')
33        ->register();
34}

Dynamic Rule Generation#Copied!

 1// Generate rules from configuration
 2$protected_endpoints = [
 3    '/api/users' => ['GET', 'POST'],
 4    '/api/posts' => ['GET', 'POST', 'PUT', 'DELETE'],
 5    '/api/settings' => ['GET', 'PUT'],
 6];
 7
 8foreach ($protected_endpoints as $endpoint => $methods) {
 9    $rule_id = 'protect_' . sanitize_title($endpoint);
10
11    Rules::create($rule_id)
12        ->when()
13            ->request_url($endpoint)
14            ->request_method($methods, 'IN')
15            ->is_user_logged_in(false) // Not logged in
16        ->then()
17            ->custom('send_401_unauthorized')
18        ->register();
19}

Package Filtering#Copied!

Execute rules with specific packages only.

Selective Package Execution#Copied!

 1// Execute only PHP rules (before WordPress loads)
 2$php_result = MilliRules::execute_rules(['PHP']);
 3
 4// Execute only WordPress rules
 5$wp_result = MilliRules::execute_rules(['WP']);
 6
 7// Execute with custom packages
 8$custom_result = MilliRules::execute_rules(['PHP', 'Custom']);

Context-Aware Package Selection#Copied!

 1add_action('init', function() {
 2    MilliRules::init();
 3
 4    // Determine which packages to use
 5    $packages = ['PHP'];
 6
 7    if (function_exists('add_action')) {
 8        $packages[] = 'WP';
 9    }
10
11    if (class_exists('WooCommerce')) {
12        $packages[] = 'WooCommerce';
13    }
14
15    // Execute with selected packages
16    $result = MilliRules::execute_rules($packages);
17}, 5);

Context Manipulation#Copied!

Extending Context#Copied!

 1// Add custom data to context before execution
 2add_filter('millirules_context', function(Context $context) {
 3    $context['custom'] = [
 4        'api_key' => get_option('my_api_key'),
 5        'feature_flags' => get_option('feature_flags', []),
 6        'site_config' => get_site_config(),
 7    ];
 8
 9    return $context;
10});

Context Transformation#Copied!

 1// Transform context for specific rules
 2Rules::register_action('with_transformed_context', function($args, Context $context) {
 3    // Add computed values
 4    $context['computed'] = [
 5        'is_business_hours' => check_business_hours(),
 6        'user_tier' => calculate_user_tier($context),
 7        'request_complexity' => analyze_request($context),
 8    ];
 9
10    // Execute sub-action with transformed context
11    $callback = $args['callback'] ?? null;
12    if (is_callable($callback)) {
13        $callback($context);
14    }
15});

Error Handling#Copied!

Graceful Degradation#Copied!

 1Rules::register_action('safe_api_call', function($args, Context $context) {
 2    try {
 3        $response = wp_remote_post($args['url'], [
 4            'body' => json_encode($args['data']),
 5            'headers' => ['Content-Type' => 'application/json'],
 6            'timeout' => 10,
 7        ]);
 8
 9        if (is_wp_error($response)) {
10            throw new Exception($response->get_error_message());
11        }
12
13        $status = wp_remote_retrieve_response_code($response);
14        if ($status >= 400) {
15            throw new Exception("API returned status {$status}");
16        }
17
18        // Success
19        return json_decode(wp_remote_retrieve_body($response), true);
20
21    } catch (Exception $e) {
22        error_log('API Error: ' . $e->getMessage());
23
24        // Fallback behavior
25        $fallback = $args['fallback'] ?? null;
26        if (is_callable($fallback)) {
27            return $fallback($context);
28        }
29
30        return null;
31    }
32});

Error Notification#Copied!

 1Rules::register_action('notify_on_error', function($args, Context $context) {
 2    try {
 3        // Risky operation
 4        perform_critical_operation($config);
 5
 6    } catch (Exception $e) {
 7        // Log error
 8        error_log('Critical error: ' . $e->getMessage());
 9
10        // Notify admin
11        wp_mail(
12            get_option('admin_email'),
13            'MilliRules Critical Error',
14            "Error: {$e->getMessage()}nnContext: " . print_r($context, true)
15        );
16
17        // Store error for admin dashboard
18        update_option('millirules_last_error', [
19            'message' => $e->getMessage(),
20            'time' => time(),
21            'context' => $context,
22        ]);
23    }
24});

Testing Strategies#Copied!

Unit Testing Rules#Copied!

 1class MilliRulesTest extends WP_UnitTestCase {
 2    public function setUp(): void {
 3        parent::setUp();
 4        MilliRules::init();
 5    }
 6
 7    public function test_api_cache_rule() {
 8        // Register test action
 9        Rules::register_action('test_cache', function($args, Context $context) {
10            update_option('test_cache_called', true);
11        });
12
13        // Create rule
14        Rules::create('test_api_cache')
15            ->when()
16                ->request_url('/api/test')
17                ->request_method('GET')
18            ->then()
19                ->custom('test_cache')
20            ->register();
21
22        // Simulate request
23        $_SERVER['REQUEST_URI'] = '/api/test';
24        $_SERVER['REQUEST_METHOD'] = 'GET';
25
26        // Execute
27        $result = MilliRules::execute_rules();
28
29        // Assert
30        $this->assertEquals(1, $result['rules_matched']);
31        $this->assertTrue(get_option('test_cache_called'));
32    }
33}

Integration Testing#Copied!

 1function test_complete_workflow() {
 2    // Setup
 3    MilliRules::init();
 4
 5    $executed_actions = [];
 6
 7    Rules::register_action('track_execution', function($args, Context $context) use (&$executed_actions) {
 8        $executed_actions[] = $args['step'];
 9    });
10
11    // Create multi-step rule
12    Rules::create('workflow_test')
13        ->when()->request_url('/test-workflow')
14        ->then()
15            ->custom('track_execution', ['step' => 'validate'])
16            ->custom('track_execution', ['step' => 'process'])
17            ->custom('track_execution', ['step' => 'complete'])
18        ->register();
19
20    // Execute
21    $_SERVER['REQUEST_URI'] = '/test-workflow';
22    MilliRules::execute_rules();
23
24    // Verify execution order
25    assert($executed_actions === ['validate', 'process', 'complete']);
26
27    echo "Workflow test passed!n";
28}

Best Practices Summary#Copied!

1. Performance#Copied!

  • Order conditions from fastest to slowest
  • Cache expensive operations
  • Use early execution for caching/redirects
  • Load rules only when needed

2. Debugging#Copied!

  • Enable debug logging in development
  • Track execution statistics
  • Use debug conditions to inspect context
  • Monitor memory and execution time

3. Maintainability#Copied!

  • Use descriptive rule IDs
  • Group related rules by feature
  • Document complex logic
  • Use consistent naming conventions

4. Error Handling#Copied!

  • Always validate input
  • Provide fallback behaviors
  • Log errors appropriately
  • Notify admins of critical issues

5. Testing#Copied!

  • Write unit tests for custom conditions/actions
  • Test complete rule workflows
  • Test with different package combinations
  • Simulate various environments

Next Steps#Copied!


Ready to explore WordPress integration? Continue to WordPress Integration Guide for WordPress-specific techniques and patterns.