Completed
Pull Request — master (#7)
by
unknown
14:27
created

ReverseDependencies::execute()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 35
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 20
c 1
b 0
f 0
nc 6
nop 2
dl 0
loc 35
rs 8.9777
1
<?php
2
3
declare(strict_types=1);
4
5
namespace drupol\ComposerPackages\Commands;
6
7
use Composer\Command\BaseCommand as Command;
8
use ComposerPackages\Dependencies;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, drupol\ComposerPackages\Commands\Dependencies. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
use ComposerPackages\Packages;
10
use PBergman\Console\Helper\TreeHelper;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Console\Question\Question;
14
15
class ReverseDependencies extends Command
16
{
17
    protected $color;
18
19
    protected function configure()
20
    {
21
        $this->setName('reverse-dependencies');
22
        $this->setDescription('Retrace the tree of dependencies originally asking for a package.');
23
    }
24
25
    protected function execute(InputInterface $input, OutputInterface $output)
26
    {
27
        $helper = $this->getHelper('question');
28
        $tree = new TreeHelper();
29
30
        $packages = array_keys(iterator_to_array(Packages::packages()));
31
        $question = new Question('<question>Please type the package name:</question>' . \PHP_EOL);
32
33
        if (method_exists($question, 'setAutocompleterCallback')) {
34
            $callback = static function (string $userInput) use ($packages): array {
35
                return array_filter($packages, static function (string $packageName) use ($userInput) {
36
                    return false !== mb_stripos($packageName, $userInput);
37
                });
38
            };
39
            $question->setAutocompleterCallback($callback);
40
        } else {
41
            $question->setAutocompleterValues($packages);
42
        }
43
44
        $search = $helper->ask($input, $output, $question) ?? '';
45
46
        $packages = Packages::packages();
47
        $parents = $this->lookForTree($packages, $search);
48
49
        foreach ($parents as $children) {
50
            $shouldBeDeleted = array_filter($parents, static function ($value) use ($children) {
51
                return \in_array($children, $value, true);
52
            });
53
54
            if (!$shouldBeDeleted && \is_array($children) && !empty($children)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $shouldBeDeleted of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
55
                $tree->addArray($children);
56
            }
57
        }
58
59
        $tree->printTree($output);
60
    }
61
62
    private function countLevels($filtered): int
63
    {
64
        return \count($filtered, \COUNT_RECURSIVE);
65
    }
66
67
    private function lookForChildren($packageName, $search): array
68
    {
69
        $dependencies = Dependencies::get($packageName);
70
71
        foreach ($dependencies as $dependencyName) {
72
            if ($dependencyName === $search) {
73
                return [$packageName => [$dependencyName]];
74
            }
75
76
            $deeperLook = $this->lookForChildren($dependencyName, $search);
77
78
            if (!empty($deeperLook)) {
79
                return [$packageName => $deeperLook];
80
            }
81
        }
82
83
        return [];
84
    }
85
86
    private function lookForTree($packages, $search): array
87
    {
88
        $children = [];
89
90
        foreach ($packages as $package) {
91
            $children[] = $this->lookForChildren($package['name'], $search);
92
        }
93
94
        $filtered = array_filter($children);
95
        $sortCriteria = array_map([$this, 'countLevels'], $filtered);
96
        array_multisort($sortCriteria, \SORT_ASC, $filtered);
97
98
        return $filtered;
99
    }
100
}
101