Failed Conditions
Pull Request — master (#56)
by Bernhard
11:07
created

DiscoveryManagerImpl::findBindingDescriptors()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 14
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 14
loc 14
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
crap 3
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\Binding\Binding;
18
use Puli\Discovery\Api\EditableDiscovery;
19
use Puli\Manager\Api\Context\ProjectContext;
20
use Puli\Manager\Api\Discovery\BindingDescriptor;
21
use Puli\Manager\Api\Discovery\BindingTypeDescriptor;
22
use Puli\Manager\Api\Discovery\DiscoveryManager;
23
use Puli\Manager\Api\Discovery\DiscoveryNotEmptyException;
24
use Puli\Manager\Api\Discovery\DuplicateBindingException;
25
use Puli\Manager\Api\Discovery\DuplicateTypeException;
26
use Puli\Manager\Api\Discovery\NoSuchBindingException;
27
use Puli\Manager\Api\Discovery\NoSuchTypeException;
28
use Puli\Manager\Api\Discovery\TypeNotEnabledException;
29
use Puli\Manager\Api\Module\InstallInfo;
30
use Puli\Manager\Api\Module\Module;
31
use Puli\Manager\Api\Module\ModuleList;
32
use Puli\Manager\Api\Module\RootModule;
33
use Puli\Manager\Api\Module\RootModuleFile;
34
use Puli\Manager\Api\NonRootModuleExpectedException;
35
use Puli\Manager\Assert\Assert;
36
use Puli\Manager\Discovery\Binding\AddBinding;
37
use Puli\Manager\Discovery\Binding\AddBindingDescriptorToModuleFile;
38
use Puli\Manager\Discovery\Binding\BindingDescriptorCollection;
39
use Puli\Manager\Discovery\Binding\DisableBindingUuid;
40
use Puli\Manager\Discovery\Binding\EnableBindingUuid;
41
use Puli\Manager\Discovery\Binding\LoadBindingDescriptor;
42
use Puli\Manager\Discovery\Binding\ReloadBindingDescriptorsByTypeName;
43
use Puli\Manager\Discovery\Binding\RemoveBindingDescriptorFromModuleFile;
44
use Puli\Manager\Discovery\Binding\SyncBinding;
45
use Puli\Manager\Discovery\Binding\UnloadBindingDescriptor;
46
use Puli\Manager\Discovery\Type\AddBindingType;
47
use Puli\Manager\Discovery\Type\AddTypeDescriptorToModuleFile;
48
use Puli\Manager\Discovery\Type\BindingTypeDescriptorCollection;
49
use Puli\Manager\Discovery\Type\LoadTypeDescriptor;
50
use Puli\Manager\Discovery\Type\RemoveTypeDescriptorFromModuleFile;
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\Json\JsonStorage;
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 ModuleList
85
     */
86
    private $modules;
87
88
    /**
89
     * @var JsonStorage
90
     */
91
    private $jsonStorage;
92
93
    /**
94
     * @var RootModule
95
     */
96
    private $rootModule;
97
98
    /**
99
     * @var RootModuleFile
100
     */
101
    private $rootModuleFile;
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 ModuleList           $modules
119
     * @param JsonStorage          $jsonStorage
120
     * @param LoggerInterface|null $logger
121
     */
122 98
    public function __construct(
123
        ProjectContext $context,
124
        EditableDiscovery $discovery,
125
        ModuleList $modules,
126
        JsonStorage $jsonStorage,
127
        LoggerInterface $logger = null
128
    ) {
129 98
        $this->context = $context;
130 98
        $this->discovery = $discovery;
131 98
        $this->modules = $modules;
132 98
        $this->jsonStorage = $jsonStorage;
133 98
        $this->rootModule = $modules->getRootModule();
134 98
        $this->rootModuleFile = $context->getRootModuleFile();
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->assertModulesLoaded();
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...
Bug introduced by
The method getUuidsByTypeName() does not seem to exist on object<Puli\Manager\Disc...y\DiscoveryManagerImpl>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
168
                $syncBindingOp = $this->syncBinding($uuid);
169
                $syncBindingOp->takeSnapshot();
170
                $syncBindingOps[] = $syncBindingOp;
171
            }
172
173
            $syncOp = $this->syncTypeName($typeName);
174
            $syncOp->takeSnapshot();
175
176
            $tx->execute($this->loadTypeDescriptor($typeDescriptor, $this->rootModule));
177
            $tx->execute($this->addTypeDescriptorToModuleFile($typeDescriptor));
178
            $tx->execute($syncOp);
179
180
            foreach ($syncBindingOps as $syncBindingOp) {
181
                $tx->execute($syncBindingOp);
182
            }
183
184
            $this->saveRootModuleFile();
185
186
            $tx->commit();
187 4
        } catch (Exception $e) {
188
            $tx->rollback();
189
190
            throw $e;
191
        }
192
    }
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->assertModulesLoaded();
204
205 11
        if (!$this->typeDescriptors->contains($typeName, $this->rootModule->getName())) {
206 2
            return;
207
        }
208
209 9
        $typeDescriptor = $this->typeDescriptors->get($typeName, $this->rootModule->getName());
210 9
        $tx = new Transaction();
211
212
        try {
213 9
            $tx->execute($this->removeTypeDescriptorFromModuleFile($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...
Bug introduced by
The method getUuidsByTypeName() does not seem to exist on object<Puli\Manager\Disc...y\DiscoveryManagerImpl>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
218
                $syncBindingOp = $this->syncBinding($uuid);
219
                $syncBindingOp->takeSnapshot();
220
                $syncBindingOps[] = $syncBindingOp;
221
            }
222
223
            $syncOp = $this->syncTypeName($typeName);
224
            $syncOp->takeSnapshot();
225
226
            $tx->execute($this->unloadTypeDescriptor($typeDescriptor));
227
            $tx->execute($syncOp);
228
229
            foreach ($syncBindingOps as $syncBindingOp) {
230
                $tx->execute($syncBindingOp);
231
            }
232
233
            $this->saveRootModuleFile();
234
235
            $tx->commit();
236 9
        } catch (Exception $e) {
237
            $tx->rollback();
238
239
            throw $e;
240
        }
241
242
        $this->emitWarningForDuplicateTypes();
243
    }
244
245
    /**
246
     * {@inheritdoc}
247
     */
248 4
    public function removeRootTypeDescriptors(Expression $expr)
249
    {
250 4
        $this->assertModulesLoaded();
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->removeTypeDescriptorFromModuleFile($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...
Bug introduced by
The method getUuidsByTypeName() does not seem to exist on object<Puli\Manager\Disc...y\DiscoveryManagerImpl>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
263
                        $syncBindingOp = $this->syncBinding($uuid);
264
                        $syncBindingOp->takeSnapshot();
265
                        $syncBindingOps[] = $syncBindingOp;
266
                    }
267
268
                    $syncOp = $this->syncTypeName($typeName);
269
                    $syncOp->takeSnapshot();
270
271
                    $tx->execute($this->unloadTypeDescriptor($typeDescriptor));
272
                    $tx->execute($syncOp);
273
                }
274
            }
275
276
            foreach ($syncBindingOps as $syncBindingOp) {
277
                $tx->execute($syncBindingOp);
278
            }
279
280
            $this->saveRootModuleFile();
281
282
            $tx->commit();
283 4
        } catch (Exception $e) {
284
            $tx->rollback();
285
286
            throw $e;
287
        }
288
289
        $this->emitWarningForDuplicateTypes();
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295 1
    public function clearRootTypeDescriptors()
296
    {
297 1
        $this->removeRootTypeDescriptors(Expr::true());
298
    }
299
300
    /**
301
     * {@inheritdoc}
302
     */
303 2
    public function getRootTypeDescriptor($typeName)
304
    {
305 2
        return $this->getTypeDescriptor($typeName, $this->rootModule->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->assertModulesLoaded();
314
315 5
        $types = array();
316 5
        $rootModuleName = $this->rootModule->getName();
317
318 5
        foreach ($this->typeDescriptors->toArray() as $typeName => $typesByModule) {
319 5
            if (isset($typesByModule[$rootModuleName])) {
320 5
                $types[] = $typesByModule[$rootModuleName];
321
            }
322
        }
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('getContainingModule', Expr::same($this->rootModule))
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->rootModule->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('getContainingModule', Expr::same($this->rootModule));
352
353 1
        if ($expr) {
354 1
            $expr2 = $expr2->andX($expr);
355
        }
356
357 1
        return $this->hasTypeDescriptors($expr2);
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363 6
    public function getTypeDescriptor($typeName, $moduleName)
364
    {
365 6
        Assert::string($typeName, 'The type name must be a string. Got: %s');
366 5
        Assert::string($moduleName, 'The module name must be a string. Got: %s');
367
368 4
        $this->assertModulesLoaded();
369
370 4
        if (!$this->typeDescriptors->contains($typeName, $moduleName)) {
371 2
            throw NoSuchTypeException::forTypeName($typeName);
372
        }
373
374 2
        return $this->typeDescriptors->get($typeName, $moduleName);
375
    }
376
377
    /**
378
     * {@inheritdoc}
379
     */
380 2
    public function getTypeDescriptors()
381
    {
382 2
        $this->assertModulesLoaded();
383
384 2
        $types = array();
385
386 2
        foreach ($this->typeDescriptors->toArray() as $typeName => $typesByModule) {
387 1
            foreach ($typesByModule as $type) {
388 1
                $types[] = $type;
389
            }
390
        }
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->assertModulesLoaded();
401
402 2
        $typeDescriptors = array();
403
404 2
        foreach ($this->typeDescriptors->toArray() as $typeName => $descriptorsByModule) {
405 2
            foreach ($descriptorsByModule as $typeDescriptor) {
406 2
                if ($expr->evaluate($typeDescriptor)) {
407 2
                    $typeDescriptors[] = $typeDescriptor;
408
                }
409
            }
410
        }
411
412 2
        return $typeDescriptors;
413
    }
414
415
    /**
416
     * {@inheritdoc}
417
     */
418 3
    public function hasTypeDescriptor($typeName, $moduleName = null)
419
    {
420 3
        Assert::nullOrString($moduleName, 'The module name must be a string or null. Got: %s');
421
422 2
        $this->assertModulesLoaded();
423
424 2
        return $this->typeDescriptors->contains($typeName, $moduleName);
425
    }
426
427
    /**
428
     * {@inheritdoc}
429
     */
430 3
    public function hasTypeDescriptors(Expression $expr = null)
431
    {
432 3
        $this->assertModulesLoaded();
433
434 3
        if (!$expr) {
435 2
            return !$this->typeDescriptors->isEmpty();
436
        }
437
438 2
        foreach ($this->typeDescriptors->toArray() as $typeName => $descriptorsByModule) {
439 2
            foreach ($descriptorsByModule as $typeDescriptor) {
440 2
                if ($expr->evaluate($typeDescriptor)) {
441 2
                    return true;
442
                }
443
            }
444
        }
445
446 2
        return false;
447
    }
448
449
    /**
450
     * {@inheritdoc}
451
     */
452 12
    public function addRootBindingDescriptor(BindingDescriptor $bindingDescriptor, $flags = 0)
453
    {
454 12
        $this->assertModulesLoaded();
455
456 12
        $typeName = $bindingDescriptor->getTypeName();
457 12
        $typeExists = $this->typeDescriptors->contains($typeName);
458
459 12
        if (!($flags & self::IGNORE_TYPE_NOT_FOUND) && !$typeExists) {
460 2
            throw NoSuchTypeException::forTypeName($typeName);
461
        }
462
463 10
        if (!($flags & self::IGNORE_TYPE_NOT_ENABLED) && $typeExists && !$this->typeDescriptors->getFirst($typeName)->isEnabled()) {
464 2
            throw TypeNotEnabledException::forTypeName($typeName);
465
        }
466
467 8
        $binding = $bindingDescriptor->getBinding();
468 8
        $exists = $this->bindingDescriptors->contains($binding);
469 8
        $existsInNonRoot = $exists
470 3
            ? !$this->bindingDescriptors->contains($binding, $this->rootModule->getName())
471 8
            : false;
472
473
        // We can only override bindings in the root module
474 8
        if ($existsInNonRoot || ($exists && !($flags & self::OVERRIDE))) {
475 2
            throw DuplicateBindingException::forUuid($binding);
476
        }
477
478 6
        $tx = new Transaction();
479
480
        try {
481 6
            $syncOp = $this->syncBinding($binding);
482 6
            $syncOp->takeSnapshot();
483
484 6
            $tx->execute($this->loadBindingDescriptor($bindingDescriptor, $this->rootModule));
485
486 6
            $this->assertBindingValid($bindingDescriptor);
487
488 5
            $tx->execute($this->addBindingDescriptorToModuleFile($bindingDescriptor));
489 5
            $tx->execute($syncOp);
490
491 5
            $this->saveRootModuleFile();
492
493 3
            $tx->commit();
494 3
        } catch (Exception $e) {
495 3
            $tx->rollback();
496
497
            throw $e;
498
        }
499 3
    }
500
501
    /**
502
     * {@inheritdoc}
503
     */
504 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...
505
    {
506 3
        $this->assertModulesLoaded();
507
508 3
        $tx = new Transaction();
509
510
        try {
511 3
            foreach ($this->getRootBindingDescriptors() as $bindingDescriptor) {
512
                if ($expr->evaluate($bindingDescriptor)) {
513
                    $syncOp = $this->syncBinding($bindingDescriptor->getUuid());
0 ignored issues
show
Bug introduced by
The method getUuid() does not seem to exist on object<Puli\Manager\Api\...very\BindingDescriptor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
514
                    $syncOp->takeSnapshot();
515
516
                    $tx->execute($this->unloadBindingDescriptor($bindingDescriptor));
517
                    $tx->execute($syncOp);
518
                    $tx->execute($this->removeBindingDescriptorFromModuleFile($bindingDescriptor->getUuid()));
0 ignored issues
show
Bug introduced by
The method getUuid() does not seem to exist on object<Puli\Manager\Api\...very\BindingDescriptor>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
519
                }
520
            }
521
522
            $this->saveRootModuleFile();
523
524
            $tx->commit();
525 3
        } catch (Exception $e) {
526
            $tx->rollback();
527
528
            throw $e;
529
        }
530
    }
531
532
    /**
533
     * {@inheritdoc}
534
     */
535 1
    public function clearRootBindingDescriptors()
536
    {
537 1
        $this->removeRootBindingDescriptors(Expr::true());
538
    }
539
540
    /**
541
     * {@inheritdoc}
542
     */
543 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...
544
    {
545 3
        $this->assertModulesLoaded();
546
547 3
        $bindings = array();
548
549 3
        foreach ($this->bindingDescriptors->toArray() as $binding) {
550 3
            if ($binding->getContainingModule() instanceof RootModule) {
0 ignored issues
show
Bug introduced by
The method getContainingModule cannot be called on $binding (of type array<integer,object<Pul...ery\BindingDescriptor>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
551
                $bindings[] = $binding;
552
            }
553
        }
554
555
        return $bindings;
556
    }
557
558
    /**
559
     * {@inheritdoc}
560
     */
561 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...
562
    {
563 1
        $expr = Expr::method('getContainingModule', Expr::same($this->rootModule))
564 1
            ->andX($expr);
565
566 1
        return $this->findBindingDescriptors($expr);
567
    }
568
569
    /**
570
     * {@inheritdoc}
571
     */
572 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...
573
    {
574 1
        $expr2 = Expr::method('getContainingModule', Expr::same($this->rootModule));
575
576 1
        if ($expr) {
577
            $expr2 = $expr2->andX($expr);
578
        }
579
580 1
        return $this->hasBindingDescriptors($expr2);
581
    }
582
583
    /**
584
     * {@inheritdoc}
585
     */
586 1
    public function getBindingDescriptors()
587
    {
588 1
        $this->assertModulesLoaded();
589
590 1
        return array_values($this->bindingDescriptors->toArray());
591
    }
592
593
    /**
594
     * {@inheritdoc}
595
     */
596 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...
597
    {
598 2
        $this->assertModulesLoaded();
599
600 2
        $descriptors = array();
601
602 2
        foreach ($this->bindingDescriptors->toArray() as $descriptor) {
603 2
            if ($expr->evaluate($descriptor)) {
604 2
                $descriptors[] = $descriptor;
605
            }
606
        }
607
608 2
        return $descriptors;
609
    }
610
611
    /**
612
     * {@inheritdoc}
613
     */
614 3 View Code Duplication
    public function hasBindingDescriptors(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...
615
    {
616 3
        $this->assertModulesLoaded();
617
618 3
        if (!$expr) {
619 2
            return !$this->bindingDescriptors->isEmpty();
620
        }
621
622 2
        foreach ($this->bindingDescriptors->toArray() as $bindingDescriptor) {
623 2
            if ($expr->evaluate($bindingDescriptor)) {
624 2
                return true;
625
            }
626
        }
627
628 2
        return false;
629
    }
630
631
    /**
632
     * {@inheritdoc}
633
     */
634 7
    public function buildDiscovery()
635
    {
636 7
        $this->assertModulesLoaded();
637 7
        $this->emitWarningForDuplicateTypes();
638 7
        $this->emitWarningForInvalidBindings();
639
640 3
        if ($this->discovery->hasBindings() || $this->discovery->hasBindingTypes()) {
641 2
            throw new DiscoveryNotEmptyException('The discovery is not empty.');
642
        }
643
644 1
        $tx = new Transaction();
645
646
        try {
647 1
            foreach ($this->typeDescriptors->toArray() as $typeName => $descriptorsByModule) {
648 1
                foreach ($descriptorsByModule as $typeDescriptor) {
649 1
                    if ($typeDescriptor->isEnabled()) {
650 1
                        $tx->execute($this->addBindingType($typeDescriptor));
651
                    }
652
                }
653
            }
654
655 1
            foreach ($this->bindingDescriptors->toArray() as $bindingDescriptor) {
656
                if ($bindingDescriptor->isEnabled()) {
0 ignored issues
show
Bug introduced by
The method isEnabled cannot be called on $bindingDescriptor (of type array<integer,object<Pul...ery\BindingDescriptor>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
657
                    $tx->execute($this->addBinding($bindingDescriptor));
0 ignored issues
show
Documentation introduced by
$bindingDescriptor is of type array<integer,object<Pul...ery\BindingDescriptor>>, but the function expects a object<Puli\Manager\Api\...very\BindingDescriptor>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
658
                }
659
            }
660
661 1
            $tx->commit();
662
        } catch (Exception $e) {
663
            $tx->rollback();
664
665
            throw $e;
666
        }
667 1
    }
668
669
    /**
670
     * {@inheritdoc}
671
     */
672 1
    public function clearDiscovery()
673
    {
674 1
        $this->discovery->removeBindingTypes();
675 1
    }
676
677 61
    private function assertModulesLoaded()
678
    {
679 61
        if (!$this->typeDescriptors) {
680 61
            $this->loadModules();
681
        }
682 61
    }
683
684 6
    private function assertBindingValid(BindingDescriptor $bindingDescriptor)
685
    {
686 6
        if ($bindingDescriptor->isTypeNotFound() || $bindingDescriptor->isTypeNotEnabled()) {
687 2
            return;
688
        }
689
690 4
        foreach ($bindingDescriptor->getLoadErrors() as $exception) {
691 1
            throw $exception;
692
        }
693 3
    }
694
695 61
    private function loadModules()
696
    {
697 61
        $this->typeDescriptors = new BindingTypeDescriptorCollection();
698 61
        $this->bindingDescriptors = new BindingDescriptorCollection();
699
700
        // First load all the types
701 61 View Code Duplication
        foreach ($this->modules as $module) {
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...
702 61
            if (null === $module->getModuleFile()) {
703 1
                continue;
704
            }
705
706 61
            foreach ($module->getModuleFile()->getTypeDescriptors() as $typeDescriptor) {
707 61
                $this->loadTypeDescriptor($typeDescriptor, $module)->execute();
708
            }
709
        }
710
711
        // Then the bindings for the loaded types
712 61
        foreach ($this->modules as $module) {
713 61
            if (null === $module->getModuleFile()) {
714 1
                continue;
715
            }
716
717 61
            foreach ($module->getModuleFile()->getBindingDescriptors() as $bindingDescriptor) {
718 61
                $this->loadBindingDescriptor($bindingDescriptor, $module)->execute();
719
            }
720
        }
721 61
    }
722
723 12
    private function emitWarningForDuplicateTypes()
724
    {
725 12
        foreach ($this->typeDescriptors->getTypeNames() as $typeName) {
726 7
            $moduleNames = $this->typeDescriptors->getModuleNames($typeName);
727
728 7
            if (count($moduleNames) > 1) {
729 1
                $lastModuleName = array_pop($moduleNames);
730
731 1
                $this->logger->warning(sprintf(
732
                    'The modules "%s" and "%s" contain type definitions for '.
733 1
                    'the same type "%s". The type has been disabled.',
734 7
                    implode('", "', $moduleNames),
735
                    $lastModuleName,
736
                    $typeName
737
                ));
738
            }
739
        }
740 12
    }
741
742 7
    private function emitWarningForInvalidBindings()
743
    {
744 7
        foreach ($this->bindingDescriptors->toArray() as $binding) {
745 4
            foreach ($binding->getLoadErrors() as $exception) {
0 ignored issues
show
Bug introduced by
The method getLoadErrors cannot be called on $binding (of type array<integer,object<Pul...ery\BindingDescriptor>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
746
                $this->logger->warning(sprintf(
747
                    'The binding "%s" in module "%s" is invalid: %s',
748
                    $binding->getUuid()->toString(),
0 ignored issues
show
Bug introduced by
The method getUuid cannot be called on $binding (of type array<integer,object<Pul...ery\BindingDescriptor>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
749
                    $binding->getContainingModule()->getName(),
0 ignored issues
show
Bug introduced by
The method getContainingModule cannot be called on $binding (of type array<integer,object<Pul...ery\BindingDescriptor>>).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
750
                    $exception->getMessage()
751
                ));
752
            }
753
        }
754 3
    }
755
756 5
    private function saveRootModuleFile()
757
    {
758 5
        $this->jsonStorage->saveRootModuleFile($this->rootModuleFile);
759 3
    }
760
761
    private function addTypeDescriptorToModuleFile(BindingTypeDescriptor $typeDescriptor)
762
    {
763
        return new AddTypeDescriptorToModuleFile($typeDescriptor, $this->rootModuleFile);
764
    }
765
766 13
    private function removeTypeDescriptorFromModuleFile($typeName)
767
    {
768 13
        return new RemoveTypeDescriptorFromModuleFile($typeName, $this->rootModuleFile);
769
    }
770
771 48 View Code Duplication
    private function loadTypeDescriptor(BindingTypeDescriptor $typeDescriptor, Module $module)
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...
772
    {
773 48
        $typeName = $typeDescriptor->getTypeName();
774
775 48
        return new InterceptedOperation(
776 48
            new LoadTypeDescriptor($typeDescriptor, $module, $this->typeDescriptors),
777
            array(
778 48
                new UpdateDuplicateMarksForTypeName($typeName, $this->typeDescriptors),
779 48
                new ReloadBindingDescriptorsByTypeName($typeName, $this->bindingDescriptors, $this->typeDescriptors),
780
            )
781
        );
782
    }
783
784 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...
785
    {
786
        $typeName = $typeDescriptor->getTypeName();
787
788
        return new InterceptedOperation(
789
            new UnloadTypeDescriptor($typeDescriptor, $this->typeDescriptors),
790
            array(
791
                new UpdateDuplicateMarksForTypeName($typeName, $this->typeDescriptors),
792
                new ReloadBindingDescriptorsByTypeName($typeName, $this->bindingDescriptors, $this->typeDescriptors),
793
            )
794
        );
795
    }
796
797
    private function addBindingType(BindingTypeDescriptor $typeDescriptor)
798
    {
799
        return new AddBindingType($typeDescriptor, $this->discovery);
800
    }
801
802
    private function syncTypeName($typeName)
803
    {
804
        return new SyncTypeName($typeName, $this->discovery, $this->typeDescriptors);
805
    }
806
807 5
    private function addBindingDescriptorToModuleFile(BindingDescriptor $bindingDescriptor)
808
    {
809 5
        return new AddBindingDescriptorToModuleFile($bindingDescriptor, $this->rootModuleFile);
810
    }
811
812
    private function removeBindingDescriptorFromModuleFile(Uuid $uuid)
813
    {
814
        return new RemoveBindingDescriptorFromModuleFile($uuid, $this->rootModuleFile);
815
    }
816
817 24
    private function loadBindingDescriptor(BindingDescriptor $bindingDescriptor, Module $module)
818
    {
819 24
        return new LoadBindingDescriptor($bindingDescriptor, $module, $this->bindingDescriptors, $this->typeDescriptors);
820
    }
821
822
    private function unloadBindingDescriptor(BindingDescriptor $bindingDescriptor)
823
    {
824
        return new UnloadBindingDescriptor($bindingDescriptor, $this->bindingDescriptors);
825
    }
826
827
    private function addBinding(BindingDescriptor $bindingDescriptor)
828
    {
829
        return new AddBinding($bindingDescriptor, $this->discovery);
830
    }
831
832 6
    private function syncBinding(Binding $binding)
833
    {
834 6
        return new SyncBinding($binding, $this->discovery, $this->bindingDescriptors);
835
    }
836
}
837