Completed
Pull Request — master (#28)
by Titouan
09:30
created

DiscoveryManagerImpl::getContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 4
c 1
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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 98
    public function __construct(
123
        ProjectContext $context,
124
        EditableDiscovery $discovery,
125
        PackageCollection $packages,
126
        PackageFileStorage $packageFileStorage,
127
        LoggerInterface $logger = null
128
    ) {
129 98
        $this->context = $context;
130 98
        $this->discovery = $discovery;
131 98
        $this->packages = $packages;
132 98
        $this->packageFileStorage = $packageFileStorage;
133 98
        $this->rootPackage = $packages->getRootPackage();
134 98
        $this->rootPackageFile = $context->getRootPackageFile();
135 98
        $this->logger = $logger ?: new NullLogger();
136 98
    }
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 1
    public function getTypeDescriptors()
381
    {
382 1
        $this->assertPackagesLoaded();
383
384 1
        $types = array();
385
386 1
        foreach ($this->typeDescriptors->toArray() as $typeName => $typesByPackage) {
387 1
            foreach ($typesByPackage as $type) {
388 1
                $types[] = $type;
389 1
            }
390 1
        }
391
392 1
        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 2
    public function getBindingDescriptors()
791
    {
792 2
        $this->assertPackagesLoaded();
793
794 1
        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 92
    private function assertPackagesLoaded()
892
    {
893 92
        if (!$this->typeDescriptors) {
894 92
            $this->loadPackages();
895 91
        }
896 91
    }
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 92
    private function loadPackages()
910
    {
911 92
        $this->typeDescriptors = new BindingTypeDescriptorCollection();
912 92
        $this->bindingDescriptors = new BindingDescriptorCollection();
913
914
        // First load all the types
915 92
        foreach ($this->packages as $package) {
916 92
            foreach ($package->getPackageFile()->getTypeDescriptors() as $typeDescriptor) {
917 72
                $this->loadTypeDescriptor($typeDescriptor, $package)->execute();
918 92
            }
919 92
        }
920
921
        // Then the bindings for the loaded types
922 92
        foreach ($this->packages as $package) {
923 92
            foreach ($package->getPackageFile()->getBindingDescriptors() as $bindingDescriptor) {
924
                // This REALLY shouldn't happen
925 48
                if ($this->bindingDescriptors->contains($bindingDescriptor->getUuid())) {
926 1
                    throw DuplicateBindingException::forUuid($bindingDescriptor->getUuid());
927
                }
928
929 48
                $this->loadBindingDescriptor($bindingDescriptor, $package)->execute();
930 92
            }
931 92
        }
932 91
    }
933
934 24
    private function emitWarningForDuplicateTypes()
935
    {
936 24
        foreach ($this->typeDescriptors->getTypeNames() as $typeName) {
937 15
            $packageNames = $this->typeDescriptors->getPackageNames($typeName);
938
939 15
            if (count($packageNames) > 1) {
940 4
                $lastPackageName = array_pop($packageNames);
941
942 4
                $this->logger->warning(sprintf(
943
                    'The packages "%s" and "%s" contain type definitions for '.
944 4
                    'the same type "%s". The type has been disabled.',
945 4
                    implode('", "', $packageNames),
946 4
                    $lastPackageName,
947
                    $typeName
948 4
                ));
949 4
            }
950 24
        }
951 24
    }
952
953 8
    private function emitWarningForInvalidBindings()
954
    {
955 8
        foreach ($this->bindingDescriptors->toArray() as $binding) {
956 5
            foreach ($binding->getLoadErrors() as $exception) {
957 2
                $this->logger->warning(sprintf(
958 2
                    'The binding "%s" in package "%s" is invalid: %s',
959 2
                    $binding->getUuid()->toString(),
960 2
                    $binding->getContainingPackage()->getName(),
961 2
                    $exception->getMessage()
962 2
                ));
963 5
            }
964 8
        }
965 8
    }
966
967 17
    private function getUuidsByTypeName($typeName)
968
    {
969 17
        $uuids = array();
970
971 17
        foreach ($this->bindingDescriptors->getUuids() as $uuid) {
972 5
            if ($typeName === $this->bindingDescriptors->get($uuid)->getTypeName()) {
973 5
                $uuids[$uuid->toString()] = $uuid;
974 5
            }
975 17
        }
976
977 17
        return $uuids;
978
    }
979
980 37
    private function saveRootPackageFile()
981
    {
982 37
        $this->packageFileStorage->saveRootPackageFile($this->rootPackageFile);
983 26
    }
984
985 4
    private function addTypeDescriptorToPackageFile(BindingTypeDescriptor $typeDescriptor)
986
    {
987 4
        return new AddTypeDescriptorToPackageFile($typeDescriptor, $this->rootPackageFile);
988
    }
989
990 13
    private function removeTypeDescriptorFromPackageFile($typeName)
991
    {
992 13
        return new RemoveTypeDescriptorFromPackageFile($typeName, $this->rootPackageFile);
993
    }
994
995 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...
996
    {
997 74
        $typeName = $typeDescriptor->getTypeName();
998
999 74
        return new InterceptedOperation(
1000 74
            new LoadTypeDescriptor($typeDescriptor, $package, $this->typeDescriptors),
1001
            array(
1002 74
                new UpdateDuplicateMarksForTypeName($typeName, $this->typeDescriptors),
1003 74
                new ReloadBindingDescriptorsByTypeName($typeName, $this->bindingDescriptors, $this->typeDescriptors),
1004
            )
1005 74
        );
1006
    }
1007
1008 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...
1009
    {
1010 13
        $typeName = $typeDescriptor->getTypeName();
1011
1012 13
        return new InterceptedOperation(
1013 13
            new UnloadTypeDescriptor($typeDescriptor, $this->typeDescriptors),
1014
            array(
1015 13
                new UpdateDuplicateMarksForTypeName($typeName, $this->typeDescriptors),
1016 13
                new ReloadBindingDescriptorsByTypeName($typeName, $this->bindingDescriptors, $this->typeDescriptors),
1017
            )
1018 13
        );
1019
    }
1020
1021 4
    private function addBindingType(BindingTypeDescriptor $typeDescriptor)
1022
    {
1023 4
        return new AddBindingType($typeDescriptor, $this->discovery);
1024
    }
1025
1026 17
    private function syncTypeName($typeName)
1027
    {
1028 17
        return new SyncTypeName($typeName, $this->discovery, $this->typeDescriptors);
1029
    }
1030
1031 5
    private function addBindingDescriptorToPackageFile(BindingDescriptor $bindingDescriptor)
1032
    {
1033 5
        return new AddBindingDescriptorToPackageFile($bindingDescriptor, $this->rootPackageFile);
1034
    }
1035
1036 7
    private function removeBindingDescriptorFromPackageFile(Uuid $uuid)
1037
    {
1038 7
        return new RemoveBindingDescriptorFromPackageFile($uuid, $this->rootPackageFile);
1039
    }
1040
1041 52
    private function loadBindingDescriptor(BindingDescriptor $bindingDescriptor, Package $package)
1042
    {
1043 52
        return new LoadBindingDescriptor($bindingDescriptor, $package, $this->bindingDescriptors, $this->typeDescriptors);
1044
    }
1045
1046 7
    private function unloadBindingDescriptor(BindingDescriptor $bindingDescriptor)
1047
    {
1048 7
        return new UnloadBindingDescriptor($bindingDescriptor, $this->bindingDescriptors);
1049
    }
1050
1051 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...
1052
    {
1053 3
        return new InterceptedOperation(
1054 3
            new EnableBindingUuid($uuid, $installInfo),
1055 3
            new ReloadBindingDescriptorsByUuid($uuid, $this->bindingDescriptors, $this->typeDescriptors)
1056 3
        );
1057
    }
1058
1059 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...
1060
    {
1061 3
        return new InterceptedOperation(
1062 3
            new DisableBindingUuid($uuid, $installInfo),
1063 3
            new ReloadBindingDescriptorsByUuid($uuid, $this->bindingDescriptors, $this->typeDescriptors)
1064 3
        );
1065
    }
1066
1067 2
    private function addBinding(BindingDescriptor $bindingDescriptor)
1068
    {
1069 2
        return new AddBinding($bindingDescriptor, $this->discovery);
1070
    }
1071
1072 24
    private function syncBindingUuid(Uuid $uuid)
1073
    {
1074 24
        return new SyncBindingUuid($uuid, $this->discovery, $this->bindingDescriptors);
1075
    }
1076
}
1077