Completed
Branch master (e35419)
by Gaetano
06:40
created

MigrationService   F

Complexity

Total Complexity 74

Size/Duplication

Total Lines 566
Duplicated Lines 0 %

Test Coverage

Coverage 67.62%

Importance

Changes 0
Metric Value
eloc 199
dl 0
loc 566
ccs 142
cts 210
cp 0.6762
rs 2.48
c 0
b 0
f 0
wmc 74

25 Methods

Rating   Name   Duplication   Size   Complexity  
A addExecutor() 0 4 2
A endMigration() 0 3 1
B resumeMigration() 0 40 8
A setLoader() 0 3 1
F executeMigrationInner() 0 135 14
A deleteMigration() 0 3 1
A executeMigration() 0 18 3
A getEntityName() 0 4 1
A getMigrations() 0 3 1
A addDefinitionParser() 0 3 1
A getMigration() 0 3 1
A getMigrationsDefinitions() 0 18 6
A getAdminUserIdentifier() 0 7 2
A getExecutor() 0 7 2
C getFullExceptionMessage() 0 46 13
A skipMigration() 0 3 1
A restoreContext() 0 3 1
A listExecutors() 0 3 1
A addMigration() 0 3 1
A __construct() 0 8 1
A parseMigrationDefinition() 0 26 5
A migrationContextFromParameters() 0 13 3
A getMigrationsByStatus() 0 3 1
A injectContextIntoStep() 0 6 1
A getCurrentContext() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like MigrationService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MigrationService, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core;
4
5
use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
6
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
7
use eZ\Publish\API\Repository\Repository;
8
use Kaliop\eZMigrationBundle\API\Collection\MigrationDefinitionCollection;
9
use Kaliop\eZMigrationBundle\API\StorageHandlerInterface;
10
use Kaliop\eZMigrationBundle\API\LoaderInterface;
11
use Kaliop\eZMigrationBundle\API\DefinitionParserInterface;
12
use Kaliop\eZMigrationBundle\API\ExecutorInterface;
13
use Kaliop\eZMigrationBundle\API\ContextProviderInterface;
14
use Kaliop\eZMigrationBundle\API\Value\Migration;
15
use Kaliop\eZMigrationBundle\API\Value\MigrationDefinition;
16
use Kaliop\eZMigrationBundle\API\Exception\MigrationStepExecutionException;
17
use Kaliop\eZMigrationBundle\API\Exception\MigrationAbortedException;
18
use Kaliop\eZMigrationBundle\API\Exception\MigrationSuspendedException;
19
use Kaliop\eZMigrationBundle\API\Exception\MigrationStepSkippedException;
20
use Kaliop\eZMigrationBundle\API\Exception\AfterMigrationExecutionException;
21
use Kaliop\eZMigrationBundle\API\Event\BeforeStepExecutionEvent;
22
use Kaliop\eZMigrationBundle\API\Event\StepExecutedEvent;
23
use Kaliop\eZMigrationBundle\API\Event\MigrationAbortedEvent;
24
use Kaliop\eZMigrationBundle\API\Event\MigrationSuspendedEvent;
25
26
class MigrationService implements ContextProviderInterface
27
{
28
    use RepositoryUserSetterTrait;
0 ignored issues
show
introduced by
The trait Kaliop\eZMigrationBundle...positoryUserSetterTrait requires some properties which are not provided by Kaliop\eZMigrationBundle\Core\MigrationService: $id, $login
Loading history...
29
30
    /**
31
     * The default Admin user Id, used when no Admin user is specified
32
     */
33
    const ADMIN_USER_ID = 14;
34
35
    /**
36
     * @var LoaderInterface $loader
37
     */
38
    protected $loader;
39
    /**
40
     * @var StorageHandlerInterface $storageHandler
41
     */
42
    protected $storageHandler;
43
44
    /** @var DefinitionParserInterface[] $DefinitionParsers */
45
    protected $DefinitionParsers = array();
46
47
    /** @var ExecutorInterface[] $executors */
48
    protected $executors = array();
49
50
    protected $repository;
51
52
    protected $dispatcher;
53
54
    /**
55
     * @var ContextHandler $contextHandler
56
     */
57
    protected $contextHandler;
58
59
    protected $eventPrefix = 'ez_migration.';
60
61
    protected $eventEntity = 'migration';
62
63
    protected $migrationContext = array();
64
65 80
    public function __construct(LoaderInterface $loader, StorageHandlerInterface $storageHandler, Repository $repository,
66
        EventDispatcherInterface $eventDispatcher, $contextHandler)
67
    {
68 80
        $this->loader = $loader;
69 80
        $this->storageHandler = $storageHandler;
70 80
        $this->repository = $repository;
71 80
        $this->dispatcher = $eventDispatcher;
72 80
        $this->contextHandler = $contextHandler;
73 80
    }
74
75 80
    public function addDefinitionParser(DefinitionParserInterface $DefinitionParser)
76
    {
77 80
        $this->DefinitionParsers[] = $DefinitionParser;
78 80
    }
79
80 80
    public function addExecutor(ExecutorInterface $executor)
81
    {
82 80
        foreach ($executor->supportedTypes() as $type) {
83 80
            $this->executors[$type] = $executor;
84
        }
85 80
    }
86
87
    /**
88
     * @param string $type
89
     * @return ExecutorInterface
90
     * @throws \InvalidArgumentException If executor doesn't exist
91
     */
92 26
    public function getExecutor($type)
93
    {
94 26
        if (!isset($this->executors[$type])) {
95
            throw new \InvalidArgumentException("Executor with type '$type' doesn't exist");
96
        }
97
98 26
        return $this->executors[$type];
99
    }
100
101
    /**
102
     * @return string[]
103
     */
104 25
    public function listExecutors()
105
    {
106 25
        return array_keys($this->executors);
107
    }
108
109
    public function setLoader(LoaderInterface $loader)
110
    {
111
        $this->loader = $loader;
112
    }
113
114
    /**
115
     * NB: returns UNPARSED definitions
116
     *
117
     * @param string[] $paths
118
     * @return MigrationDefinitionCollection key: migration name, value: migration definition as binary string
119
     */
120 78
    public function getMigrationsDefinitions(array $paths = array())
121
    {
122
        // we try to be flexible in file types we support, and the same time avoid loading all files in a directory
123 78
        $handledDefinitions = array();
124 78
        foreach ($this->loader->listAvailableDefinitions($paths) as $migrationName => $definitionPath) {
125 78
            foreach ($this->DefinitionParsers as $definitionParser) {
126 78
                if ($definitionParser->supports($migrationName)) {
127 78
                    $handledDefinitions[] = $definitionPath;
128
                }
129
            }
130
        }
131
132
        // we can not call loadDefinitions with an empty array using the Filesystem loader, or it will start looking in bundles...
133 78
        if (empty($handledDefinitions) && !empty($paths)) {
134
            return new MigrationDefinitionCollection();
135
        }
136
137 78
        return $this->loader->loadDefinitions($handledDefinitions);
138
    }
139
140
    /**
141
     * Returns the list of all the migrations which where executed or attempted so far
142
     *
143
     * @param int $limit 0 or below will be treated as 'no limit'
144
     * @param int $offset
145
     * @return \Kaliop\eZMigrationBundle\API\Collection\MigrationCollection
146
     */
147 79
    public function getMigrations($limit = null, $offset = null)
148
    {
149 79
        return $this->storageHandler->loadMigrations($limit, $offset);
150
    }
151
152
    /**
153
     * Returns the list of all the migrations in a given status which where executed or attempted so far
154
     *
155
     * @param int $status
156
     * @param int $limit 0 or below will be treated as 'no limit'
157
     * @param int $offset
158
     * @return \Kaliop\eZMigrationBundle\API\Collection\MigrationCollection
159
     */
160 1
    public function getMigrationsByStatus($status, $limit = null, $offset = null)
161
    {
162 1
        return $this->storageHandler->loadMigrationsByStatus($status, $limit, $offset);
163
    }
164
165
    /**
166
     * @param string $migrationName
167
     * @return Migration|null
168
     */
169 49
    public function getMigration($migrationName)
170
    {
171 49
        return $this->storageHandler->loadMigration($migrationName);
172
    }
173
174
    /**
175
     * @param MigrationDefinition $migrationDefinition
176
     * @return Migration
177
     */
178 48
    public function addMigration(MigrationDefinition $migrationDefinition)
179
    {
180 48
        return $this->storageHandler->addMigration($migrationDefinition);
181
    }
182
183
    /**
184
     * @param Migration $migration
185
     */
186 46
    public function deleteMigration(Migration $migration)
187
    {
188 46
        return $this->storageHandler->deleteMigration($migration);
189
    }
190
191
    /**
192
     * @param MigrationDefinition $migrationDefinition
193
     * @return Migration
194
     */
195
    public function skipMigration(MigrationDefinition $migrationDefinition)
196
    {
197
        return $this->storageHandler->skipMigration($migrationDefinition);
198
    }
199
200
    /**
201
     * Not to be called by external users for normal use cases, you should use executeMigration() instead
202
     *
203
     * @param Migration $migration
204
     */
205
    public function endMigration(Migration $migration)
206
    {
207
        return $this->storageHandler->endMigration($migration);
208
    }
209
210
    /**
211
     * Parses a migration definition, return a parsed definition.
212
     * If there is a parsing error, the definition status will be updated accordingly
213
     *
214
     * @param MigrationDefinition $migrationDefinition
215
     * @return MigrationDefinition
216
     * @throws \Exception if the migrationDefinition has no suitable parser for its source format
217
     */
218 80
    public function parseMigrationDefinition(MigrationDefinition $migrationDefinition)
219
    {
220 80
        foreach ($this->DefinitionParsers as $definitionParser) {
221 80
            if ($definitionParser->supports($migrationDefinition->name)) {
222
                // parse the source file
223 80
                $migrationDefinition = $definitionParser->parseMigrationDefinition($migrationDefinition);
224
225
                // and make sure we know how to handle all steps
226 80
                foreach ($migrationDefinition->steps as $step) {
227 71
                    if (!isset($this->executors[$step->type])) {
228 2
                        return new MigrationDefinition(
229 2
                            $migrationDefinition->name,
230 2
                            $migrationDefinition->path,
231 2
                            $migrationDefinition->rawDefinition,
232 2
                            MigrationDefinition::STATUS_INVALID,
233 2
                            array(),
234 71
                            "Can not handle migration step of type '{$step->type}'"
235
                        );
236
                    }
237
                }
238
239 80
                return $migrationDefinition;
240
            }
241
        }
242
243
        throw new \Exception("No parser available to parse migration definition '{$migrationDefinition->name}'");
244
    }
245
246
    /**
247
     * @param MigrationDefinition $migrationDefinition
248
     * @param bool $useTransaction when set to false, no repo transaction will be used to wrap the migration
249
     * @param string $defaultLanguageCode
250
     * @param string|int|false|null $adminLogin when false, current user is used; when null, hardcoded admin account
251
     * @param bool $force when true, execute a migration if it was already in status DONE or SKIPPED (would throw by default)
252
     * @throws \Exception
253
     *
254
     * @todo treating a null and false $adminLogin values differently is prone to hard-to-track errors.
255
     *       Shall we use instead -1 to indicate the desire to not-login-as-admin-user-at-all ?
256
     */
257 40
    public function executeMigration(MigrationDefinition $migrationDefinition, $useTransaction = true,
258
        $defaultLanguageCode = null, $adminLogin = null, $force = false)
259
    {
260 40
        if ($migrationDefinition->status == MigrationDefinition::STATUS_TO_PARSE) {
261 2
            $migrationDefinition = $this->parseMigrationDefinition($migrationDefinition);
262
        }
263
264 40
        if ($migrationDefinition->status == MigrationDefinition::STATUS_INVALID) {
265
            throw new \Exception("Can not execute " . $this->getEntityName($migrationDefinition). " '{$migrationDefinition->name}': {$migrationDefinition->parsingError}");
266
        }
267
268
        /// @todo add support for setting in $migrationContext a userContentType ?
269 40
        $migrationContext = $this->migrationContextFromParameters($defaultLanguageCode, $adminLogin);
270
271
        // set migration as begun - has to be in own db transaction
272 40
        $migration = $this->storageHandler->startMigration($migrationDefinition, $force);
273
274 40
        $this->executeMigrationInner($migration, $migrationDefinition, $migrationContext, 0, $useTransaction, $adminLogin);
275 32
    }
276
277
    /**
278
     * @param Migration $migration
279
     * @param MigrationDefinition $migrationDefinition
280
     * @param array $migrationContext
281
     * @param int $stepOffset
282
     * @param bool $useTransaction when set to false, no repo transaction will be used to wrap the migration
283
     * @param string|int|false|null $adminLogin used only for committing db transaction if needed. If false or null, hardcoded admin is used
284
     * @throws \Exception
285
     */
286 40
    protected function executeMigrationInner(Migration $migration, MigrationDefinition $migrationDefinition,
287
        $migrationContext, $stepOffset = 0, $useTransaction = true, $adminLogin = null)
288
    {
289 40
        if ($useTransaction) {
290 2
            $this->repository->beginTransaction();
291
        }
292
293 40
        $this->migrationContext[$migration->name] = array('context' => $migrationContext);
294 40
        $previousUserId = null;
295 40
        $steps = array_slice($migrationDefinition->steps->getArrayCopy(), $stepOffset);
296
297
        try {
298
299 40
            $i = $stepOffset+1;
300 40
            $finalStatus = Migration::STATUS_DONE;
301 40
            $finalMessage = null;
302
303
            try {
304
305 40
                foreach ($steps as $step) {
306
                    // save enough data in the context to be able to successfully suspend/resume
307 40
                    $this->migrationContext[$migration->name]['step'] = $i;
308
309 40
                    $step = $this->injectContextIntoStep($step, $migrationContext);
310
311
                    // we validated the fact that we have a good executor at parsing time
312 40
                    $executor = $this->executors[$step->type];
313
314 40
                    $beforeStepExecutionEvent = new BeforeStepExecutionEvent($step, $executor);
315 40
                    $this->dispatcher->dispatch($this->eventPrefix . 'before_execution', $beforeStepExecutionEvent);
316
                    // allow some sneaky trickery here: event listeners can manipulate 'live' the step definition and the executor
317 40
                    $executor = $beforeStepExecutionEvent->getExecutor();
318 40
                    $step = $beforeStepExecutionEvent->getStep();
319
320
                    try {
321 40
                        $result = $executor->execute($step);
322
323 36
                        $this->dispatcher->dispatch($this->eventPrefix . 'step_executed', new StepExecutedEvent($step, $result));
324 13
                    } catch (MigrationStepSkippedException $e) {
325 1
                        continue;
326
                    }
327
328 36
                    $i++;
329
                }
330
331 12
            } catch (MigrationAbortedException $e) {
332
                // allow a migration step (or events) to abort the migration via a specific exception
333
334 3
                $this->dispatcher->dispatch($this->eventPrefix . $this->eventEntity . '_aborted', new MigrationAbortedEvent($step, $e));
335
336 3
                $finalStatus = $e->getCode();
337 3
                $finalMessage = "Abort in execution of step $i: " . $e->getMessage();
338 9
            } catch (MigrationSuspendedException $e) {
339
                // allow a migration step (or events) to suspend the migration via a specific exception
340
341 1
                $this->dispatcher->dispatch($this->eventPrefix . $this->eventEntity . '_suspended', new MigrationSuspendedEvent($step, $e));
342
343
                // let the context handler store our context, along with context data from any other (tagged) service which has some
344 1
                $this->contextHandler->storeCurrentContext($migration->name);
345
346 1
                $finalStatus = Migration::STATUS_SUSPENDED;
347 1
                $finalMessage = "Suspended in execution of step $i: " . $e->getMessage();
348
            }
349
350
            // in case we have an exception thrown in the commit phase after the last step, make sure we report the correct step
351 32
            $i--;
352
353
            // set migration as done
354 32
            $this->storageHandler->endMigration(new Migration(
355 32
                $migration->name,
356 32
                $migration->md5,
357 32
                $migration->path,
358 32
                $migration->executionDate,
359 32
                $finalStatus,
360 32
                $finalMessage
361
            ));
362
363 32
            if ($useTransaction) {
364
                // there might be workflows or other actions happening at commit time that fail if we are not admin
365 2
                $previousUserId = $this->loginUser($this->getAdminUserIdentifier($adminLogin));
0 ignored issues
show
Bug introduced by
It seems like $adminLogin can also be of type false; however, parameter $adminLogin of Kaliop\eZMigrationBundle...etAdminUserIdentifier() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

365
                $previousUserId = $this->loginUser($this->getAdminUserIdentifier(/** @scrutinizer ignore-type */ $adminLogin));
Loading history...
366
367 2
                $this->repository->commit();
368 32
                $this->loginUser($previousUserId);
369
            }
370
371 8
        } catch (\Exception $e) {
372
373 8
            $errorMessage = $this->getFullExceptionMessage($e) . ' in file ' . $e->getFile() . ' line ' . $e->getLine();
374 8
            $finalStatus = Migration::STATUS_FAILED;
375 8
            $exception = null;
376
377 8
            if ($useTransaction) {
378
                try {
379
                    // cater to the case where the $this->repository->commit() call above throws an exception
380
                    if ($previousUserId) {
381
                        $this->loginUser($previousUserId);
382
                    }
383
384
                    // there is no need to become admin here, at least in theory
385
                    $this->repository->rollBack();
386
387
                } catch (\Exception $e2) {
388
                    // This check is not rock-solid, but at the moment is all we can do to tell apart 2 cases of
389
                    // exceptions originating above: the case where the commit was successful but handling of a commit-queue
390
                    // signal failed, from the case where something failed beforehand.
391
                    // Known cases for signals failing at commit time include fe. https://jira.ez.no/browse/EZP-29333
392
                    if ($previousUserId && $e2->getMessage() == 'There is no active transaction.') {
393
                        // since the migration succeeded and it was committed, no use to mark it as failed...
394
                        $finalStatus = Migration::STATUS_DONE;
395
                        $errorMessage = 'An exception was thrown after committing, in file ' .
396
                            $e->getFile() . ' line ' . $e->getLine() . ': ' . $this->getFullExceptionMessage($e);
397
                        $exception = new AfterMigrationExecutionException($errorMessage, $i, $e);
398
                    } else {
399
                        $errorMessage .= '. In addition, an exception was thrown while rolling back, in file ' .
400
                            $e2->getFile() . ' line ' . $e2->getLine() . ': ' . $this->getFullExceptionMessage($e2);
401
                    }
402
                }
403
            }
404
405
            // set migration as failed
406
            // NB: we use the 'force' flag here because we might be catching an exception happened during the call to
407
            // $this->repository->commit() above, in which case the Migration might already be in the DB with a status 'done'
408 8
            $this->storageHandler->endMigration(
409 8
                new Migration(
410 8
                    $migration->name,
411 8
                    $migration->md5,
412 8
                    $migration->path,
413 8
                    $migration->executionDate,
414 8
                    $finalStatus,
415 8
                    $errorMessage
416
                ),
417 8
                true
418
            );
419
420 8
            throw $exception ? $exception : new MigrationStepExecutionException($errorMessage, $i, $e);
421
        }
422 32
    }
423
424
    /**
425
     * @param Migration $migration
426
     * @param bool $useTransaction
427
     * @throws \Exception
428
     *
429
     * @todo add support for adminLogin ?
430
     */
431
    public function resumeMigration(Migration $migration, $useTransaction = true)
432
    {
433
        if ($migration->status != Migration::STATUS_SUSPENDED) {
434
            throw new \Exception("Can not resume ".$this->getEntityName($migration)." '{$migration->name}': it is not in suspended status");
435
        }
436
437
        $migrationDefinitions = $this->getMigrationsDefinitions(array($migration->path));
438
        if (!count($migrationDefinitions)) {
439
            throw new \Exception("Can not resume ".$this->getEntityName($migration)." '{$migration->name}': its definition is missing");
440
        }
441
442
        $defs = $migrationDefinitions->getArrayCopy();
443
        $migrationDefinition = reset($defs);
444
445
        $migrationDefinition = $this->parseMigrationDefinition($migrationDefinition);
446
        if ($migrationDefinition->status == MigrationDefinition::STATUS_INVALID) {
447
            throw new \Exception("Can not resume ".$this->getEntityName($migration)." '{$migration->name}': {$migrationDefinition->parsingError}");
448
        }
449
450
        // restore context
451
        $this->contextHandler->restoreCurrentContext($migration->name);
452
453
        if (!isset($this->migrationContext[$migration->name])) {
454
            throw new \Exception("Can not resume ".$this->getEntityName($migration)." '{$migration->name}': the stored context is missing");
455
        }
456
        $restoredContext = $this->migrationContext[$migration->name];
457
        if (!is_array($restoredContext) || !isset($restoredContext['context']) || !isset($restoredContext['step'] )) {
458
            throw new \Exception("Can not resume ".$this->getEntityName($migration)." '{$migration->name}': the stored context is invalid");
459
        }
460
461
        // update migration status
462
        $migration = $this->storageHandler->resumeMigration($migration);
463
464
        // clean up restored context - ideally it should be in the same db transaction as the line above
465
        $this->contextHandler->deleteContext($migration->name);
466
467
        // and go
468
        // note: we store the current step counting starting at 1, but use offset starting at 0, hence the -1 here
469
        $this->executeMigrationInner($migration, $migrationDefinition, $restoredContext['context'],
470
            $restoredContext['step'] - 1, $useTransaction);
471
    }
472
473
    /**
474
     * @param string $defaultLanguageCode
475
     * @param string|int|false $adminLogin
476
     * @return array
477
     */
478 40
    protected function migrationContextFromParameters($defaultLanguageCode = null, $adminLogin = null)
479
    {
480 40
        $properties = array();
481
482 40
        if ($defaultLanguageCode != null) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $defaultLanguageCode of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
483 1
            $properties['defaultLanguageCode'] = $defaultLanguageCode;
484
        }
485
        // nb: other parts of the codebase treat differently a false and null values for $properties['adminUserLogin']
486 40
        if ($adminLogin !== null) {
487
            $properties['adminUserLogin'] = $adminLogin;
488
        }
489
490 40
        return $properties;
491
    }
492
493 40
    protected function injectContextIntoStep(MigrationStep $step, array $context)
494
    {
495 40
        return new MigrationStep(
496 40
            $step->type,
497 40
            $step->dsl,
498 40
            array_merge($step->context, $context)
499
        );
500
    }
501
502
    /**
503
     * @param string $adminLogin
504
     * @return int|string
505
     */
506 2
    protected function getAdminUserIdentifier($adminLogin)
507
    {
508 2
        if ($adminLogin != null) {
509
            return $adminLogin;
510
        }
511
512 2
        return self::ADMIN_USER_ID;
513
    }
514
515
    /**
516
     * Turns eZPublish cryptic exceptions into something more palatable for random devs
517
     * @todo should this be moved to a lower layer ?
518
     *
519
     * @param \Exception $e
520
     * @return string
521
     */
522 8
    protected function getFullExceptionMessage(\Exception $e)
523
    {
524 8
        $message = $e->getMessage();
525 8
        if (is_a($e, '\eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException') ||
526 8
            is_a($e, '\eZ\Publish\API\Repository\Exceptions\LimitationValidationException') ||
527 8
            is_a($e, '\eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException')
528
        ) {
529
            if (is_a($e, '\eZ\Publish\API\Repository\Exceptions\LimitationValidationException')) {
530
                $errorsArray = $e->getLimitationErrors();
0 ignored issues
show
Bug introduced by
The method getLimitationErrors() does not exist on Exception. It seems like you code against a sub-type of Exception such as eZ\Publish\API\Repositor...tionValidationException. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

530
                /** @scrutinizer ignore-call */ 
531
                $errorsArray = $e->getLimitationErrors();
Loading history...
531
                if ($errorsArray == null) {
532
                    return $message;
533
                }
534
            } else if (is_a($e, '\eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException')) {
535
                $errorsArray = array();
536
                foreach ($e->getFieldErrors() as $limitationError) {
0 ignored issues
show
Bug introduced by
The method getFieldErrors() does not exist on Exception. It seems like you code against a sub-type of Exception such as eZ\Publish\API\Repositor...tionValidationException or eZ\Publish\API\Repositor...ieldValidationException. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

536
                foreach ($e->/** @scrutinizer ignore-call */ getFieldErrors() as $limitationError) {
Loading history...
537
                    // we get the 1st language
538
                    $errorsArray[] = reset($limitationError);
539
                }
540
            } else {
541
                $errorsArray = $e->getFieldErrors();
542
            }
543
544
            foreach ($errorsArray as $errors) {
545
                // sometimes error arrays are 2-level deep, sometimes 1...
546
                if (!is_array($errors)) {
547
                    $errors = array($errors);
548
                }
549
                foreach ($errors as $error) {
550
                    /// @todo find out what is the proper eZ way of getting a translated message for these errors
551
                    $translatableMessage = $error->getTranslatableMessage();
552
                    if (is_a($translatableMessage, '\eZ\Publish\API\Repository\Values\Translation\Plural')) {
553
                        $msgText = $translatableMessage->plural;
554
                    } else {
555
                        $msgText = $translatableMessage->message;
556
                    }
557
558
                    $message .= "\n" . $msgText . " - " . var_export($translatableMessage->values, true);
559
                }
560
            }
561
        }
562
563 8
        while (($e = $e->getPrevious()) != null) {
564 1
            $message .= "\n" . $e->getMessage();
565
        }
566
567 8
        return $message;
568
    }
569
570
    /**
571
     * @param string $migrationName
572
     * @return array
573
     */
574 1
    public function getCurrentContext($migrationName)
575
    {
576 1
        return isset($this->migrationContext[$migrationName]) ? $this->migrationContext[$migrationName] : null;
577
    }
578
579
    /**
580
     * @param string $migrationName
581
     * @param array $context
582
     */
583
    public function restoreContext($migrationName, array $context)
584
    {
585
        $this->migrationContext[$migrationName] = $context;
586
    }
587
588
    protected function getEntityName($migration)
589
    {
590
        $array = explode('\\', get_class($migration));
591
        return strtolower(preg_replace('/Definition$/', '', end($array)));
592
    }
593
}
594