Test Failed
Push — main ( ed15c3...2f7161 )
by Paul
12:26 queued 06:03
created

Migrate::run()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 6
rs 10
ccs 2
cts 2
cp 1
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use GeminiLabs\SiteReviews\Contracts\MigrateContract;
6
use GeminiLabs\SiteReviews\Database;
7
use GeminiLabs\SiteReviews\Database\OptionManager;
8
use GeminiLabs\SiteReviews\Helpers\Arr;
9
use GeminiLabs\SiteReviews\Helpers\Cast;
10
11
/**
12
 * Do not use OptionManager non-static methods here!
13
 */
14
class Migrate
15
{
16
    public array $migrations;
17
    public string $migrationsKey;
18
    public string $migrationsLastRun;
19
20
    public function __construct()
21
    {
22
        $this->migrations = $this->allMigrations();
23
        $this->migrationsKey = glsr()->prefix.'migrations';
24
        $this->migrationsLastRun = glsr()->prefix.'last_migration_run';
25
    }
26
27
    public function isMigrationNeeded(): bool
28 31
    {
29
        if (empty($this->migrations)) {
30 31
            return false;
31 31
        }
32 31
        if (!empty($this->pendingMigrations())) {
33
            // check if this is a fresh install of the plugin
34
            return '0.0.0' !== $this->versionUpgradedFrom();
35
        }
36
        return false;
37
    }
38
39
    /**
40
     * Returns a UNIX timestamp
41
     */
42
    public function lastRun(): int
43
    {
44
        return Cast::toInt(get_option($this->migrationsLastRun));
45
    }
46
47
    /**
48
     * Used by Notices\MigrationNotice::class.
49
     */
50
    public function pendingVersions(): string
51
    {
52
        $versions = array_map(
53
            fn ($migration) => str_replace(['Migrate_', '_'], ['', '.'], $migration),
54
            $this->pendingMigrations()
55
        );
56
        return implode(', ', $versions);
57
    }
58
59
    public function reset(): void
60
    {
61
        delete_option($this->migrationsKey);
62
    }
63
64 23
    public function run(): void
65
    {
66 23
        if (glsr(Database::class)->isMigrationNeeded()) {
67
            $this->runAll();
68
        } else {
69
            $this->runMigrations();
70
        }
71
    }
72 8
73
    public function runAll(): void
74 8
    {
75
        $this->reset();
76
        $this->runMigrations();
77 8
    }
78
79
    protected function allMigrations(): array
80
    {
81
        $migrations = [];
82
        $dir = glsr()->path('plugin/Migrations');
83
        if (is_dir($dir)) {
84 23
            $iterator = new \DirectoryIterator($dir);
85
            foreach ($iterator as $fileinfo) {
86 23
                if ('file' === $fileinfo->getType()) {
87 23
                    $migrations[] = str_replace('.php', '', $fileinfo->getFilename());
88
                }
89
            }
90
            natsort($migrations);
91
        }
92
        return Arr::reindex($migrations);
93 31
    }
94
95 31
    protected function migrations(): array
96 31
    {
97 31
        $storedMigrations = Arr::consolidate(get_option($this->migrationsKey));
98 31
        if (!Arr::compare(array_keys($storedMigrations), array_values($this->migrations))) {
99 31
            $migrations = [];
100 31
            foreach ($this->migrations as $migration) {
101 31
                $migrations[$migration] = Arr::get($storedMigrations, $migration, false);
102
            }
103
            $storedMigrations = $migrations;
104 31
        }
105
        return array_map('wp_validate_boolean', $storedMigrations);
106 31
    }
107
108
    protected function pendingMigrations(array $migrations = []): array
109
    {
110
        if (empty($migrations)) {
111
            $migrations = $this->migrations();
112 23
        }
113
        return array_keys(array_filter($migrations, fn ($hasRun) => !$hasRun));
114 23
    }
115 23
116 23
    protected function runMigrations(): void
117
    {
118 23
        wp_raise_memory_limit('admin');
119
        $migrations = $this->migrations();
120
        glsr()->action('migration/start', $migrations);
121
        foreach ($this->pendingMigrations($migrations) as $migration) {
122
            if (class_exists($classname = "\GeminiLabs\SiteReviews\Migrations\\{$migration}")) {
123
                $instance = glsr($classname);
124 31
                if (!$instance instanceof MigrateContract) {
125
                    glsr_log()->debug("[$migration] was skipped");
126 31
                    continue;
127 31
                }
128 31
                if ($instance->run()) {
129 31
                    $migrations[$migration] = true;
130 31
                    glsr_log()->debug("[$migration] has run successfully");
131 31
                    continue;
132
                }
133
                glsr_log()->error("[$migration] was unsuccessful");
134
            }
135 31
        }
136
        $this->updateMigrationStatus($migrations);
137
        glsr()->action('migration/end', $migrations);
138
    }
139
140
    protected function updateMigrationStatus(array $migrations): void
141 31
    {
142
        update_option($this->migrationsKey, $migrations);
143 31
        update_option($this->migrationsLastRun, current_time('timestamp'));
144
    }
145
146 31
    protected function versionUpgradedFrom(): string
147 31
    {
148 31
        $settings = get_option(OptionManager::databaseKey());
149
        $version = Arr::getAs('string', $settings, 'version_upgraded_from');
150
        return $version ?: '0.0.0';
151
    }
152
}
153