Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
100.00% |
82 / 82 |
|
90.91% |
40 / 44 |
|
60.00% |
18 / 30 |
|
62.50% |
5 / 8 |
CRAP | |
0.00% |
0 / 1 |
| Executor | |
100.00% |
82 / 82 |
|
90.91% |
40 / 44 |
|
60.00% |
18 / 30 |
|
100.00% |
8 / 8 |
49.22 | |
100.00% |
1 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| migrate | |
100.00% |
12 / 12 |
|
84.62% |
11 / 13 |
|
45.45% |
5 / 11 |
|
100.00% |
1 / 1 |
11.84 | |||
| migrateSQL | |
100.00% |
5 / 5 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| migrateCompiledSQL | |
100.00% |
8 / 8 |
|
83.33% |
5 / 6 |
|
50.00% |
2 / 4 |
|
100.00% |
1 / 1 |
2.50 | |||
| migrateTPQL | |
100.00% |
28 / 28 |
|
91.67% |
11 / 12 |
|
33.33% |
2 / 6 |
|
100.00% |
1 / 1 |
8.74 | |||
| migratePHP | |
100.00% |
8 / 8 |
|
100.00% |
3 / 3 |
|
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| showEmptyMessage | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| showMessage | |
100.00% |
15 / 15 |
|
100.00% |
5 / 5 |
|
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Celemas\Quma\Migrations; |
| 6 | |
| 7 | use Celemas\Quma\Environment; |
| 8 | use RuntimeException; |
| 9 | use Throwable; |
| 10 | |
| 11 | final readonly class Executor |
| 12 | { |
| 13 | public const string STARTED = 'start'; |
| 14 | public const string ERROR = 'error'; |
| 15 | public const string WARNING = 'warning'; |
| 16 | public const string SUCCESS = 'success'; |
| 17 | |
| 18 | public function __construct( |
| 19 | private Environment $env, |
| 20 | private Log $log, |
| 21 | private PhpLoader $phpLoader, |
| 22 | ) {} |
| 23 | |
| 24 | public function migrate(string $namespace, string $migration, bool $showStacktrace): string |
| 25 | { |
| 26 | $script = file_get_contents($migration); |
| 27 | |
| 28 | if ($script === false) { |
| 29 | $this->showMessage($migration, new RuntimeException('Could not read migration file')); |
| 30 | |
| 31 | return self::ERROR; |
| 32 | } |
| 33 | |
| 34 | if (trim($script) === '') { |
| 35 | $this->showEmptyMessage($migration); |
| 36 | |
| 37 | return self::WARNING; |
| 38 | } |
| 39 | |
| 40 | return match (pathinfo($migration, PATHINFO_EXTENSION)) { |
| 41 | 'sql' => $this->migrateSQL($namespace, $migration, $script, $showStacktrace), |
| 42 | 'tpql' => $this->migrateTPQL($namespace, $migration, $showStacktrace), |
| 43 | 'php' => $this->migratePHP($namespace, $migration, $showStacktrace), |
| 44 | }; |
| 45 | } |
| 46 | |
| 47 | private function migrateSQL( |
| 48 | string $namespace, |
| 49 | string $migration, |
| 50 | string $script, |
| 51 | bool $showStacktrace, |
| 52 | ): string { |
| 53 | try { |
| 54 | $script = $this->env->conn->config->placeholders?->compileSql($script, $migration) ?? $script; |
| 55 | |
| 56 | return $this->migrateCompiledSQL($namespace, $migration, $script); |
| 57 | } catch (Throwable $e) { |
| 58 | $this->showMessage($migration, $e, $showStacktrace); |
| 59 | |
| 60 | return self::ERROR; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | private function migrateCompiledSQL( |
| 65 | string $namespace, |
| 66 | string $migration, |
| 67 | string $script, |
| 68 | ): string { |
| 69 | if (trim($script) === '') { |
| 70 | $this->showEmptyMessage($migration); |
| 71 | |
| 72 | return self::WARNING; |
| 73 | } |
| 74 | |
| 75 | $db = $this->env->db; |
| 76 | $db->execute($script)->run(); |
| 77 | $this->log->record($db, $namespace, $migration); |
| 78 | $this->showMessage($migration); |
| 79 | |
| 80 | return self::SUCCESS; |
| 81 | } |
| 82 | |
| 83 | private function migrateTPQL( |
| 84 | string $namespace, |
| 85 | string $migration, |
| 86 | bool $showStacktrace, |
| 87 | ): string { |
| 88 | try { |
| 89 | $db = $this->env->db; |
| 90 | $conn = $this->env->conn; |
| 91 | $context = [ |
| 92 | 'driver' => $db->getPdoDriver(), |
| 93 | 'db' => $db, |
| 94 | 'conn' => $conn, |
| 95 | ]; |
| 96 | |
| 97 | $executeTemplate = static function ( |
| 98 | string $templatePath, |
| 99 | array $context, |
| 100 | ): void { |
| 101 | extract($context, EXTR_SKIP); |
| 102 | |
| 103 | /** @psalm-suppress UnresolvableInclude */ |
| 104 | require $templatePath; |
| 105 | }; |
| 106 | |
| 107 | ob_start(); |
| 108 | $script = ''; |
| 109 | |
| 110 | try { |
| 111 | $executeTemplate($migration, $context); |
| 112 | $script = ob_get_contents(); |
| 113 | } finally { |
| 114 | ob_end_clean(); |
| 115 | } |
| 116 | |
| 117 | if (!is_string($script)) { |
| 118 | // Defensive guard for an impossible false from ob_get_contents() after ob_start(). |
| 119 | $script = ''; // @codeCoverageIgnore |
| 120 | } |
| 121 | |
| 122 | $script = $conn->config->placeholders?->compileSql($script, $migration) ?? $script; |
| 123 | |
| 124 | if (trim($script) === '') { |
| 125 | $this->showEmptyMessage($migration); |
| 126 | |
| 127 | return self::WARNING; |
| 128 | } |
| 129 | |
| 130 | return $this->migrateCompiledSQL($namespace, $migration, $script); |
| 131 | } catch (Throwable $e) { |
| 132 | $this->showMessage($migration, $e, $showStacktrace); |
| 133 | |
| 134 | return self::ERROR; |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | private function migratePHP( |
| 139 | string $namespace, |
| 140 | string $migration, |
| 141 | bool $showStacktrace, |
| 142 | ): string { |
| 143 | try { |
| 144 | $migrationObject = $this->phpLoader->load($migration); |
| 145 | $migrationObject->run($this->env); |
| 146 | $this->log->record($this->env->db, $namespace, $migration); |
| 147 | $this->showMessage($migration); |
| 148 | |
| 149 | return self::SUCCESS; |
| 150 | } catch (Throwable $e) { |
| 151 | $this->showMessage($migration, $e, $showStacktrace); |
| 152 | |
| 153 | return self::ERROR; |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | private function showEmptyMessage(string $migration): void |
| 158 | { |
| 159 | echo |
| 160 | "\033[33mWarning\033[0m: Migration '\033[1;33m" |
| 161 | . basename($migration) |
| 162 | . "'\033[0m is empty. Skipped\n" |
| 163 | ; |
| 164 | } |
| 165 | |
| 166 | private function showMessage( |
| 167 | string $migration, |
| 168 | ?Throwable $e = null, |
| 169 | bool $showStacktrace = false, |
| 170 | ): void { |
| 171 | if ($e) { |
| 172 | echo |
| 173 | "\033[1;31mError\033[0m: while working on migration '\033[1;33m" |
| 174 | . basename($migration) |
| 175 | . "\033[0m'\n" |
| 176 | ; |
| 177 | echo $e->getMessage() . "\n"; |
| 178 | |
| 179 | if ($showStacktrace) { |
| 180 | echo $e->getTraceAsString() . "\n"; |
| 181 | } |
| 182 | |
| 183 | return; |
| 184 | } |
| 185 | |
| 186 | echo |
| 187 | "\033[1;32mSuccess\033[0m: Migration '\033[1;33m" |
| 188 | . basename($migration) |
| 189 | . "\033[0m' successfully applied\n" |
| 190 | ; |
| 191 | } |
| 192 | } |
Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not
necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once.
Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement
always has an else as part of its logical flow even if you didn't write one.
| 19 | private Environment $env, |
| 20 | private Log $log, |
| 21 | private PhpLoader $phpLoader, |
| 22 | ) {} |
| 24 | public function migrate(string $namespace, string $migration, bool $showStacktrace): string |
| 25 | { |
| 26 | $script = file_get_contents($migration); |
| 27 | |
| 28 | if ($script === false) { |
| 29 | $this->showMessage($migration, new RuntimeException('Could not read migration file')); |
| 30 | |
| 31 | return self::ERROR; |
| 34 | if (trim($script) === '') { |
| 34 | if (trim($script) === '') { |
| 34 | if (trim($script) === '') { |
| 34 | if (trim($script) === '') { |
| 35 | $this->showEmptyMessage($migration); |
| 36 | |
| 37 | return self::WARNING; |
| 40 | return match (pathinfo($migration, PATHINFO_EXTENSION)) { |
| 40 | return match (pathinfo($migration, PATHINFO_EXTENSION)) { |
| 41 | 'sql' => $this->migrateSQL($namespace, $migration, $script, $showStacktrace), |
| 42 | 'tpql' => $this->migrateTPQL($namespace, $migration, $showStacktrace), |
| 43 | 'php' => $this->migratePHP($namespace, $migration, $showStacktrace), |
| 43 | 'php' => $this->migratePHP($namespace, $migration, $showStacktrace), |
| 44 | }; |
| 45 | } |
| 65 | string $namespace, |
| 66 | string $migration, |
| 67 | string $script, |
| 68 | ): string { |
| 69 | if (trim($script) === '') { |
| 69 | if (trim($script) === '') { |
| 69 | if (trim($script) === '') { |
| 69 | if (trim($script) === '') { |
| 70 | $this->showEmptyMessage($migration); |
| 71 | |
| 72 | return self::WARNING; |
| 75 | $db = $this->env->db; |
| 76 | $db->execute($script)->run(); |
| 77 | $this->log->record($db, $namespace, $migration); |
| 78 | $this->showMessage($migration); |
| 79 | |
| 80 | return self::SUCCESS; |
| 81 | } |
| 139 | string $namespace, |
| 140 | string $migration, |
| 141 | bool $showStacktrace, |
| 142 | ): string { |
| 143 | try { |
| 144 | $migrationObject = $this->phpLoader->load($migration); |
| 145 | $migrationObject->run($this->env); |
| 146 | $this->log->record($this->env->db, $namespace, $migration); |
| 147 | $this->showMessage($migration); |
| 148 | |
| 149 | return self::SUCCESS; |
| 150 | } catch (Throwable $e) { |
| 151 | $this->showMessage($migration, $e, $showStacktrace); |
| 152 | |
| 153 | return self::ERROR; |
| 154 | } |
| 155 | } |
| 48 | string $namespace, |
| 49 | string $migration, |
| 50 | string $script, |
| 51 | bool $showStacktrace, |
| 52 | ): string { |
| 53 | try { |
| 54 | $script = $this->env->conn->config->placeholders?->compileSql($script, $migration) ?? $script; |
| 55 | |
| 56 | return $this->migrateCompiledSQL($namespace, $migration, $script); |
| 57 | } catch (Throwable $e) { |
| 58 | $this->showMessage($migration, $e, $showStacktrace); |
| 59 | |
| 60 | return self::ERROR; |
| 61 | } |
| 62 | } |
| 84 | string $namespace, |
| 85 | string $migration, |
| 86 | bool $showStacktrace, |
| 87 | ): string { |
| 88 | try { |
| 89 | $db = $this->env->db; |
| 90 | $conn = $this->env->conn; |
| 91 | $context = [ |
| 92 | 'driver' => $db->getPdoDriver(), |
| 93 | 'db' => $db, |
| 94 | 'conn' => $conn, |
| 95 | ]; |
| 96 | |
| 97 | $executeTemplate = static function ( |
| 98 | string $templatePath, |
| 99 | array $context, |
| 100 | ): void { |
| 101 | extract($context, EXTR_SKIP); |
| 102 | |
| 103 | /** @psalm-suppress UnresolvableInclude */ |
| 104 | require $templatePath; |
| 105 | }; |
| 106 | |
| 107 | ob_start(); |
| 108 | $script = ''; |
| 109 | |
| 110 | try { |
| 111 | $executeTemplate($migration, $context); |
| 112 | $script = ob_get_contents(); |
| 113 | } finally { |
| 113 | } finally { |
| 114 | ob_end_clean(); |
| 117 | if (!is_string($script)) { |
| 122 | $script = $conn->config->placeholders?->compileSql($script, $migration) ?? $script; |
| 123 | |
| 124 | if (trim($script) === '') { |
| 124 | if (trim($script) === '') { |
| 124 | if (trim($script) === '') { |
| 124 | if (trim($script) === '') { |
| 125 | $this->showEmptyMessage($migration); |
| 126 | |
| 127 | return self::WARNING; |
| 130 | return $this->migrateCompiledSQL($namespace, $migration, $script); |
| 131 | } catch (Throwable $e) { |
| 132 | $this->showMessage($migration, $e, $showStacktrace); |
| 133 | |
| 134 | return self::ERROR; |
| 135 | } |
| 136 | } |
| 157 | private function showEmptyMessage(string $migration): void |
| 158 | { |
| 159 | echo |
| 160 | "\033[33mWarning\033[0m: Migration '\033[1;33m" |
| 161 | . basename($migration) |
| 162 | . "'\033[0m is empty. Skipped\n" |
| 163 | ; |
| 164 | } |
| 167 | string $migration, |
| 168 | ?Throwable $e = null, |
| 169 | bool $showStacktrace = false, |
| 170 | ): void { |
| 171 | if ($e) { |
| 174 | . basename($migration) |
| 175 | . "\033[0m'\n" |
| 176 | ; |
| 177 | echo $e->getMessage() . "\n"; |
| 178 | |
| 179 | if ($showStacktrace) { |
| 180 | echo $e->getTraceAsString() . "\n"; |
| 181 | } |
| 182 | |
| 183 | return; |
| 183 | return; |
| 188 | . basename($migration) |
| 189 | . "\033[0m' successfully applied\n" |
| 190 | ; |
| 191 | } |
| 98 | string $templatePath, |
| 99 | array $context, |
| 100 | ): void { |
| 101 | extract($context, EXTR_SKIP); |
| 102 | |
| 103 | /** @psalm-suppress UnresolvableInclude */ |
| 104 | require $templatePath; |
| 105 | }; |