# easy-invoice/2.3.8/includes/Migration/MigrationRunner.php

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.3.8. 399 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.3.8/code/includes/Migration/MigrationRunner.php
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.3.8/raw/includes/Migration/MigrationRunner.php
- Modified: 2025-08-19T15:19:02+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.3.8/code/includes/Migration/MigrationRunner.php#L10-L20`.

```php
<?php
/**
 * Migration Runner for Easy Invoice
 *
 * @package     EasyInvoice
 * @subpackage  Migration
 * @since       2.0.0
 */

namespace EasyInvoice\Migration;

use EasyInvoice\Migration\Src\SettingsMigration;
use EasyInvoice\Migration\Src\ClientMigration;
use EasyInvoice\Migration\Src\PostTypeMigration;
use EasyInvoice\Migration\Src\MetaMigration;
use EasyInvoice\Migration\Src\ProOptionsMigration;
use EasyInvoice\Migration\Src\DataCleanupMigration;

/**
 * Migration Runner Class
 *
 * Orchestrates the migration process.
 *
 * @since 2.0.0
 */
class MigrationRunner {

    /**
     * Version checker instance.
     *
     * @since 2.0.0
     * @var VersionChecker
     */
    private $version_checker;

    /**
     * Migration steps.
     *
     * @since 2.0.0
     * @var array
     */
    private $migration_steps = [
        'settings' => [
            'class' => SettingsMigration::class,
            'dependencies' => []
        ],
        'clients' => [
            'class' => ClientMigration::class,
            'dependencies' => ['settings']
        ],
        'post_types' => [
            'class' => PostTypeMigration::class,
            'dependencies' => ['clients']
        ],
        'meta' => [
            'class' => MetaMigration::class,
            'dependencies' => ['clients', 'post_types']
        ],
        'pro_options' => [
            'class' => ProOptionsMigration::class,
            'dependencies' => ['settings']
        ],
        'cleanup' => [
            'class' => DataCleanupMigration::class,
            'dependencies' => ['post_types', 'meta']
        ]
    ];

    /**
     * Constructor.
     *
     * @since 2.0.0
     * @param VersionChecker $version_checker Version checker instance
     */
    public function __construct(VersionChecker $version_checker) {
        $this->version_checker = $version_checker;
    }

    /**
     * Check if migration is needed.
     *
     * @since 2.0.0
     * @return bool
     */
    public function should_run_migration(): bool {
        // Get current version
        $current_version = $this->version_checker->get_stored_version();

        // Get target version
        $target_version = $this->version_checker->get_target_version();

        // Compare versions
        if (version_compare($current_version, $target_version, '<')) {
            $this->log_migration_check(true, $current_version, $target_version);
            return true;
        }

        $this->log_migration_check(false, $current_version, $target_version);
        return false;
    }

    /**
     * Run the migration.
     *
     * @since 2.0.0
     * @return array
     */
    public function run_migration(): array {
        try {
            // Set migration running flag
            $this->set_migration_running(true);

            // Initialize results
            $results = [];
            $errors = [];

            // Get migration order based on dependencies
            $migration_order = $this->get_migration_order();

            // Run each migration step in order
            foreach ($migration_order as $step) {
                $result = $this->run_migration_step($step);
                $results[$step] = $result;

                // Check for errors
                if (!$result['success']) {
                    $errors[] = sprintf('%s migration failed: %s', $step, $result['message']);
                    break; // Stop on first error
                }

                // Store step completion
                update_option('easy_invoice_migration_step_' . $step . '_completed', true);
            }

            // Update version after successful migration
            if (empty($errors)) {
                $this->update_version();
                $this->set_migration_completed(true);
                $this->log_migration_success($results);
            } else {
                $this->log_migration_error($errors);
            }

            // Clear migration running flag
            $this->set_migration_running(false);

            return [
                'success' => empty($errors),
                'results' => $results,
                'errors' => $errors
            ];

        } catch (\Exception $e) {
            // Log error and clear flags
            $this->log_migration_error([$e->getMessage()]);
            $this->set_migration_running(false);

            return [
                'success' => false,
                'message' => $e->getMessage(),
                'errors' => [$e->getMessage()]
            ];
        }
    }

    /**
     * Get migration order based on dependencies.
     *
     * @since 2.0.0
     * @return array
     */
    private function get_migration_order(): array {
        $order = [];
        $visited = [];

        foreach (array_keys($this->migration_steps) as $step) {
            if (!isset($visited[$step])) {
                $this->visit_step($step, $visited, $order);
            }
        }

        return $order;
    }

    /**
     * Visit migration step for topological sort.
     *
     * @since 2.0.0
     * @param string $step Step name
     * @param array $visited Visited steps
     * @param array $order Migration order
     * @return void
     */
    private function visit_step(string $step, array &$visited, array &$order): void {
        $visited[$step] = true;

        foreach ($this->migration_steps[$step]['dependencies'] as $dependency) {
            if (!isset($visited[$dependency])) {
                $this->visit_step($dependency, $visited, $order);
            }
        }

        $order[] = $step;
    }

    /**
     * Check if a migration step is completed.
     *
     * @since 2.0.0
     * @param string $step Migration step
     * @return bool
     */
    private function is_step_completed(string $step): bool {
        return get_option('easy_invoice_migration_step_' . $step . '_completed', false);
    }

    /**
     * Run a specific migration step.
     *
     * @since 2.0.0
     * @param string $step Migration step
     * @return array
     */
    public function run_migration_step(string $step): array {
        if (!isset($this->migration_steps[$step])) {
            return [
                'success' => false,
                'message' => sprintf('Invalid migration step: %s', $step)
            ];
        }

        try {
            // Check dependencies
            foreach ($this->migration_steps[$step]['dependencies'] as $dependency) {
                if (!$this->is_step_completed($dependency)) {
                    return [
                        'success' => false,
                        'message' => sprintf('Dependency %s must be migrated first', $dependency)
                    ];
                }
            }

            $class = $this->migration_steps[$step]['class'];
            $migration = new $class();

            if (!$migration->is_needed()) {
                // Mark step as completed even if not needed
                update_option('easy_invoice_migration_step_' . $step . '_completed', true);

                return [
                    'success' => true,
                    'message' => sprintf('%s migration not needed', $step)
                ];
            }

            $result = $migration->migrate();

            if ($result['success']) {
                // Mark step as completed on success
                update_option('easy_invoice_migration_step_' . $step . '_completed', true);
            }

            return $result;

        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage()
            ];
        }
    }

    /**
     * Set migration running flag.
     *
     * @since 2.0.0
     * @param bool $running Running state
     * @return void
     */
    private function set_migration_running(bool $running): void {
        update_option('easy_invoice_migration_running', $running);
    }

    /**
     * Set migration completed flag.
     *
     * @since 2.0.0
     * @param bool $completed Completed state
     * @return void
     */
    private function set_migration_completed(bool $completed): void {
        update_option('easy_invoice_migration_completed', $completed);
        if ($completed) {
            update_option('easy_invoice_migration_completed_time', current_time('mysql'));
        }
    }

    /**
     * Update version after successful migration.
     *
     * @since 2.0.0
     * @return void
     */
    private function update_version(): void {
        $target_version = $this->version_checker->get_target_version();
        update_option('easy_invoice_version', $target_version);
    }

    /**
     * Log migration check.
     *
     * @since 2.0.0
     * @param bool $needed Whether migration is needed
     * @param string $current_version Current version
     * @param string $target_version Target version
     * @return void
     */
    private function log_migration_check(bool $needed, string $current_version, string $target_version): void {
        $message = $needed
            ? sprintf('Migration needed: Current version %s is less than target version %s', $current_version, $target_version)
            : sprintf('Migration not needed: Current version %s is greater than or equal to target version %s', $current_version, $target_version);

        $this->log($message);
    }

    /**
     * Log migration success.
     *
     * @since 2.0.0
     * @param array $results Migration results
     * @return void
     */
    private function log_migration_success(array $results): void {
        $message = 'Migration completed successfully:' . PHP_EOL;

        foreach ($results as $step => $result) {
            $message .= sprintf('- %s: %s' . PHP_EOL, $step, $result['message']);
        }

        $message .= PHP_EOL . 'System Information:' . PHP_EOL;
        $message .= sprintf('- WordPress Version: %s' . PHP_EOL, get_bloginfo('version'));
        $message .= sprintf('- PHP Version: %s' . PHP_EOL, PHP_VERSION);
        $message .= sprintf('- MySQL Version: %s' . PHP_EOL, $this->get_mysql_version());
        $message .= sprintf('- Memory Limit: %s' . PHP_EOL, WP_MEMORY_LIMIT);
        $message .= sprintf('- Max Execution Time: %s' . PHP_EOL, ini_get('max_execution_time'));

        $this->log($message);
    }

    /**
     * Log migration error.
     *
     * @since 2.0.0
     * @param array $errors Error messages
     * @return void
     */
    private function log_migration_error(array $errors): void {
        $message = 'Migration failed:' . PHP_EOL;

        foreach ($errors as $error) {
            $message .= '- ' . $error . PHP_EOL;
        }

        $this->log($message, 'error');
    }

    /**
     * Get MySQL version.
     *
     * @since 2.0.0
     * @return string
     */
    private function get_mysql_version(): string {
        global $wpdb;
        return $wpdb->get_var('SELECT VERSION()');
    }

    /**
     * Log a message.
     *
     * @since 2.0.0
     * @param string $message Message to log
     * @param string $level Log level
     * @return void
     */
    private function log(string $message, string $level = 'info'): void {
        $log = [
            'message' => $message,
            'level' => $level,
            'timestamp' => current_time('mysql')
        ];

        $logs = get_option('easy_invoice_migration_logs', []);
        $logs[] = $log;

        update_option('easy_invoice_migration_logs', $logs);
    }
}

```
