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