Completed
Push — master ( fa0673...7f3713 )
by Bernhard
12:46
created

DiscoveryManagerImpl::loadPackages()   C

Complexity

Conditions 8
Paths 12

Size

Total Lines 32
Code Lines 15

Duplication

Lines 9
Ratio 28.13 %

Code Coverage

Tests 20
CRAP Score 8

Importance

Changes 3
Bugs 0 Features 1
Metric Value
c 3
b 0
f 1
dl 9
loc 32
ccs 20
cts 20
cp 1
rs 5.3846
cc 8
eloc 15
nc 12
nop 0
crap 8
1
<?php
2
3
/*
4
 * This file is part of the puli/manager package.
5
 *
6
 * (c) Bernhard Schussek <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Puli\Manager\Discovery;
13
14
use Exception;
15
use Psr\Log\LoggerInterface;
16
use Psr\Log\NullLogger;
17
use Puli\Discovery\Api\EditableDiscovery;
18
use Puli\Manager\Api\Context\ProjectContext;
19
use Puli\Manager\Api\Discovery\BindingDescriptor;
20
use Puli\Manager\Api\Discovery\BindingTypeDescriptor;
21
use Puli\Manager\Api\Discovery\DiscoveryManager;
22
use Puli\Manager\Api\Discovery\DiscoveryNotEmptyException;
23
use Puli\Manager\Api\Discovery\DuplicateBindingException;
24
use Puli\Manager\Api\Discovery\DuplicateTypeException;
25
use Puli\Manager\Api\Discovery\NoSuchBindingException;
26
use Puli\Manager\Api\Discovery\NoSuchTypeException;
27
use Puli\Manager\Api\Discovery\TypeNotEnabledException;
28
use Puli\Manager\Api\NonRootPackageExpectedException;
29
use Puli\Manager\Api\Package\InstallInfo;
30
use Puli\Manager\Api\Package\Package;
31
use Puli\Manager\Api\Package\PackageCollection;
32
use Puli\Manager\Api\Package\RootPackage;
33
use Puli\Manager\Api\Package\RootPackageFile;
34
use Puli\Manager\Assert\Assert;
35
use Puli\Manager\Discovery\Binding\AddBinding;
36
use Puli\Manager\Discovery\Binding\AddBindingDescriptorToPackageFile;
37
use Puli\Manager\Discovery\Binding\BindingDescriptorCollection;
38
use Puli\Manager\Discovery\Binding\DisableBindingUuid;
39
use Puli\Manager\Discovery\Binding\EnableBindingUuid;
40
use Puli\Manager\Discovery\Binding\LoadBindingDescriptor;
41
use Puli\Manager\Discovery\Binding\ReloadBindingDescriptorsByTypeName;
42
use Puli\Manager\Discovery\Binding\ReloadBindingDescriptorsByUuid;
43
use Puli\Manager\Discovery\Binding\RemoveBindingDescriptorFromPackageFile;
44
use Puli\Manager\Discovery\Binding\SyncBindingUuid;
45
use Puli\Manager\Discovery\Binding\UnloadBindingDescriptor;
46
use Puli\Manager\Discovery\Type\AddBindingType;
47
use Puli\Manager\Discovery\Type\AddTypeDescriptorToPackageFile;
48
use Puli\Manager\Discovery\Type\BindingTypeDescriptorCollection;
49
use Puli\Manager\Discovery\Type\LoadTypeDescriptor;
50
use Puli\Manager\Discovery\Type\RemoveTypeDescriptorFromPackageFile;
51
use Puli\Manager\Discovery\Type\SyncTypeName;
52
use Puli\Manager\Discovery\Type\UnloadTypeDescriptor;
53
use Puli\Manager\Discovery\Type\UpdateDuplicateMarksForTypeName;
54
use Puli\Manager\Package\PackageFileStorage;
55
use Puli\Manager\Transaction\InterceptedOperation;
56
use Puli\Manager\Transaction\Transaction;
57
use Rhumsaa\Uuid\Uuid;
58
use Webmozart\Expression\Expr;
59
use Webmozart\Expression\Expression;
60
61
/**
62
 * @since  1.0
63
 *
64
 * @author Bernhard Schussek <[email protected]>
65
 */
66
class DiscoveryManagerImpl implements DiscoveryManager
67
{
68
    /**
69
     * @var ProjectContext
70
     */
71
    private $context;
72
73
    /**
74
     * @var LoggerInterface
75
     */
76
    private $logger;
77
78
    /**
79
     * @var EditableDiscovery
80
     */
81
    private $discovery;
82
83
    /**
84
     * @var PackageCollection
85
     */
86
    private $packages;
87
88
    /**
89
     * @var PackageFileStorage
90
     */
91
    private $packageFileStorage;
92
93
    /**
94
     * @var RootPackage
95
     */
96
    private $rootPackage;
97
98
    /**
99
     * @var RootPackageFile
100
     */
101
    private $rootPackageFile;
102
103
    /**
104
     * @var BindingTypeDescriptorCollection
105
     */
106
    private $typeDescriptors;
107
108
    /**
109
     * @var BindingDescriptorCollection
110
     */
111
    private $bindingDescriptors;
112
113
    /**
114
     * Creates a tag manager.
115
     *
116
     * @param ProjectContext       $context
117
     * @param EditableDiscovery    $discovery
118
     * @param PackageCollection    $packages
119
     * @param PackageFileStorage   $packageFileStorage
120
     * @param LoggerInterface|null $logger
121
     */
122 99
    public function __construct(
123
        ProjectContext $context,
124
        EditableDiscovery $discovery,
125
        PackageCollection $packages,
126
        PackageFileStorage $packageFileStorage,
127
        LoggerInterface $logger = null
128
    ) {
129 99
        $this->context = $context;
130 99
        $this->discovery = $discovery;
131 99
        $this->packages = $packages;
132 99
        $this->packageFileStorage = $packageFileStorage;
133 99
        $this->rootPackage = $packages->getRootPackage();
134 99
        $this->rootPackageFile = $context->getRootPackageFile();
135 99
        $this->logger = $logger ?: new NullLogger();
136 99
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141 1
    public function getContext()
142
    {
143 1
        return $this->context;
144
    }
145
146
    /**
147
     * {@inheritdoc}
148
     */
149 5
    public function addRootTypeDescriptor(BindingTypeDescriptor $typeDescriptor, $flags = 0)
150
    {
151 5
        Assert::integer($flags, 'The argument $flags must be a boolean.');
152
153 5
        $this->assertPackagesLoaded();
154 5
        $this->emitWarningForDuplicateTypes();
155
156 5
        $typeName = $typeDescriptor->getTypeName();
157
158 5
        if (!($flags & self::OVERRIDE) && $this->typeDescriptors->contains($typeName)) {
159 1
            throw DuplicateTypeException::forTypeName($typeName);
160
        }
161
162 4
        $tx = new Transaction();
163
164
        try {
165 4
            $syncBindingOps = array();
166
167 4 View Code Duplication
            foreach ($this->getUuidsByTypeName($typeName) as $uuid) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168 1
                $syncBindingOp = $this->syncBindingUuid($uuid);
169 1
                $syncBindingOp->takeSnapshot();
170 1
                $syncBindingOps[] = $syncBindingOp;
171 4
            }
172
173 4
            $syncOp = $this->syncTypeName($typeName);
174 4
            $syncOp->takeSnapshot();
175
176 4
            $tx->execute($this->loadTypeDescriptor($typeDescriptor, $this->rootPackage));
177 4
            $tx->execute($this->addTypeDescriptorToPackageFile($typeDescriptor));
178 4
            $tx->execute($syncOp);
179
180 4
            foreach ($syncBindingOps as $syncBindingOp) {
181 1
                $tx->execute($syncBindingOp);
182 4
            }
183
184 4
            $this->saveRootPackageFile();
185
186 3
            $tx->commit();
187 4
        } catch (Exception $e) {
188 1
            $tx->rollback();
189
190 1
            throw $e;
191
        }
192 3
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197 11
    public function removeRootTypeDescriptor($typeName)
198
    {
199
        // Only check that this is a string. The error message "not found" is
200
        // more helpful than e.g. "type name must contain /".
201 11
        Assert::string($typeName, 'The type name must be a string');
202
203 11
        $this->assertPackagesLoaded();
204
205 11
        if (!$this->typeDescriptors->contains($typeName, $this->rootPackage->getName())) {
206 2
            return;
207
        }
208
209 9
        $typeDescriptor = $this->typeDescriptors->get($typeName, $this->rootPackage->getName());
210 9
        $tx = new Transaction();
211
212
        try {
213 9
            $tx->execute($this->removeTypeDescriptorFromPackageFile($typeName));
214
215 9
            $syncBindingOps = array();
216
217 9 View Code Duplication
            foreach ($this->getUuidsByTypeName($typeName) as $uuid) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
218 3
                $syncBindingOp = $this->syncBindingUuid($uuid);
219 3
                $syncBindingOp->takeSnapshot();
220 3
                $syncBindingOps[] = $syncBindingOp;
221 9
            }
222
223 9
            $syncOp = $this->syncTypeName($typeName);
224 9
            $syncOp->takeSnapshot();
225
226 9
            $tx->execute($this->unloadTypeDescriptor($typeDescriptor));
227 9
            $tx->execute($syncOp);
228
229 9
            foreach ($syncBindingOps as $syncBindingOp) {
230 3
                $tx->execute($syncBindingOp);
231 9
            }
232
233 9
            $this->saveRootPackageFile();
234
235 8
            $tx->commit();
236 9
        } catch (Exception $e) {
237 1
            $tx->rollback();
238
239 1
            throw $e;
240
        }
241
242 8
        $this->emitWarningForDuplicateTypes();
243 8
    }
244
245
    /**
246
     * {@inheritdoc}
247
     */
248 4
    public function removeRootTypeDescriptors(Expression $expr)
249
    {
250 4
        $this->assertPackagesLoaded();
251
252 4
        $tx = new Transaction();
253 4
        $syncBindingOps = array();
254
255
        try {
256 4
            foreach ($this->getRootTypeDescriptors() as $typeDescriptor) {
257 4
                if ($expr->evaluate($typeDescriptor)) {
258 4
                    $typeName = $typeDescriptor->getTypeName();
259
260 4
                    $tx->execute($this->removeTypeDescriptorFromPackageFile($typeName));
261
262 4 View Code Duplication
                    foreach ($this->getUuidsByTypeName($typeName) as $uuid) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263 1
                        $syncBindingOp = $this->syncBindingUuid($uuid);
264 1
                        $syncBindingOp->takeSnapshot();
265 1
                        $syncBindingOps[] = $syncBindingOp;
266 4
                    }
267
268 4
                    $syncOp = $this->syncTypeName($typeName);
269 4
                    $syncOp->takeSnapshot();
270
271 4
                    $tx->execute($this->unloadTypeDescriptor($typeDescriptor));
272 4
                    $tx->execute($syncOp);
273 4
                }
274 4
            }
275
276 4
            foreach ($syncBindingOps as $syncBindingOp) {
277 1
                $tx->execute($syncBindingOp);
278 4
            }
279
280 4
            $this->saveRootPackageFile();
281
282 3
            $tx->commit();
283 4
        } catch (Exception $e) {
284 1
            $tx->rollback();
285
286 1
            throw $e;
287
        }
288
289 3
        $this->emitWarningForDuplicateTypes();
290 3
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 1
    public function clearRootTypeDescriptors()
296
    {
297 1
        $this->removeRootTypeDescriptors(Expr::true());
298 1
    }
299
300
    /**
301
     * {@inheritdoc}
302
     */
303 2
    public function getRootTypeDescriptor($typeName)
304
    {
305 2
        return $this->getTypeDescriptor($typeName, $this->rootPackage->getName());
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311 5 View Code Duplication
    public function getRootTypeDescriptors()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
312
    {
313 5
        $this->assertPackagesLoaded();
314
315 5
        $types = array();
316 5
        $rootPackageName = $this->rootPackage->getName();
317
318 5
        foreach ($this->typeDescriptors->toArray() as $typeName => $typesByPackage) {
319 5
            if (isset($typesByPackage[$rootPackageName])) {
320 5
                $types[] = $typesByPackage[$rootPackageName];
321 5
            }
322 5
        }
323
324 5
        return $types;
325
    }
326
327
    /**
328
     * {@inheritdoc}
329
     */
330 1 View Code Duplication
    public function findRootTypeDescriptors(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
331
    {
332 1
        $expr = Expr::method('getContainingPackage', Expr::same($this->rootPackage))
333 1
            ->andX($expr);
334
335 1
        return $this->findTypeDescriptors($expr);
336
    }
337
338
    /**
339
     * {@inheritdoc}
340
     */
341 1
    public function hasRootTypeDescriptor($typeName)
342
    {
343 1
        return $this->hasTypeDescriptor($typeName, $this->rootPackage->getName());
344
    }
345
346
    /**
347
     * {@inheritdoc}
348
     */
349 1 View Code Duplication
    public function hasRootTypeDescriptors(Expression $expr = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
350
    {
351 1
        $expr2 = Expr::method('getContainingPackage', Expr::same($this->rootPackage));
352
353 1
        if ($expr) {
354 1
            $expr2 = $expr2->andX($expr);
355 1
        }
356
357 1
        return $this->hasTypeDescriptors($expr2);
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363 6
    public function getTypeDescriptor($typeName, $packageName)
364
    {
365 6
        Assert::string($typeName, 'The type name must be a string. Got: %s');
366 5
        Assert::string($packageName, 'The package name must be a string. Got: %s');
367
368 4
        $this->assertPackagesLoaded();
369
370 4
        if (!$this->typeDescriptors->contains($typeName, $packageName)) {
371 2
            throw NoSuchTypeException::forTypeName($typeName);
372
        }
373
374 2
        return $this->typeDescriptors->get($typeName, $packageName);
375
    }
376
377
    /**
378
     * {@inheritdoc}
379
     */
380 2
    public function getTypeDescriptors()
381
    {
382 2
        $this->assertPackagesLoaded();
383
384 2
        $types = array();
385
386 2
        foreach ($this->typeDescriptors->toArray() as $typeName => $typesByPackage) {
387 1
            foreach ($typesByPackage as $type) {
388 1
                $types[] = $type;
389 1
            }
390 2
        }
391
392 2
        return $types;
393
    }
394
395
    /**
396
     * {@inheritdoc}
397
     */
398 2 View Code Duplication
    public function findTypeDescriptors(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
399
    {
400 2
        $this->assertPackagesLoaded();
401
402 2
        $typeDescriptors = array();
403
404 2
        foreach ($this->typeDescriptors->toArray() as $typeName => $descriptorsByPackage) {
405 2
            foreach ($descriptorsByPackage as $typeDescriptor) {
406 2
                if ($expr->evaluate($typeDescriptor)) {
407 2
                    $typeDescriptors[] = $typeDescriptor;
408 2
                }
409 2
            }
410 2
        }
411
412 2
        return $typeDescriptors;
413
    }
414
415
    /**
416
     * {@inheritdoc}
417
     */
418 3
    public function hasTypeDescriptor($typeName, $packageName = null)
419
    {
420 3
        Assert::nullOrString($packageName, 'The package name must be a string or null. Got: %s');
421
422 2
        $this->assertPackagesLoaded();
423
424 2
        return $this->typeDescriptors->contains($typeName, $packageName);
425
    }
426
427
    /**
428
     * {@inheritdoc}
429
     */
430 3 View Code Duplication
    public function hasTypeDescriptors(Expression $expr = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
431
    {
432 3
        $this->assertPackagesLoaded();
433
434 3
        if (!$expr) {
435 2
            return !$this->typeDescriptors->isEmpty();
436
        }
437
438 2
        foreach ($this->typeDescriptors->toArray() as $typeName => $descriptorsByPackage) {
439 2
            foreach ($descriptorsByPackage as $typeDescriptor) {
440 2
                if ($expr->evaluate($typeDescriptor)) {
441 2
                    return true;
442
                }
443 2
            }
444 2
        }
445
446 2
        return false;
447
    }
448
449
    /**
450
     * {@inheritdoc}
451
     */
452 13
    public function addRootBindingDescriptor(BindingDescriptor $bindingDescriptor, $flags = 0)
453
    {
454 13
        $this->assertPackagesLoaded();
455
456 13
        $typeName = $bindingDescriptor->getTypeName();
457 13
        $typeExists = $this->typeDescriptors->contains($typeName);
458
459 13
        if (!($flags & self::IGNORE_TYPE_NOT_FOUND) && !$typeExists) {
460 2
            throw NoSuchTypeException::forTypeName($typeName);
461
        }
462
463 11
        if (!($flags & self::IGNORE_TYPE_NOT_ENABLED) && $typeExists && !$this->typeDescriptors->getFirst($typeName)->isEnabled()) {
464 2
            throw TypeNotEnabledException::forTypeName($typeName);
465
        }
466
467 9
        $uuid = $bindingDescriptor->getUuid();
468 9
        $exists = $this->bindingDescriptors->contains($uuid);
469
        $existsInNonRoot = $exists
470 9
            ? !($this->bindingDescriptors->get($uuid)->getContainingPackage() instanceof RootPackage)
471 9
            : false;
472
473
        // We can only override bindings in the root package
474 9
        if ($existsInNonRoot || ($exists && !($flags & self::OVERRIDE))) {
475 3
            throw DuplicateBindingException::forUuid($uuid);
476
        }
477
478 6
        $tx = new Transaction();
479
480
        try {
481 6
            $syncOp = $this->syncBindingUuid($uuid);
482 6
            $syncOp->takeSnapshot();
483
484 6
            $tx->execute($this->loadBindingDescriptor($bindingDescriptor, $this->rootPackage));
485
486 6
            $this->assertBindingValid($bindingDescriptor);
487
488 5
            $tx->execute($this->addBindingDescriptorToPackageFile($bindingDescriptor));
489 5
            $tx->execute($syncOp);
490
491 5
            $this->saveRootPackageFile();
492
493 4
            $tx->commit();
494 6
        } catch (Exception $e) {
495 2
            $tx->rollback();
496
497 2
            throw $e;
498
        }
499 4
    }
500
501
    /**
502
     * {@inheritdoc}
503
     */
504 6
    public function removeRootBindingDescriptor(Uuid $uuid)
505
    {
506 6
        $this->assertPackagesLoaded();
507
508 6
        if (!$this->bindingDescriptors->contains($uuid)) {
509 1
            return;
510
        }
511
512 5
        $bindingDescriptor = $this->bindingDescriptors->get($uuid);
513
514 5
        if (!$bindingDescriptor->getContainingPackage() instanceof RootPackage) {
515 1
            return;
516
        }
517
518 4
        $tx = new Transaction();
519
520
        try {
521 4
            $syncOp = $this->syncBindingUuid($uuid);
522 4
            $syncOp->takeSnapshot();
523
524 4
            $tx->execute($this->unloadBindingDescriptor($bindingDescriptor));
525 4
            $tx->execute($syncOp);
526 4
            $tx->execute($this->removeBindingDescriptorFromPackageFile($uuid));
527
528 4
            $this->saveRootPackageFile();
529
530 3
            $tx->commit();
531 4
        } catch (Exception $e) {
532 1
            $tx->rollback();
533
534 1
            throw $e;
535
        }
536 3
    }
537
538
    /**
539
     * {@inheritdoc}
540
     */
541 3 View Code Duplication
    public function removeRootBindingDescriptors(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
542
    {
543 3
        $this->assertPackagesLoaded();
544
545 3
        $tx = new Transaction();
546
547
        try {
548 3
            foreach ($this->getRootBindingDescriptors() as $bindingDescriptor) {
549 3
                if ($expr->evaluate($bindingDescriptor)) {
550 3
                    $syncOp = $this->syncBindingUuid($bindingDescriptor->getUuid());
551 3
                    $syncOp->takeSnapshot();
552
553 3
                    $tx->execute($this->unloadBindingDescriptor($bindingDescriptor));
554 3
                    $tx->execute($syncOp);
555 3
                    $tx->execute($this->removeBindingDescriptorFromPackageFile($bindingDescriptor->getUuid()));
556 3
                }
557 3
            }
558
559 3
            $this->saveRootPackageFile();
560
561 2
            $tx->commit();
562 3
        } catch (Exception $e) {
563 1
            $tx->rollback();
564
565 1
            throw $e;
566
        }
567 2
    }
568
569
    /**
570
     * {@inheritdoc}
571
     */
572 1
    public function clearRootBindingDescriptors()
573
    {
574 1
        $this->removeRootBindingDescriptors(Expr::true());
575 1
    }
576
577
    /**
578
     * {@inheritdoc}
579
     */
580
    public function getRootBindingDescriptor(Uuid $uuid)
581
    {
582
        $binding = $this->getBindingDescriptor($uuid);
583
584
        if (!$binding->getContainingPackage() instanceof RootPackage) {
585
            throw NoSuchBindingException::forUuidAndPackage($uuid, $this->rootPackage->getName());
586
        }
587
588
        return $binding;
589
    }
590
591
    /**
592
     * {@inheritdoc}
593
     */
594 3 View Code Duplication
    public function getRootBindingDescriptors()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
595
    {
596 3
        $this->assertPackagesLoaded();
597
598 3
        $bindings = array();
599
600 3
        foreach ($this->bindingDescriptors->toArray() as $binding) {
601 3
            if ($binding->getContainingPackage() instanceof RootPackage) {
602 3
                $bindings[] = $binding;
603 3
            }
604 3
        }
605
606 3
        return $bindings;
607
    }
608
609
    /**
610
     * {@inheritdoc}
611
     */
612 1 View Code Duplication
    public function findRootBindingDescriptors(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
613
    {
614 1
        $expr = Expr::method('getContainingPackage', Expr::same($this->rootPackage))
615 1
            ->andX($expr);
616
617 1
        return $this->findBindingDescriptors($expr);
618
    }
619
620
    /**
621
     * {@inheritdoc}
622
     */
623 1
    public function hasRootBindingDescriptor(Uuid $uuid)
624
    {
625 1
        return $this->hasBindingDescriptor($uuid) && $this->getBindingDescriptor($uuid)->getContainingPackage() instanceof RootPackage;
626
    }
627
628
    /**
629
     * {@inheritdoc}
630
     */
631 1 View Code Duplication
    public function hasRootBindingDescriptors(Expression $expr = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
632
    {
633 1
        $expr2 = Expr::method('getContainingPackage', Expr::same($this->rootPackage));
634
635 1
        if ($expr) {
636 1
            $expr2 = $expr2->andX($expr);
637 1
        }
638
639 1
        return $this->hasBindingDescriptors($expr2);
640
    }
641
642
    /**
643
     * {@inheritdoc}
644
     */
645 8 View Code Duplication
    public function enableBindingDescriptor(Uuid $uuid)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
646
    {
647 8
        $this->assertPackagesLoaded();
648
649 8
        if (!$this->bindingDescriptors->contains($uuid)) {
650 1
            throw NoSuchBindingException::forUuid($uuid);
651
        }
652
653 7
        $bindingDescriptor = $this->bindingDescriptors->get($uuid);
654 7
        $package = $bindingDescriptor->getContainingPackage();
655
656 7
        if ($package instanceof RootPackage) {
657 1
            throw NonRootPackageExpectedException::cannotEnableBinding($uuid, $package->getName());
658
        }
659
660 6
        if ($bindingDescriptor->isTypeNotFound()) {
661 1
            throw NoSuchTypeException::forTypeName($bindingDescriptor->getTypeName());
662
        }
663
664 5
        if ($bindingDescriptor->isTypeNotEnabled()) {
665 1
            throw TypeNotEnabledException::forTypeName($bindingDescriptor->getTypeName());
666
        }
667
668 4
        if ($bindingDescriptor->isEnabled()) {
669 1
            return;
670
        }
671
672 3
        $tx = new Transaction();
673
674
        try {
675 3
            $syncOp = $this->syncBindingUuid($uuid);
676 3
            $syncOp->takeSnapshot();
677
678 3
            $tx->execute($this->enableBindingUuid($uuid, $package->getInstallInfo()));
679 3
            $tx->execute($syncOp);
680
681 3
            $this->saveRootPackageFile();
682
683 1
            $tx->commit();
684 3
        } catch (Exception $e) {
685 2
            $tx->rollback();
686
687 2
            throw $e;
688
        }
689 1
    }
690
691
    /**
692
     * {@inheritdoc}
693
     */
694 8 View Code Duplication
    public function disableBindingDescriptor(Uuid $uuid)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
695
    {
696 8
        $this->assertPackagesLoaded();
697
698 8
        if (!$this->bindingDescriptors->contains($uuid)) {
699 1
            throw NoSuchBindingException::forUuid($uuid);
700
        }
701
702 7
        $bindingDescriptor = $this->bindingDescriptors->get($uuid);
703 7
        $package = $bindingDescriptor->getContainingPackage();
704
705 7
        if ($package instanceof RootPackage) {
706 1
            throw NonRootPackageExpectedException::cannotDisableBinding($uuid, $package->getName());
707
        }
708
709 6
        if ($bindingDescriptor->isTypeNotFound()) {
710 1
            throw NoSuchTypeException::forTypeName($bindingDescriptor->getTypeName());
711
        }
712
713 5
        if ($bindingDescriptor->isTypeNotEnabled()) {
714 1
            throw TypeNotEnabledException::forTypeName($bindingDescriptor->getTypeName());
715
        }
716
717 4
        if ($bindingDescriptor->isDisabled()) {
718 1
            return;
719
        }
720
721 3
        $tx = new Transaction();
722
723
        try {
724 3
            $syncOp = $this->syncBindingUuid($uuid);
725 3
            $syncOp->takeSnapshot();
726
727 3
            $tx->execute($this->disableBindingUuid($uuid, $package->getInstallInfo()));
728 3
            $tx->execute($syncOp);
729
730 3
            $this->saveRootPackageFile();
731
732 1
            $tx->commit();
733 3
        } catch (Exception $e) {
734 2
            $tx->rollback();
735
736 2
            throw $e;
737
        }
738 1
    }
739
740
    /**
741
     * {@inheritdoc}
742
     */
743 2
    public function removeObsoleteDisabledBindingDescriptors()
744
    {
745 2
        $this->assertPackagesLoaded();
746
747 2
        $removedUuidsByPackage = array();
748
749
        try {
750 2
            foreach ($this->rootPackageFile->getInstallInfos() as $installInfo) {
751 2
                foreach ($installInfo->getDisabledBindingUuids() as $uuid) {
752 2
                    if (!$this->bindingDescriptors->contains($uuid)) {
753 2
                        $installInfo->removeDisabledBindingUuid($uuid);
754 2
                        $removedUuidsByPackage[$installInfo->getPackageName()][] = $uuid;
755 2
                    }
756 2
                }
757 2
            }
758
759 2
            $this->saveRootPackageFile();
760 2
        } catch (Exception $e) {
761 1
            foreach ($removedUuidsByPackage as $packageName => $removedUuids) {
762 1
                $installInfo = $this->rootPackageFile->getInstallInfo($packageName);
763
764 1
                foreach ($removedUuids as $uuid) {
765 1
                    $installInfo->addDisabledBindingUuid($uuid);
766 1
                }
767 1
            }
768
769 1
            throw $e;
770
        }
771 1
    }
772
773
    /**
774
     * {@inheritdoc}
775
     */
776 3
    public function getBindingDescriptor(Uuid $uuid)
777
    {
778 3
        $this->assertPackagesLoaded();
779
780 3
        if (!$this->bindingDescriptors->contains($uuid)) {
781 1
            throw NoSuchBindingException::forUuid($uuid);
782
        }
783
784 2
        return $this->bindingDescriptors->get($uuid);
785
    }
786
787
    /**
788
     * {@inheritdoc}
789
     */
790 3
    public function getBindingDescriptors()
791
    {
792 3
        $this->assertPackagesLoaded();
793
794 2
        return array_values($this->bindingDescriptors->toArray());
795
    }
796
797
    /**
798
     * {@inheritdoc}
799
     */
800 2 View Code Duplication
    public function findBindingDescriptors(Expression $expr)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
801
    {
802 2
        $this->assertPackagesLoaded();
803
804 2
        $descriptors = array();
805
806 2
        foreach ($this->bindingDescriptors->toArray() as $descriptor) {
807 2
            if ($expr->evaluate($descriptor)) {
808 2
                $descriptors[] = $descriptor;
809 2
            }
810 2
        }
811
812 2
        return $descriptors;
813
    }
814
815
    /**
816
     * {@inheritdoc}
817
     */
818 2
    public function hasBindingDescriptor(Uuid $uuid)
819
    {
820 2
        $this->assertPackagesLoaded();
821
822 2
        return $this->bindingDescriptors->contains($uuid);
823
    }
824
825
    /**
826
     * {@inheritdoc}
827
     */
828 3
    public function hasBindingDescriptors(Expression $expr = null)
829
    {
830 3
        $this->assertPackagesLoaded();
831
832 3
        if (!$expr) {
833 2
            return !$this->bindingDescriptors->isEmpty();
834
        }
835
836 2
        foreach ($this->bindingDescriptors->toArray() as $bindingDescriptor) {
837 2
            if ($expr->evaluate($bindingDescriptor)) {
838 2
                return true;
839
            }
840 2
        }
841
842 2
        return false;
843
    }
844
845
    /**
846
     * {@inheritdoc}
847
     */
848 8
    public function buildDiscovery()
849
    {
850 8
        $this->assertPackagesLoaded();
851 8
        $this->emitWarningForDuplicateTypes();
852 8
        $this->emitWarningForInvalidBindings();
853
854 8
        if ($this->discovery->hasBindings() || $this->discovery->hasBindingTypes()) {
855 2
            throw new DiscoveryNotEmptyException('The discovery is not empty.');
856
        }
857
858 6
        $tx = new Transaction();
859
860
        try {
861 6
            foreach ($this->typeDescriptors->toArray() as $typeName => $descriptorsByPackage) {
862 5
                foreach ($descriptorsByPackage as $typeDescriptor) {
863 5
                    if ($typeDescriptor->isEnabled()) {
864 4
                        $tx->execute($this->addBindingType($typeDescriptor));
865 4
                    }
866 5
                }
867 6
            }
868
869 6
            foreach ($this->bindingDescriptors->toArray() as $bindingDescriptor) {
870 5
                if ($bindingDescriptor->isEnabled()) {
871 2
                    $tx->execute($this->addBinding($bindingDescriptor));
872 2
                }
873 6
            }
874
875 6
            $tx->commit();
876 6
        } catch (Exception $e) {
877
            $tx->rollback();
878
879
            throw $e;
880
        }
881 6
    }
882
883
    /**
884
     * {@inheritdoc}
885
     */
886 1
    public function clearDiscovery()
887
    {
888 1
        $this->discovery->removeBindingTypes();
889 1
    }
890
891 93
    private function assertPackagesLoaded()
892
    {
893 93
        if (!$this->typeDescriptors) {
894 93
            $this->loadPackages();
895 92
        }
896 92
    }
897
898 6
    private function assertBindingValid(BindingDescriptor $bindingDescriptor)
899
    {
900 6
        if ($bindingDescriptor->isTypeNotFound() || $bindingDescriptor->isTypeNotEnabled()) {
901 2
            return;
902
        }
903
904 4
        foreach ($bindingDescriptor->getLoadErrors() as $exception) {
905 1
            throw $exception;
906 3
        }
907 3
    }
908
909 93
    private function loadPackages()
910
    {
911 93
        $this->typeDescriptors = new BindingTypeDescriptorCollection();
912 93
        $this->bindingDescriptors = new BindingDescriptorCollection();
913
914
        // First load all the types
915 93 View Code Duplication
        foreach ($this->packages as $package) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
916 93
            if (null === $package->getPackageFile()) {
917 1
                continue;
918
            }
919
920 93
            foreach ($package->getPackageFile()->getTypeDescriptors() as $typeDescriptor) {
921 72
                $this->loadTypeDescriptor($typeDescriptor, $package)->execute();
922 93
            }
923 93
        }
924
925
        // Then the bindings for the loaded types
926 93
        foreach ($this->packages as $package) {
927 93
            if (null === $package->getPackageFile()) {
928 1
                continue;
929
            }
930
931 93
            foreach ($package->getPackageFile()->getBindingDescriptors() as $bindingDescriptor) {
932
                // This REALLY shouldn't happen
933 48
                if ($this->bindingDescriptors->contains($bindingDescriptor->getUuid())) {
934 1
                    throw DuplicateBindingException::forUuid($bindingDescriptor->getUuid());
935
                }
936
937 48
                $this->loadBindingDescriptor($bindingDescriptor, $package)->execute();
938 93
            }
939 93
        }
940 92
    }
941
942 24
    private function emitWarningForDuplicateTypes()
943
    {
944 24
        foreach ($this->typeDescriptors->getTypeNames() as $typeName) {
945 15
            $packageNames = $this->typeDescriptors->getPackageNames($typeName);
946
947 15
            if (count($packageNames) > 1) {
948 4
                $lastPackageName = array_pop($packageNames);
949
950 4
                $this->logger->warning(sprintf(
951
                    'The packages "%s" and "%s" contain type definitions for '.
952 4
                    'the same type "%s". The type has been disabled.',
953 4
                    implode('", "', $packageNames),
954 4
                    $lastPackageName,
955
                    $typeName
956 4
                ));
957 4
            }
958 24
        }
959 24
    }
960
961 8
    private function emitWarningForInvalidBindings()
962
    {
963 8
        foreach ($this->bindingDescriptors->toArray() as $binding) {
964 5
            foreach ($binding->getLoadErrors() as $exception) {
965 2
                $this->logger->warning(sprintf(
966 2
                    'The binding "%s" in package "%s" is invalid: %s',
967 2
                    $binding->getUuid()->toString(),
968 2
                    $binding->getContainingPackage()->getName(),
969 2
                    $exception->getMessage()
970 2
                ));
971 5
            }
972 8
        }
973 8
    }
974
975 17
    private function getUuidsByTypeName($typeName)
976
    {
977 17
        $uuids = array();
978
979 17
        foreach ($this->bindingDescriptors->getUuids() as $uuid) {
980 5
            if ($typeName === $this->bindingDescriptors->get($uuid)->getTypeName()) {
981 5
                $uuids[$uuid->toString()] = $uuid;
982 5
            }
983 17
        }
984
985 17
        return $uuids;
986
    }
987
988 37
    private function saveRootPackageFile()
989
    {
990 37
        $this->packageFileStorage->saveRootPackageFile($this->rootPackageFile);
991 26
    }
992
993 4
    private function addTypeDescriptorToPackageFile(BindingTypeDescriptor $typeDescriptor)
994
    {
995 4
        return new AddTypeDescriptorToPackageFile($typeDescriptor, $this->rootPackageFile);
996
    }
997
998 13
    private function removeTypeDescriptorFromPackageFile($typeName)
999
    {
1000 13
        return new RemoveTypeDescriptorFromPackageFile($typeName, $this->rootPackageFile);
1001
    }
1002
1003 74 View Code Duplication
    private function loadTypeDescriptor(BindingTypeDescriptor $typeDescriptor, Package $package)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1004
    {
1005 74
        $typeName = $typeDescriptor->getTypeName();
1006
1007 74
        return new InterceptedOperation(
1008 74
            new LoadTypeDescriptor($typeDescriptor, $package, $this->typeDescriptors),
1009
            array(
1010 74
                new UpdateDuplicateMarksForTypeName($typeName, $this->typeDescriptors),
1011 74
                new ReloadBindingDescriptorsByTypeName($typeName, $this->bindingDescriptors, $this->typeDescriptors),
1012
            )
1013 74
        );
1014
    }
1015
1016 13 View Code Duplication
    private function unloadTypeDescriptor(BindingTypeDescriptor $typeDescriptor)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1017
    {
1018 13
        $typeName = $typeDescriptor->getTypeName();
1019
1020 13
        return new InterceptedOperation(
1021 13
            new UnloadTypeDescriptor($typeDescriptor, $this->typeDescriptors),
1022
            array(
1023 13
                new UpdateDuplicateMarksForTypeName($typeName, $this->typeDescriptors),
1024 13
                new ReloadBindingDescriptorsByTypeName($typeName, $this->bindingDescriptors, $this->typeDescriptors),
1025
            )
1026 13
        );
1027
    }
1028
1029 4
    private function addBindingType(BindingTypeDescriptor $typeDescriptor)
1030
    {
1031 4
        return new AddBindingType($typeDescriptor, $this->discovery);
1032
    }
1033
1034 17
    private function syncTypeName($typeName)
1035
    {
1036 17
        return new SyncTypeName($typeName, $this->discovery, $this->typeDescriptors);
1037
    }
1038
1039 5
    private function addBindingDescriptorToPackageFile(BindingDescriptor $bindingDescriptor)
1040
    {
1041 5
        return new AddBindingDescriptorToPackageFile($bindingDescriptor, $this->rootPackageFile);
1042
    }
1043
1044 7
    private function removeBindingDescriptorFromPackageFile(Uuid $uuid)
1045
    {
1046 7
        return new RemoveBindingDescriptorFromPackageFile($uuid, $this->rootPackageFile);
1047
    }
1048
1049 52
    private function loadBindingDescriptor(BindingDescriptor $bindingDescriptor, Package $package)
1050
    {
1051 52
        return new LoadBindingDescriptor($bindingDescriptor, $package, $this->bindingDescriptors, $this->typeDescriptors);
1052
    }
1053
1054 7
    private function unloadBindingDescriptor(BindingDescriptor $bindingDescriptor)
1055
    {
1056 7
        return new UnloadBindingDescriptor($bindingDescriptor, $this->bindingDescriptors);
1057
    }
1058
1059 3 View Code Duplication
    private function enableBindingUuid(Uuid $uuid, InstallInfo $installInfo)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1060
    {
1061 3
        return new InterceptedOperation(
1062 3
            new EnableBindingUuid($uuid, $installInfo),
1063 3
            new ReloadBindingDescriptorsByUuid($uuid, $this->bindingDescriptors, $this->typeDescriptors)
1064 3
        );
1065
    }
1066
1067 3 View Code Duplication
    private function disableBindingUuid(Uuid $uuid, InstallInfo $installInfo)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1068
    {
1069 3
        return new InterceptedOperation(
1070 3
            new DisableBindingUuid($uuid, $installInfo),
1071 3
            new ReloadBindingDescriptorsByUuid($uuid, $this->bindingDescriptors, $this->typeDescriptors)
1072 3
        );
1073
    }
1074
1075 2
    private function addBinding(BindingDescriptor $bindingDescriptor)
1076
    {
1077 2
        return new AddBinding($bindingDescriptor, $this->discovery);
1078
    }
1079
1080 24
    private function syncBindingUuid(Uuid $uuid)
1081
    {
1082 24
        return new SyncBindingUuid($uuid, $this->discovery, $this->bindingDescriptors);
1083
    }
1084
}
1085