Completed
Pull Request — master (#8121)
by Joas
21:13
created
lib/private/DB/MigrationService.php 3 patches
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -31,7 +31,6 @@
 block discarded – undo
31 31
 use OCP\IDBConnection;
32 32
 use OCP\Migration\IMigrationStep;
33 33
 use OCP\Migration\IOutput;
34
-use Doctrine\DBAL\Schema\Column;
35 34
 use Doctrine\DBAL\Schema\Table;
36 35
 use Doctrine\DBAL\Types\Type;
37 36
 
Please login to merge, or discard this patch.
Indentation   +408 added lines, -408 removed lines patch added patch discarded remove patch
@@ -37,412 +37,412 @@
 block discarded – undo
37 37
 
38 38
 class MigrationService {
39 39
 
40
-	/** @var boolean */
41
-	private $migrationTableCreated;
42
-	/** @var array */
43
-	private $migrations;
44
-	/** @var IOutput */
45
-	private $output;
46
-	/** @var Connection */
47
-	private $connection;
48
-	/** @var string */
49
-	private $appName;
50
-
51
-	/**
52
-	 * MigrationService constructor.
53
-	 *
54
-	 * @param $appName
55
-	 * @param IDBConnection $connection
56
-	 * @param AppLocator $appLocator
57
-	 * @param IOutput|null $output
58
-	 * @throws \Exception
59
-	 */
60
-	public function __construct($appName, IDBConnection $connection, IOutput $output = null, AppLocator $appLocator = null) {
61
-		$this->appName = $appName;
62
-		$this->connection = $connection;
63
-		$this->output = $output;
64
-		if (null === $this->output) {
65
-			$this->output = new SimpleOutput(\OC::$server->getLogger(), $appName);
66
-		}
67
-
68
-		if ($appName === 'core') {
69
-			$this->migrationsPath = \OC::$SERVERROOT . '/core/Migrations';
70
-			$this->migrationsNamespace = 'OC\\Core\\Migrations';
71
-		} else {
72
-			if (null === $appLocator) {
73
-				$appLocator = new AppLocator();
74
-			}
75
-			$appPath = $appLocator->getAppPath($appName);
76
-			$namespace = App::buildAppNamespace($appName);
77
-			$this->migrationsPath = "$appPath/lib/Migration";
78
-			$this->migrationsNamespace = $namespace . '\\Migration';
79
-		}
80
-	}
81
-
82
-	/**
83
-	 * Returns the name of the app for which this migration is executed
84
-	 *
85
-	 * @return string
86
-	 */
87
-	public function getApp() {
88
-		return $this->appName;
89
-	}
90
-
91
-	/**
92
-	 * @return bool
93
-	 * @codeCoverageIgnore - this will implicitly tested on installation
94
-	 */
95
-	private function createMigrationTable() {
96
-		if ($this->migrationTableCreated) {
97
-			return false;
98
-		}
99
-
100
-		$schema = new SchemaWrapper($this->connection);
101
-
102
-		/**
103
-		 * We drop the table when it has different columns or the definition does not
104
-		 * match. E.g. ownCloud uses a length of 177 for app and 14 for version.
105
-		 */
106
-		try {
107
-			$table = $schema->getTable('migrations');
108
-			$columns = $table->getColumns();
109
-
110
-			if (count($columns) === 2) {
111
-				try {
112
-					$column = $table->getColumn('app');
113
-					$schemaMismatch = $column->getLength() !== 255;
114
-
115
-					if (!$schemaMismatch) {
116
-						$column = $table->getColumn('version');
117
-						$schemaMismatch = $column->getLength() !== 255;
118
-					}
119
-				} catch (SchemaException $e) {
120
-					// One of the columns is missing
121
-					$schemaMismatch = true;
122
-				}
123
-
124
-				if (!$schemaMismatch) {
125
-					// Table exists and schema matches: return back!
126
-					$this->migrationTableCreated = true;
127
-					return false;
128
-				}
129
-			}
130
-
131
-			// Drop the table, when it didn't match our expectations.
132
-			$this->connection->dropTable('migrations');
133
-
134
-			// Recreate the schema after the table was dropped.
135
-			$schema = new SchemaWrapper($this->connection);
136
-
137
-		} catch (SchemaException $e) {
138
-			// Table not found, no need to panic, we will create it.
139
-		}
140
-
141
-		$table = $schema->createTable('migrations');
142
-		$table->addColumn('app', Type::STRING, ['length' => 255]);
143
-		$table->addColumn('version', Type::STRING, ['length' => 255]);
144
-		$table->setPrimaryKey(['app', 'version']);
145
-
146
-		$this->connection->migrateToSchema($schema->getWrappedSchema());
147
-
148
-		$this->migrationTableCreated = true;
149
-
150
-		return true;
151
-	}
152
-
153
-	/**
154
-	 * Returns all versions which have already been applied
155
-	 *
156
-	 * @return string[]
157
-	 * @codeCoverageIgnore - no need to test this
158
-	 */
159
-	public function getMigratedVersions() {
160
-		$this->createMigrationTable();
161
-		$qb = $this->connection->getQueryBuilder();
162
-
163
-		$qb->select('version')
164
-			->from('migrations')
165
-			->where($qb->expr()->eq('app', $qb->createNamedParameter($this->getApp())))
166
-			->orderBy('version');
167
-
168
-		$result = $qb->execute();
169
-		$rows = $result->fetchAll(\PDO::FETCH_COLUMN);
170
-		$result->closeCursor();
171
-
172
-		return $rows;
173
-	}
174
-
175
-	/**
176
-	 * Returns all versions which are available in the migration folder
177
-	 *
178
-	 * @return array
179
-	 */
180
-	public function getAvailableVersions() {
181
-		$this->ensureMigrationsAreLoaded();
182
-		return array_map('strval', array_keys($this->migrations));
183
-	}
184
-
185
-	protected function findMigrations() {
186
-		$directory = realpath($this->migrationsPath);
187
-		if ($directory === false || !file_exists($directory) || !is_dir($directory)) {
188
-			return [];
189
-		}
190
-
191
-		$iterator = new \RegexIterator(
192
-			new \RecursiveIteratorIterator(
193
-				new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS),
194
-				\RecursiveIteratorIterator::LEAVES_ONLY
195
-			),
196
-			'#^.+\\/Version[^\\/]{1,255}\\.php$#i',
197
-			\RegexIterator::GET_MATCH);
198
-
199
-		$files = array_keys(iterator_to_array($iterator));
200
-		uasort($files, function ($a, $b) {
201
-			preg_match('/^Version(\d+)Date(\d+)\\.php$/', basename($a), $matchA);
202
-			preg_match('/^Version(\d+)Date(\d+)\\.php$/', basename($b), $matchB);
203
-			if (!empty($matchA) && !empty($matchB)) {
204
-				if ($matchA[1] !== $matchB[1]) {
205
-					return ($matchA[1] < $matchB[1]) ? -1 : 1;
206
-				}
207
-				return ($matchA[2] < $matchB[2]) ? -1 : 1;
208
-			}
209
-			return (basename($a) < basename($b)) ? -1 : 1;
210
-		});
211
-
212
-		$migrations = [];
213
-
214
-		foreach ($files as $file) {
215
-			$className = basename($file, '.php');
216
-			$version = (string) substr($className, 7);
217
-			if ($version === '0') {
218
-				throw new \InvalidArgumentException(
219
-					"Cannot load a migrations with the name '$version' because it is a reserved number"
220
-				);
221
-			}
222
-			$migrations[$version] = sprintf('%s\\%s', $this->migrationsNamespace, $className);
223
-		}
224
-
225
-		return $migrations;
226
-	}
227
-
228
-	/**
229
-	 * @param string $to
230
-	 * @return string[]
231
-	 */
232
-	private function getMigrationsToExecute($to) {
233
-		$knownMigrations = $this->getMigratedVersions();
234
-		$availableMigrations = $this->getAvailableVersions();
235
-
236
-		$toBeExecuted = [];
237
-		foreach ($availableMigrations as $v) {
238
-			if ($to !== 'latest' && $v > $to) {
239
-				continue;
240
-			}
241
-			if ($this->shallBeExecuted($v, $knownMigrations)) {
242
-				$toBeExecuted[] = $v;
243
-			}
244
-		}
245
-
246
-		return $toBeExecuted;
247
-	}
248
-
249
-	/**
250
-	 * @param string $m
251
-	 * @param string[] $knownMigrations
252
-	 * @return bool
253
-	 */
254
-	private function shallBeExecuted($m, $knownMigrations) {
255
-		if (in_array($m, $knownMigrations)) {
256
-			return false;
257
-		}
258
-
259
-		return true;
260
-	}
261
-
262
-	/**
263
-	 * @param string $version
264
-	 */
265
-	private function markAsExecuted($version) {
266
-		$this->connection->insertIfNotExist('*PREFIX*migrations', [
267
-			'app' => $this->appName,
268
-			'version' => $version
269
-		]);
270
-	}
271
-
272
-	/**
273
-	 * Returns the name of the table which holds the already applied versions
274
-	 *
275
-	 * @return string
276
-	 */
277
-	public function getMigrationsTableName() {
278
-		return $this->connection->getPrefix() . 'migrations';
279
-	}
280
-
281
-	/**
282
-	 * Returns the namespace of the version classes
283
-	 *
284
-	 * @return string
285
-	 */
286
-	public function getMigrationsNamespace() {
287
-		return $this->migrationsNamespace;
288
-	}
289
-
290
-	/**
291
-	 * Returns the directory which holds the versions
292
-	 *
293
-	 * @return string
294
-	 */
295
-	public function getMigrationsDirectory() {
296
-		return $this->migrationsPath;
297
-	}
298
-
299
-	/**
300
-	 * Return the explicit version for the aliases; current, next, prev, latest
301
-	 *
302
-	 * @param string $alias
303
-	 * @return mixed|null|string
304
-	 */
305
-	public function getMigration($alias) {
306
-		switch($alias) {
307
-			case 'current':
308
-				return $this->getCurrentVersion();
309
-			case 'next':
310
-				return $this->getRelativeVersion($this->getCurrentVersion(), 1);
311
-			case 'prev':
312
-				return $this->getRelativeVersion($this->getCurrentVersion(), -1);
313
-			case 'latest':
314
-				$this->ensureMigrationsAreLoaded();
315
-
316
-				$migrations = $this->getAvailableVersions();
317
-				return @end($migrations);
318
-		}
319
-		return '0';
320
-	}
321
-
322
-	/**
323
-	 * @param string $version
324
-	 * @param int $delta
325
-	 * @return null|string
326
-	 */
327
-	private function getRelativeVersion($version, $delta) {
328
-		$this->ensureMigrationsAreLoaded();
329
-
330
-		$versions = $this->getAvailableVersions();
331
-		array_unshift($versions, 0);
332
-		$offset = array_search($version, $versions, true);
333
-		if ($offset === false || !isset($versions[$offset + $delta])) {
334
-			// Unknown version or delta out of bounds.
335
-			return null;
336
-		}
337
-
338
-		return (string) $versions[$offset + $delta];
339
-	}
340
-
341
-	/**
342
-	 * @return string
343
-	 */
344
-	private function getCurrentVersion() {
345
-		$m = $this->getMigratedVersions();
346
-		if (count($m) === 0) {
347
-			return '0';
348
-		}
349
-		$migrations = array_values($m);
350
-		return @end($migrations);
351
-	}
352
-
353
-	/**
354
-	 * @param string $version
355
-	 * @return string
356
-	 * @throws \InvalidArgumentException
357
-	 */
358
-	private function getClass($version) {
359
-		$this->ensureMigrationsAreLoaded();
360
-
361
-		if (isset($this->migrations[$version])) {
362
-			return $this->migrations[$version];
363
-		}
364
-
365
-		throw new \InvalidArgumentException("Version $version is unknown.");
366
-	}
367
-
368
-	/**
369
-	 * Allows to set an IOutput implementation which is used for logging progress and messages
370
-	 *
371
-	 * @param IOutput $output
372
-	 */
373
-	public function setOutput(IOutput $output) {
374
-		$this->output = $output;
375
-	}
376
-
377
-	/**
378
-	 * Applies all not yet applied versions up to $to
379
-	 *
380
-	 * @param string $to
381
-	 * @throws \InvalidArgumentException
382
-	 */
383
-	public function migrate($to = 'latest') {
384
-		// read known migrations
385
-		$toBeExecuted = $this->getMigrationsToExecute($to);
386
-		foreach ($toBeExecuted as $version) {
387
-			$this->executeStep($version);
388
-		}
389
-	}
390
-
391
-	/**
392
-	 * @param string $version
393
-	 * @return mixed
394
-	 * @throws \InvalidArgumentException
395
-	 */
396
-	protected function createInstance($version) {
397
-		$class = $this->getClass($version);
398
-		try {
399
-			$s = \OC::$server->query($class);
400
-		} catch (QueryException $e) {
401
-			if (class_exists($class)) {
402
-				$s = new $class();
403
-			} else {
404
-				throw new \InvalidArgumentException("Migration step '$class' is unknown");
405
-			}
406
-		}
407
-
408
-		return $s;
409
-	}
410
-
411
-	/**
412
-	 * Executes one explicit version
413
-	 *
414
-	 * @param string $version
415
-	 * @throws \InvalidArgumentException
416
-	 */
417
-	public function executeStep($version) {
418
-		$instance = $this->createInstance($version);
419
-		if (!$instance instanceof IMigrationStep) {
420
-			throw new \InvalidArgumentException('Not a valid migration');
421
-		}
422
-
423
-		$instance->preSchemaChange($this->output, function() {
424
-			return new SchemaWrapper($this->connection);
425
-		}, ['tablePrefix' => $this->connection->getPrefix()]);
426
-
427
-		$toSchema = $instance->changeSchema($this->output, function() {
428
-			return new SchemaWrapper($this->connection);
429
-		}, ['tablePrefix' => $this->connection->getPrefix()]);
430
-
431
-		if ($toSchema instanceof SchemaWrapper) {
432
-			$this->connection->migrateToSchema($toSchema->getWrappedSchema());
433
-			$toSchema->performDropTableCalls();
434
-		}
435
-
436
-		$instance->postSchemaChange($this->output, function() {
437
-			return new SchemaWrapper($this->connection);
438
-		}, ['tablePrefix' => $this->connection->getPrefix()]);
439
-
440
-		$this->markAsExecuted($version);
441
-	}
442
-
443
-	private function ensureMigrationsAreLoaded() {
444
-		if (empty($this->migrations)) {
445
-			$this->migrations = $this->findMigrations();
446
-		}
447
-	}
40
+    /** @var boolean */
41
+    private $migrationTableCreated;
42
+    /** @var array */
43
+    private $migrations;
44
+    /** @var IOutput */
45
+    private $output;
46
+    /** @var Connection */
47
+    private $connection;
48
+    /** @var string */
49
+    private $appName;
50
+
51
+    /**
52
+     * MigrationService constructor.
53
+     *
54
+     * @param $appName
55
+     * @param IDBConnection $connection
56
+     * @param AppLocator $appLocator
57
+     * @param IOutput|null $output
58
+     * @throws \Exception
59
+     */
60
+    public function __construct($appName, IDBConnection $connection, IOutput $output = null, AppLocator $appLocator = null) {
61
+        $this->appName = $appName;
62
+        $this->connection = $connection;
63
+        $this->output = $output;
64
+        if (null === $this->output) {
65
+            $this->output = new SimpleOutput(\OC::$server->getLogger(), $appName);
66
+        }
67
+
68
+        if ($appName === 'core') {
69
+            $this->migrationsPath = \OC::$SERVERROOT . '/core/Migrations';
70
+            $this->migrationsNamespace = 'OC\\Core\\Migrations';
71
+        } else {
72
+            if (null === $appLocator) {
73
+                $appLocator = new AppLocator();
74
+            }
75
+            $appPath = $appLocator->getAppPath($appName);
76
+            $namespace = App::buildAppNamespace($appName);
77
+            $this->migrationsPath = "$appPath/lib/Migration";
78
+            $this->migrationsNamespace = $namespace . '\\Migration';
79
+        }
80
+    }
81
+
82
+    /**
83
+     * Returns the name of the app for which this migration is executed
84
+     *
85
+     * @return string
86
+     */
87
+    public function getApp() {
88
+        return $this->appName;
89
+    }
90
+
91
+    /**
92
+     * @return bool
93
+     * @codeCoverageIgnore - this will implicitly tested on installation
94
+     */
95
+    private function createMigrationTable() {
96
+        if ($this->migrationTableCreated) {
97
+            return false;
98
+        }
99
+
100
+        $schema = new SchemaWrapper($this->connection);
101
+
102
+        /**
103
+         * We drop the table when it has different columns or the definition does not
104
+         * match. E.g. ownCloud uses a length of 177 for app and 14 for version.
105
+         */
106
+        try {
107
+            $table = $schema->getTable('migrations');
108
+            $columns = $table->getColumns();
109
+
110
+            if (count($columns) === 2) {
111
+                try {
112
+                    $column = $table->getColumn('app');
113
+                    $schemaMismatch = $column->getLength() !== 255;
114
+
115
+                    if (!$schemaMismatch) {
116
+                        $column = $table->getColumn('version');
117
+                        $schemaMismatch = $column->getLength() !== 255;
118
+                    }
119
+                } catch (SchemaException $e) {
120
+                    // One of the columns is missing
121
+                    $schemaMismatch = true;
122
+                }
123
+
124
+                if (!$schemaMismatch) {
125
+                    // Table exists and schema matches: return back!
126
+                    $this->migrationTableCreated = true;
127
+                    return false;
128
+                }
129
+            }
130
+
131
+            // Drop the table, when it didn't match our expectations.
132
+            $this->connection->dropTable('migrations');
133
+
134
+            // Recreate the schema after the table was dropped.
135
+            $schema = new SchemaWrapper($this->connection);
136
+
137
+        } catch (SchemaException $e) {
138
+            // Table not found, no need to panic, we will create it.
139
+        }
140
+
141
+        $table = $schema->createTable('migrations');
142
+        $table->addColumn('app', Type::STRING, ['length' => 255]);
143
+        $table->addColumn('version', Type::STRING, ['length' => 255]);
144
+        $table->setPrimaryKey(['app', 'version']);
145
+
146
+        $this->connection->migrateToSchema($schema->getWrappedSchema());
147
+
148
+        $this->migrationTableCreated = true;
149
+
150
+        return true;
151
+    }
152
+
153
+    /**
154
+     * Returns all versions which have already been applied
155
+     *
156
+     * @return string[]
157
+     * @codeCoverageIgnore - no need to test this
158
+     */
159
+    public function getMigratedVersions() {
160
+        $this->createMigrationTable();
161
+        $qb = $this->connection->getQueryBuilder();
162
+
163
+        $qb->select('version')
164
+            ->from('migrations')
165
+            ->where($qb->expr()->eq('app', $qb->createNamedParameter($this->getApp())))
166
+            ->orderBy('version');
167
+
168
+        $result = $qb->execute();
169
+        $rows = $result->fetchAll(\PDO::FETCH_COLUMN);
170
+        $result->closeCursor();
171
+
172
+        return $rows;
173
+    }
174
+
175
+    /**
176
+     * Returns all versions which are available in the migration folder
177
+     *
178
+     * @return array
179
+     */
180
+    public function getAvailableVersions() {
181
+        $this->ensureMigrationsAreLoaded();
182
+        return array_map('strval', array_keys($this->migrations));
183
+    }
184
+
185
+    protected function findMigrations() {
186
+        $directory = realpath($this->migrationsPath);
187
+        if ($directory === false || !file_exists($directory) || !is_dir($directory)) {
188
+            return [];
189
+        }
190
+
191
+        $iterator = new \RegexIterator(
192
+            new \RecursiveIteratorIterator(
193
+                new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS),
194
+                \RecursiveIteratorIterator::LEAVES_ONLY
195
+            ),
196
+            '#^.+\\/Version[^\\/]{1,255}\\.php$#i',
197
+            \RegexIterator::GET_MATCH);
198
+
199
+        $files = array_keys(iterator_to_array($iterator));
200
+        uasort($files, function ($a, $b) {
201
+            preg_match('/^Version(\d+)Date(\d+)\\.php$/', basename($a), $matchA);
202
+            preg_match('/^Version(\d+)Date(\d+)\\.php$/', basename($b), $matchB);
203
+            if (!empty($matchA) && !empty($matchB)) {
204
+                if ($matchA[1] !== $matchB[1]) {
205
+                    return ($matchA[1] < $matchB[1]) ? -1 : 1;
206
+                }
207
+                return ($matchA[2] < $matchB[2]) ? -1 : 1;
208
+            }
209
+            return (basename($a) < basename($b)) ? -1 : 1;
210
+        });
211
+
212
+        $migrations = [];
213
+
214
+        foreach ($files as $file) {
215
+            $className = basename($file, '.php');
216
+            $version = (string) substr($className, 7);
217
+            if ($version === '0') {
218
+                throw new \InvalidArgumentException(
219
+                    "Cannot load a migrations with the name '$version' because it is a reserved number"
220
+                );
221
+            }
222
+            $migrations[$version] = sprintf('%s\\%s', $this->migrationsNamespace, $className);
223
+        }
224
+
225
+        return $migrations;
226
+    }
227
+
228
+    /**
229
+     * @param string $to
230
+     * @return string[]
231
+     */
232
+    private function getMigrationsToExecute($to) {
233
+        $knownMigrations = $this->getMigratedVersions();
234
+        $availableMigrations = $this->getAvailableVersions();
235
+
236
+        $toBeExecuted = [];
237
+        foreach ($availableMigrations as $v) {
238
+            if ($to !== 'latest' && $v > $to) {
239
+                continue;
240
+            }
241
+            if ($this->shallBeExecuted($v, $knownMigrations)) {
242
+                $toBeExecuted[] = $v;
243
+            }
244
+        }
245
+
246
+        return $toBeExecuted;
247
+    }
248
+
249
+    /**
250
+     * @param string $m
251
+     * @param string[] $knownMigrations
252
+     * @return bool
253
+     */
254
+    private function shallBeExecuted($m, $knownMigrations) {
255
+        if (in_array($m, $knownMigrations)) {
256
+            return false;
257
+        }
258
+
259
+        return true;
260
+    }
261
+
262
+    /**
263
+     * @param string $version
264
+     */
265
+    private function markAsExecuted($version) {
266
+        $this->connection->insertIfNotExist('*PREFIX*migrations', [
267
+            'app' => $this->appName,
268
+            'version' => $version
269
+        ]);
270
+    }
271
+
272
+    /**
273
+     * Returns the name of the table which holds the already applied versions
274
+     *
275
+     * @return string
276
+     */
277
+    public function getMigrationsTableName() {
278
+        return $this->connection->getPrefix() . 'migrations';
279
+    }
280
+
281
+    /**
282
+     * Returns the namespace of the version classes
283
+     *
284
+     * @return string
285
+     */
286
+    public function getMigrationsNamespace() {
287
+        return $this->migrationsNamespace;
288
+    }
289
+
290
+    /**
291
+     * Returns the directory which holds the versions
292
+     *
293
+     * @return string
294
+     */
295
+    public function getMigrationsDirectory() {
296
+        return $this->migrationsPath;
297
+    }
298
+
299
+    /**
300
+     * Return the explicit version for the aliases; current, next, prev, latest
301
+     *
302
+     * @param string $alias
303
+     * @return mixed|null|string
304
+     */
305
+    public function getMigration($alias) {
306
+        switch($alias) {
307
+            case 'current':
308
+                return $this->getCurrentVersion();
309
+            case 'next':
310
+                return $this->getRelativeVersion($this->getCurrentVersion(), 1);
311
+            case 'prev':
312
+                return $this->getRelativeVersion($this->getCurrentVersion(), -1);
313
+            case 'latest':
314
+                $this->ensureMigrationsAreLoaded();
315
+
316
+                $migrations = $this->getAvailableVersions();
317
+                return @end($migrations);
318
+        }
319
+        return '0';
320
+    }
321
+
322
+    /**
323
+     * @param string $version
324
+     * @param int $delta
325
+     * @return null|string
326
+     */
327
+    private function getRelativeVersion($version, $delta) {
328
+        $this->ensureMigrationsAreLoaded();
329
+
330
+        $versions = $this->getAvailableVersions();
331
+        array_unshift($versions, 0);
332
+        $offset = array_search($version, $versions, true);
333
+        if ($offset === false || !isset($versions[$offset + $delta])) {
334
+            // Unknown version or delta out of bounds.
335
+            return null;
336
+        }
337
+
338
+        return (string) $versions[$offset + $delta];
339
+    }
340
+
341
+    /**
342
+     * @return string
343
+     */
344
+    private function getCurrentVersion() {
345
+        $m = $this->getMigratedVersions();
346
+        if (count($m) === 0) {
347
+            return '0';
348
+        }
349
+        $migrations = array_values($m);
350
+        return @end($migrations);
351
+    }
352
+
353
+    /**
354
+     * @param string $version
355
+     * @return string
356
+     * @throws \InvalidArgumentException
357
+     */
358
+    private function getClass($version) {
359
+        $this->ensureMigrationsAreLoaded();
360
+
361
+        if (isset($this->migrations[$version])) {
362
+            return $this->migrations[$version];
363
+        }
364
+
365
+        throw new \InvalidArgumentException("Version $version is unknown.");
366
+    }
367
+
368
+    /**
369
+     * Allows to set an IOutput implementation which is used for logging progress and messages
370
+     *
371
+     * @param IOutput $output
372
+     */
373
+    public function setOutput(IOutput $output) {
374
+        $this->output = $output;
375
+    }
376
+
377
+    /**
378
+     * Applies all not yet applied versions up to $to
379
+     *
380
+     * @param string $to
381
+     * @throws \InvalidArgumentException
382
+     */
383
+    public function migrate($to = 'latest') {
384
+        // read known migrations
385
+        $toBeExecuted = $this->getMigrationsToExecute($to);
386
+        foreach ($toBeExecuted as $version) {
387
+            $this->executeStep($version);
388
+        }
389
+    }
390
+
391
+    /**
392
+     * @param string $version
393
+     * @return mixed
394
+     * @throws \InvalidArgumentException
395
+     */
396
+    protected function createInstance($version) {
397
+        $class = $this->getClass($version);
398
+        try {
399
+            $s = \OC::$server->query($class);
400
+        } catch (QueryException $e) {
401
+            if (class_exists($class)) {
402
+                $s = new $class();
403
+            } else {
404
+                throw new \InvalidArgumentException("Migration step '$class' is unknown");
405
+            }
406
+        }
407
+
408
+        return $s;
409
+    }
410
+
411
+    /**
412
+     * Executes one explicit version
413
+     *
414
+     * @param string $version
415
+     * @throws \InvalidArgumentException
416
+     */
417
+    public function executeStep($version) {
418
+        $instance = $this->createInstance($version);
419
+        if (!$instance instanceof IMigrationStep) {
420
+            throw new \InvalidArgumentException('Not a valid migration');
421
+        }
422
+
423
+        $instance->preSchemaChange($this->output, function() {
424
+            return new SchemaWrapper($this->connection);
425
+        }, ['tablePrefix' => $this->connection->getPrefix()]);
426
+
427
+        $toSchema = $instance->changeSchema($this->output, function() {
428
+            return new SchemaWrapper($this->connection);
429
+        }, ['tablePrefix' => $this->connection->getPrefix()]);
430
+
431
+        if ($toSchema instanceof SchemaWrapper) {
432
+            $this->connection->migrateToSchema($toSchema->getWrappedSchema());
433
+            $toSchema->performDropTableCalls();
434
+        }
435
+
436
+        $instance->postSchemaChange($this->output, function() {
437
+            return new SchemaWrapper($this->connection);
438
+        }, ['tablePrefix' => $this->connection->getPrefix()]);
439
+
440
+        $this->markAsExecuted($version);
441
+    }
442
+
443
+    private function ensureMigrationsAreLoaded() {
444
+        if (empty($this->migrations)) {
445
+            $this->migrations = $this->findMigrations();
446
+        }
447
+    }
448 448
 }
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -66,7 +66,7 @@  discard block
 block discarded – undo
66 66
 		}
67 67
 
68 68
 		if ($appName === 'core') {
69
-			$this->migrationsPath = \OC::$SERVERROOT . '/core/Migrations';
69
+			$this->migrationsPath = \OC::$SERVERROOT.'/core/Migrations';
70 70
 			$this->migrationsNamespace = 'OC\\Core\\Migrations';
71 71
 		} else {
72 72
 			if (null === $appLocator) {
@@ -75,7 +75,7 @@  discard block
 block discarded – undo
75 75
 			$appPath = $appLocator->getAppPath($appName);
76 76
 			$namespace = App::buildAppNamespace($appName);
77 77
 			$this->migrationsPath = "$appPath/lib/Migration";
78
-			$this->migrationsNamespace = $namespace . '\\Migration';
78
+			$this->migrationsNamespace = $namespace.'\\Migration';
79 79
 		}
80 80
 	}
81 81
 
@@ -197,7 +197,7 @@  discard block
 block discarded – undo
197 197
 			\RegexIterator::GET_MATCH);
198 198
 
199 199
 		$files = array_keys(iterator_to_array($iterator));
200
-		uasort($files, function ($a, $b) {
200
+		uasort($files, function($a, $b) {
201 201
 			preg_match('/^Version(\d+)Date(\d+)\\.php$/', basename($a), $matchA);
202 202
 			preg_match('/^Version(\d+)Date(\d+)\\.php$/', basename($b), $matchB);
203 203
 			if (!empty($matchA) && !empty($matchB)) {
@@ -275,7 +275,7 @@  discard block
 block discarded – undo
275 275
 	 * @return string
276 276
 	 */
277 277
 	public function getMigrationsTableName() {
278
-		return $this->connection->getPrefix() . 'migrations';
278
+		return $this->connection->getPrefix().'migrations';
279 279
 	}
280 280
 
281 281
 	/**
@@ -303,7 +303,7 @@  discard block
 block discarded – undo
303 303
 	 * @return mixed|null|string
304 304
 	 */
305 305
 	public function getMigration($alias) {
306
-		switch($alias) {
306
+		switch ($alias) {
307 307
 			case 'current':
308 308
 				return $this->getCurrentVersion();
309 309
 			case 'next':
Please login to merge, or discard this patch.