|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Kaliop\eZMigrationBundle\Core\StorageHandler\Database; |
|
4
|
|
|
|
|
5
|
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException; |
|
6
|
|
|
use Doctrine\DBAL\Schema\Schema; |
|
7
|
|
|
use eZ\Publish\Core\Persistence\Database\DatabaseHandler; |
|
8
|
|
|
use eZ\Publish\Core\Persistence\Database\SelectQuery; |
|
9
|
|
|
use Kaliop\eZMigrationBundle\API\StorageHandlerInterface; |
|
10
|
|
|
use Kaliop\eZMigrationBundle\API\Collection\MigrationCollection; |
|
11
|
|
|
use Kaliop\eZMigrationBundle\API\Value\Migration as APIMigration; |
|
12
|
|
|
use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition; |
|
13
|
|
|
|
|
14
|
|
|
use Kaliop\eZMigrationBundle\API\ConfigResolverInterface; |
|
15
|
|
|
|
|
16
|
|
|
/** |
|
17
|
|
|
* Database-backed storage for info on executed migrations |
|
18
|
|
|
* |
|
19
|
|
|
* @todo replace all usage of the ezcdb api with the doctrine dbal one, so that we only depend on one |
|
20
|
|
|
*/ |
|
21
|
|
|
class Migration extends TableStorage implements StorageHandlerInterface |
|
22
|
|
|
{ |
|
23
|
|
|
protected $fieldList = 'migration, md5, path, execution_date, status, execution_error'; |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* @param DatabaseHandler $dbHandler |
|
27
|
|
|
* @param string $tableNameParameter |
|
28
|
|
|
* @param ConfigResolverInterface $configResolver |
|
29
|
|
|
* @throws \Exception |
|
30
|
|
|
*/ |
|
31
|
|
|
public function __construct(DatabaseHandler $dbHandler, $tableNameParameter = 'kaliop_migrations', ConfigResolverInterface $configResolver = null) |
|
32
|
|
|
{ |
|
33
|
|
|
parent::__construct($dbHandler, $tableNameParameter, $configResolver); |
|
34
|
|
|
} |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* @param int $limit |
|
38
|
|
|
* @param int $offset |
|
39
|
|
|
* @return MigrationCollection |
|
40
|
|
|
*/ |
|
41
|
|
|
public function loadMigrations($limit = null, $offset = null) |
|
42
|
|
|
{ |
|
43
|
|
|
return $this->loadMigrationsInner(null, $limit, $offset); |
|
44
|
|
|
} |
|
45
|
|
|
|
|
46
|
|
|
/** |
|
47
|
|
|
* @param int $status |
|
48
|
|
|
* @param int $limit |
|
49
|
|
|
* @param int $offset |
|
50
|
|
|
* @return MigrationCollection |
|
51
|
|
|
*/ |
|
52
|
|
|
public function loadMigrationsByStatus($status, $limit = null, $offset = null) |
|
53
|
|
|
{ |
|
54
|
|
|
return $this->loadMigrationsInner($status, $limit, $offset); |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* @param int $status |
|
59
|
|
|
* @param int $limit |
|
60
|
|
|
* @param int $offset |
|
61
|
|
|
* @return MigrationCollection |
|
62
|
|
|
*/ |
|
63
|
|
|
protected function loadMigrationsInner($status = null, $limit = null, $offset = null) |
|
64
|
|
|
{ |
|
65
|
|
|
$this->createTableIfNeeded(); |
|
66
|
|
|
|
|
67
|
|
|
/** @var \eZ\Publish\Core\Persistence\Database\SelectQuery $q */ |
|
68
|
|
|
$q = $this->dbHandler->createSelectQuery(); |
|
69
|
|
|
$q->select($this->fieldList) |
|
|
|
|
|
|
70
|
|
|
->from($this->tableName) |
|
|
|
|
|
|
71
|
|
|
->orderBy('migration', SelectQuery::ASC); |
|
72
|
|
|
if ($status !== null) { |
|
73
|
|
|
$q->where($q->expr->eq('status', $q->bindValue($status))); |
|
|
|
|
|
|
74
|
|
|
} |
|
75
|
|
|
if ($limit > 0 || $offset > 0) { |
|
76
|
|
|
if ($limit <= 0) { |
|
77
|
|
|
$limit = null; |
|
78
|
|
|
} |
|
79
|
|
|
if ($offset == 0) { |
|
|
|
|
|
|
80
|
|
|
$offset = null; |
|
81
|
|
|
} |
|
82
|
|
|
$q->limit($limit, $offset); |
|
83
|
|
|
} |
|
84
|
|
|
$stmt = $q->prepare(); |
|
85
|
|
|
$stmt->execute(); |
|
86
|
|
|
$results = $stmt->fetchAll(); |
|
87
|
|
|
|
|
88
|
|
|
$migrations = array(); |
|
89
|
|
|
foreach ($results as $result) { |
|
90
|
|
|
$migrations[$result['migration']] = $this->arrayToMigration($result); |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
return new MigrationCollection($migrations); |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
/** |
|
97
|
|
|
* @param string $migrationName |
|
98
|
|
|
* @return APIMigration|null |
|
99
|
|
|
*/ |
|
100
|
|
View Code Duplication |
public function loadMigration($migrationName) |
|
|
|
|
|
|
101
|
|
|
{ |
|
102
|
|
|
$this->createTableIfNeeded(); |
|
103
|
|
|
|
|
104
|
|
|
/** @var \eZ\Publish\Core\Persistence\Database\SelectQuery $q */ |
|
105
|
|
|
$q = $this->dbHandler->createSelectQuery(); |
|
106
|
|
|
$q->select($this->fieldList) |
|
|
|
|
|
|
107
|
|
|
->from($this->tableName) |
|
|
|
|
|
|
108
|
|
|
->where($q->expr->eq('migration', $q->bindValue($migrationName))); |
|
|
|
|
|
|
109
|
|
|
$stmt = $q->prepare(); |
|
110
|
|
|
$stmt->execute(); |
|
111
|
|
|
$result = $stmt->fetch(\PDO::FETCH_ASSOC); |
|
112
|
|
|
|
|
113
|
|
|
if (is_array($result) && !empty($result)) { |
|
114
|
|
|
return $this->arrayToMigration($result); |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
return null; |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* Creates and stores a new migration (leaving it in TODO status) |
|
122
|
|
|
* @param MigrationDefinition $migrationDefinition |
|
123
|
|
|
* @return APIMigration |
|
124
|
|
|
* @throws \Exception If the migration exists already (we rely on the PK for that) |
|
125
|
|
|
*/ |
|
126
|
|
|
public function addMigration(MigrationDefinition $migrationDefinition) |
|
127
|
|
|
{ |
|
128
|
|
|
$this->createTableIfNeeded(); |
|
129
|
|
|
|
|
130
|
|
|
$conn = $this->getConnection(); |
|
131
|
|
|
|
|
132
|
|
|
$migration = new APIMigration( |
|
133
|
|
|
$migrationDefinition->name, |
|
134
|
|
|
md5($migrationDefinition->rawDefinition), |
|
135
|
|
|
$migrationDefinition->path, |
|
136
|
|
|
null, |
|
137
|
|
|
APIMigration::STATUS_TODO |
|
138
|
|
|
); |
|
139
|
|
|
try { |
|
140
|
|
|
$conn->insert($this->tableName, $this->migrationToArray($migration)); |
|
141
|
|
|
} catch (UniqueConstraintViolationException $e) { |
|
142
|
|
|
throw new \Exception("Migration '{$migrationDefinition->name}' already exists"); |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
|
|
return $migration; |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* Starts a migration, given its definition: stores its status in the db, returns the Migration object |
|
150
|
|
|
* |
|
151
|
|
|
* @param MigrationDefinition $migrationDefinition |
|
152
|
|
|
* @return APIMigration |
|
153
|
|
|
* @throws \Exception if migration was already executing or already done |
|
154
|
|
|
* @todo add a parameter to allow re-execution of already-done migrations |
|
155
|
|
|
*/ |
|
156
|
|
|
public function startMigration(MigrationDefinition $migrationDefinition) |
|
157
|
|
|
{ |
|
158
|
|
|
return $this->createMigration($migrationDefinition, APIMigration::STATUS_STARTED, 'started'); |
|
159
|
|
|
} |
|
160
|
|
|
|
|
161
|
|
|
/** |
|
162
|
|
|
* Stops a migration by storing it in the db. Migration status can not be 'started' |
|
163
|
|
|
* |
|
164
|
|
|
* NB: if this call happens within another DB transaction which has already been flagged for rollback, the result |
|
165
|
|
|
* will be that a RuntimeException is thrown, as Doctrine does not allow to call commit() after rollback(). |
|
166
|
|
|
* One way to fix the problem would be not to use a transaction and select-for-update here, but since that is the |
|
167
|
|
|
* best way to insure atomic updates, I am loath to remove it. |
|
168
|
|
|
* A known workaround is to call the Doctrine Connection method setNestTransactionsWithSavepoints(true); this can |
|
169
|
|
|
* be achieved as simply as setting the parameter 'use_savepoints' in the doctrine connection configuration. |
|
170
|
|
|
* |
|
171
|
|
|
* @param APIMigration $migration |
|
172
|
|
|
* @param bool $force When true, the migration will be updated even if it was not in 'started' status |
|
173
|
|
|
* @throws \Exception If the migration was not started (unless $force=true) |
|
174
|
|
|
*/ |
|
175
|
|
|
public function endMigration(APIMigration $migration, $force = false) |
|
176
|
|
|
{ |
|
177
|
|
|
if ($migration->status == APIMigration::STATUS_STARTED) { |
|
178
|
|
|
throw new \Exception($this->getEntityName($migration)." '{$migration->name}' can not be ended as its status is 'started'..."); |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
$this->createTableIfNeeded(); |
|
182
|
|
|
|
|
183
|
|
|
// select for update |
|
184
|
|
|
|
|
185
|
|
|
// annoyingly enough, neither Doctrine nor EZP provide built in support for 'FOR UPDATE' in their query builders... |
|
186
|
|
|
// at least the doctrine one allows us to still use parameter binding when we add our sql particle |
|
187
|
|
|
$conn = $this->getConnection(); |
|
188
|
|
|
|
|
189
|
|
|
$qb = $conn->createQueryBuilder(); |
|
190
|
|
|
$qb->select('*') |
|
191
|
|
|
->from($this->tableName, 'm') |
|
192
|
|
|
->where('migration = ?'); |
|
193
|
|
|
$sql = $qb->getSQL() . ' FOR UPDATE'; |
|
194
|
|
|
|
|
195
|
|
|
$conn->beginTransaction(); |
|
196
|
|
|
|
|
197
|
|
|
$stmt = $conn->executeQuery($sql, array($migration->name)); |
|
198
|
|
|
$existingMigrationData = $stmt->fetch(\PDO::FETCH_ASSOC); |
|
199
|
|
|
|
|
200
|
|
|
// fail if it was not executing |
|
201
|
|
|
|
|
202
|
|
View Code Duplication |
if (!is_array($existingMigrationData)) { |
|
|
|
|
|
|
203
|
|
|
// commit to release the lock |
|
204
|
|
|
$conn->commit(); |
|
205
|
|
|
throw new \Exception($this->getEntityName($migration)." '{$migration->name}' can not be ended as it is not found"); |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
View Code Duplication |
if (($existingMigrationData['status'] != APIMigration::STATUS_STARTED) && !$force) { |
|
|
|
|
|
|
209
|
|
|
// commit to release the lock |
|
210
|
|
|
$conn->commit(); |
|
211
|
|
|
throw new \Exception($this->getEntityName($migration)." '{$migration->name}' can not be ended as it is not executing"); |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
$conn->update( |
|
215
|
|
|
$this->tableName, |
|
216
|
|
|
array( |
|
217
|
|
|
'status' => $migration->status, |
|
218
|
|
|
/// @todo use mb_substr (if all dbs we support count col length not in bytes but in chars...) |
|
219
|
|
|
'execution_error' => substr($migration->executionError, 0, 4000), |
|
220
|
|
|
'execution_date' => $migration->executionDate |
|
221
|
|
|
), |
|
222
|
|
|
array('migration' => $migration->name) |
|
223
|
|
|
); |
|
224
|
|
|
|
|
225
|
|
|
$conn->commit(); |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
/** |
|
229
|
|
|
* Removes a Migration from the table - regardless of its state! |
|
230
|
|
|
* |
|
231
|
|
|
* @param APIMigration $migration |
|
232
|
|
|
*/ |
|
233
|
|
|
public function deleteMigration(APIMigration $migration) |
|
234
|
|
|
{ |
|
235
|
|
|
$this->createTableIfNeeded(); |
|
236
|
|
|
$conn = $this->getConnection(); |
|
237
|
|
|
$conn->delete($this->tableName, array('migration' => $migration->name)); |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
/** |
|
241
|
|
|
* Skips a migration by storing it in the db. Migration status can not be 'started' |
|
242
|
|
|
* |
|
243
|
|
|
* @param MigrationDefinition $migrationDefinition |
|
244
|
|
|
* @return APIMigration |
|
245
|
|
|
* @throws \Exception If the migration was already executed or executing |
|
246
|
|
|
*/ |
|
247
|
|
|
public function skipMigration(MigrationDefinition $migrationDefinition) |
|
248
|
|
|
{ |
|
249
|
|
|
return $this->createMigration($migrationDefinition, APIMigration::STATUS_SKIPPED, 'skipped'); |
|
250
|
|
|
} |
|
251
|
|
|
|
|
252
|
|
|
/** |
|
253
|
|
|
* @param MigrationDefinition $migrationDefinition |
|
254
|
|
|
* @param int $status |
|
255
|
|
|
* @param string $action |
|
256
|
|
|
* @return APIMigration |
|
257
|
|
|
* @throws \Exception |
|
258
|
|
|
*/ |
|
259
|
|
|
protected function createMigration(MigrationDefinition $migrationDefinition, $status, $action) |
|
260
|
|
|
{ |
|
261
|
|
|
$this->createTableIfNeeded(); |
|
262
|
|
|
|
|
263
|
|
|
// select for update |
|
264
|
|
|
|
|
265
|
|
|
// annoyingly enough, neither Doctrine nor EZP provide built in support for 'FOR UPDATE' in their query builders... |
|
266
|
|
|
// at least the doctrine one allows us to still use parameter binding when we add our sql particle |
|
267
|
|
|
$conn = $this->getConnection(); |
|
268
|
|
|
|
|
269
|
|
|
$qb = $conn->createQueryBuilder(); |
|
270
|
|
|
$qb->select('*') |
|
271
|
|
|
->from($this->tableName, 'm') |
|
272
|
|
|
->where('migration = ?'); |
|
273
|
|
|
$sql = $qb->getSQL() . ' FOR UPDATE'; |
|
274
|
|
|
|
|
275
|
|
|
$conn->beginTransaction(); |
|
276
|
|
|
|
|
277
|
|
|
$stmt = $conn->executeQuery($sql, array($migrationDefinition->name)); |
|
278
|
|
|
$existingMigrationData = $stmt->fetch(\PDO::FETCH_ASSOC); |
|
279
|
|
|
|
|
280
|
|
|
if (is_array($existingMigrationData)) { |
|
281
|
|
|
// migration exists |
|
282
|
|
|
|
|
283
|
|
|
// fail if it was already executing or already done |
|
284
|
|
View Code Duplication |
if ($existingMigrationData['status'] == APIMigration::STATUS_STARTED) { |
|
|
|
|
|
|
285
|
|
|
// commit to release the lock |
|
286
|
|
|
$conn->commit(); |
|
287
|
|
|
throw new \Exception("Migration '{$migrationDefinition->name}' can not be $action as it is already executing"); |
|
288
|
|
|
} |
|
289
|
|
View Code Duplication |
if ($existingMigrationData['status'] == APIMigration::STATUS_DONE) { |
|
|
|
|
|
|
290
|
|
|
// commit to release the lock |
|
291
|
|
|
$conn->commit(); |
|
292
|
|
|
throw new \Exception("Migration '{$migrationDefinition->name}' can not be $action as it was already executed"); |
|
293
|
|
|
} |
|
294
|
|
View Code Duplication |
if ($existingMigrationData['status'] == APIMigration::STATUS_SKIPPED) { |
|
|
|
|
|
|
295
|
|
|
// commit to release the lock |
|
296
|
|
|
$conn->commit(); |
|
297
|
|
|
throw new \Exception("Migration '{$migrationDefinition->name}' can not be $action as it was already skipped"); |
|
298
|
|
|
} |
|
299
|
|
|
|
|
300
|
|
|
// do not set migration start date if we are skipping it |
|
301
|
|
|
$migration = new APIMigration( |
|
302
|
|
|
$migrationDefinition->name, |
|
303
|
|
|
md5($migrationDefinition->rawDefinition), |
|
304
|
|
|
$migrationDefinition->path, |
|
305
|
|
|
($status == APIMigration::STATUS_SKIPPED ? null : time()), |
|
306
|
|
|
$status |
|
307
|
|
|
); |
|
308
|
|
|
$conn->update( |
|
309
|
|
|
$this->tableName, |
|
310
|
|
|
array( |
|
311
|
|
|
'execution_date' => $migration->executionDate, |
|
312
|
|
|
'status' => $status, |
|
313
|
|
|
'execution_error' => null |
|
314
|
|
|
), |
|
315
|
|
|
array('migration' => $migrationDefinition->name) |
|
316
|
|
|
); |
|
317
|
|
|
$conn->commit(); |
|
318
|
|
|
|
|
319
|
|
|
} else { |
|
320
|
|
|
// migration did not exist. Create it! |
|
321
|
|
|
|
|
322
|
|
|
// commit immediately, to release the lock and avoid deadlocks |
|
323
|
|
|
$conn->commit(); |
|
324
|
|
|
|
|
325
|
|
|
$migration = new APIMigration( |
|
326
|
|
|
$migrationDefinition->name, |
|
327
|
|
|
md5($migrationDefinition->rawDefinition), |
|
328
|
|
|
$migrationDefinition->path, |
|
329
|
|
|
($status == APIMigration::STATUS_SKIPPED ? null : time()), |
|
330
|
|
|
$status |
|
331
|
|
|
); |
|
332
|
|
|
$conn->insert($this->tableName, $this->migrationToArray($migration)); |
|
333
|
|
|
} |
|
334
|
|
|
|
|
335
|
|
|
return $migration; |
|
336
|
|
|
} |
|
337
|
|
|
|
|
338
|
|
|
public function resumeMigration(APIMigration $migration) |
|
339
|
|
|
{ |
|
340
|
|
|
$this->createTableIfNeeded(); |
|
341
|
|
|
|
|
342
|
|
|
// select for update |
|
343
|
|
|
|
|
344
|
|
|
// annoyingly enough, neither Doctrine nor EZP provide built in support for 'FOR UPDATE' in their query builders... |
|
345
|
|
|
// at least the doctrine one allows us to still use parameter binding when we add our sql particle |
|
346
|
|
|
$conn = $this->getConnection(); |
|
347
|
|
|
|
|
348
|
|
|
$qb = $conn->createQueryBuilder(); |
|
349
|
|
|
$qb->select('*') |
|
350
|
|
|
->from($this->tableName, 'm') |
|
351
|
|
|
->where('migration = ?'); |
|
352
|
|
|
$sql = $qb->getSQL() . ' FOR UPDATE'; |
|
353
|
|
|
|
|
354
|
|
|
$conn->beginTransaction(); |
|
355
|
|
|
|
|
356
|
|
|
$stmt = $conn->executeQuery($sql, array($migration->name)); |
|
357
|
|
|
$existingMigrationData = $stmt->fetch(\PDO::FETCH_ASSOC); |
|
358
|
|
|
|
|
359
|
|
View Code Duplication |
if (!is_array($existingMigrationData)) { |
|
|
|
|
|
|
360
|
|
|
// commit immediately, to release the lock and avoid deadlocks |
|
361
|
|
|
$conn->commit(); |
|
362
|
|
|
throw new \Exception($this->getEntityName($migration)." '{$migration->name}' can not be resumed as it is not found"); |
|
363
|
|
|
} |
|
364
|
|
|
|
|
365
|
|
|
// migration exists |
|
366
|
|
|
|
|
367
|
|
|
// fail if it was not suspended |
|
368
|
|
View Code Duplication |
if ($existingMigrationData['status'] != APIMigration::STATUS_SUSPENDED) { |
|
|
|
|
|
|
369
|
|
|
// commit to release the lock |
|
370
|
|
|
$conn->commit(); |
|
371
|
|
|
throw new \Exception($this->getEntityName($migration)." '{$migration->name}' can not be resumed as it is not suspended"); |
|
372
|
|
|
} |
|
373
|
|
|
|
|
374
|
|
|
$migration = new APIMigration( |
|
375
|
|
|
$migration->name, |
|
376
|
|
|
$migration->md5, |
|
377
|
|
|
$migration->path, |
|
378
|
|
|
time(), |
|
379
|
|
|
APIMigration::STATUS_STARTED |
|
380
|
|
|
); |
|
381
|
|
|
|
|
382
|
|
|
$conn->update( |
|
383
|
|
|
$this->tableName, |
|
384
|
|
|
array( |
|
385
|
|
|
'execution_date' => $migration->executionDate, |
|
386
|
|
|
'status' => APIMigration::STATUS_STARTED, |
|
387
|
|
|
'execution_error' => null |
|
388
|
|
|
), |
|
389
|
|
|
array('migration' => $migration->name) |
|
390
|
|
|
); |
|
391
|
|
|
$conn->commit(); |
|
392
|
|
|
|
|
393
|
|
|
return $migration; |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* Removes all migration from storage (regardless of their status) |
|
398
|
|
|
*/ |
|
399
|
|
|
public function deleteMigrations() |
|
400
|
|
|
{ |
|
401
|
|
|
$this->drop(); |
|
402
|
|
|
} |
|
403
|
|
|
|
|
404
|
|
|
public function createTable() |
|
405
|
|
|
{ |
|
406
|
|
|
/** @var \Doctrine\DBAL\Schema\AbstractSchemaManager $sm */ |
|
407
|
|
|
$sm = $this->getConnection()->getSchemaManager(); |
|
408
|
|
|
$dbPlatform = $sm->getDatabasePlatform(); |
|
409
|
|
|
|
|
410
|
|
|
$schema = new Schema(); |
|
411
|
|
|
|
|
412
|
|
|
$t = $schema->createTable($this->tableName); |
|
413
|
|
|
$t->addColumn('migration', 'string', array('length' => 255)); |
|
414
|
|
|
$t->addColumn('path', 'string', array('length' => 4000)); |
|
415
|
|
|
$t->addColumn('md5', 'string', array('length' => 32)); |
|
416
|
|
|
$t->addColumn('execution_date', 'integer', array('notnull' => false)); |
|
417
|
|
|
$t->addColumn('status', 'integer', array('default ' => APIMigration::STATUS_TODO)); |
|
418
|
|
|
$t->addColumn('execution_error', 'string', array('length' => 4000, 'notnull' => false)); |
|
419
|
|
|
$t->setPrimaryKey(array('migration')); |
|
420
|
|
|
// in case users want to look up migrations by their full path |
|
421
|
|
|
// NB: disabled for the moment, as it causes problems on some versions of mysql which limit index length to 767 bytes, |
|
422
|
|
|
// and 767 bytes can be either 255 chars or 191 chars depending on charset utf8 or utf8mb4... |
|
423
|
|
|
//$t->addIndex(array('path')); |
|
|
|
|
|
|
424
|
|
|
|
|
425
|
|
|
foreach ($schema->toSql($dbPlatform) as $sql) { |
|
426
|
|
|
$this->dbHandler->exec($sql); |
|
427
|
|
|
} |
|
428
|
|
|
} |
|
429
|
|
|
|
|
430
|
|
|
protected function migrationToArray(APIMigration $migration) |
|
431
|
|
|
{ |
|
432
|
|
|
return array( |
|
433
|
|
|
'migration' => $migration->name, |
|
434
|
|
|
'md5' => $migration->md5, |
|
435
|
|
|
'path' => $migration->path, |
|
436
|
|
|
'execution_date' => $migration->executionDate, |
|
437
|
|
|
'status' => $migration->status, |
|
438
|
|
|
'execution_error' => $migration->executionError |
|
439
|
|
|
); |
|
440
|
|
|
} |
|
441
|
|
|
|
|
442
|
|
|
protected function arrayToMigration(array $data) |
|
443
|
|
|
{ |
|
444
|
|
|
return new APIMigration( |
|
445
|
|
|
$data['migration'], |
|
446
|
|
|
$data['md5'], |
|
447
|
|
|
$data['path'], |
|
448
|
|
|
$data['execution_date'], |
|
449
|
|
|
$data['status'], |
|
450
|
|
|
$data['execution_error'] |
|
451
|
|
|
); |
|
452
|
|
|
} |
|
453
|
|
|
|
|
454
|
|
|
protected function getEntityName($migration) |
|
455
|
|
|
{ |
|
456
|
|
|
return end(explode('\\', get_class($migration))); |
|
|
|
|
|
|
457
|
|
|
} |
|
458
|
|
|
} |
|
459
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.