Completed
Pull Request — master (#71)
by
unknown
01:43
created

ModelFinder   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 10
dl 0
loc 97
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A setFocus() 0 5 1
A getModelsInDirectory() 0 24 5
A getFullyQualifiedClassNameFromFile() 0 30 2
A hasRelationWithFocus() 0 15 4
1
<?php
2
3
namespace BeyondCode\ErdGenerator;
4
5
use Illuminate\Database\Eloquent\Model as EloquentModel;
6
use Illuminate\Filesystem\Filesystem;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Str;
9
use PhpParser\Node\Stmt\Class_;
10
use PhpParser\Node\Stmt\Namespace_;
11
use PhpParser\NodeTraverser;
12
use PhpParser\NodeVisitor\NameResolver;
13
use PhpParser\ParserFactory;
14
use ReflectionClass;
15
16
class ModelFinder
17
{
18
19
    /** @var Filesystem */
20
    protected $filesystem;
21
22
    /** @var RelationFinder */
23
    protected $relationFinder;
24
25
    /** @var array $focus */
26
    protected $focus = [];
27
28
    public function __construct(Filesystem $filesystem, RelationFinder $relationFinder)
29
    {
30
        $this->filesystem = $filesystem;
31
32
        $this->relationFinder = $relationFinder;
33
    }
34
35
    public function setFocus(array $focus = []): self
36
    {
37
        $this->focus = $focus;
38
        return $this;
39
    }
40
41
    public function getModelsInDirectory(string $directory): Collection
42
    {
43
        $files = config('erd-generator.recursive') ?
44
            $this->filesystem->allFiles($directory) :
45
            $this->filesystem->files($directory);
46
47
        $ignoreModels = array_filter(config('erd-generator.ignore', []), 'is_string');
48
49
        return Collection::make($files)->filter(function ($path) {
50
            return Str::endsWith($path, '.php');
51
        })->map(function ($path) {
52
            return $this->getFullyQualifiedClassNameFromFile($path);
53
        })->filter(function (string $className) {
54
            return !empty($className)
55
                && is_subclass_of($className, EloquentModel::class)
56
                && ! (new ReflectionClass($className))->isAbstract();
57
        })->diff($ignoreModels)
58
            ->filter(function (string $className) {
59
                if (0 === count($this->focus)) {
60
                    return true;
61
                }
62
                return $this->hasRelationWithFocus($className);
63
            })->sort();
64
    }
65
66
    protected function getFullyQualifiedClassNameFromFile(string $path): string
67
    {
68
        $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
69
70
        $traverser = new NodeTraverser();
71
        $traverser->addVisitor(new NameResolver());
72
73
        $code = file_get_contents($path);
74
75
        $statements = $parser->parse($code);
76
        $statements = $traverser->traverse($statements);
0 ignored issues
show
Bug introduced by
It seems like $statements can also be of type null; however, PhpParser\NodeTraverser::traverse() does only seem to accept array<integer,object<PhpParser\Node>>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
77
78
        // get the first namespace declaration in the file
79
        $root_statement = collect($statements)->first(function ($statement) {
80
            return $statement instanceof Namespace_;
81
        });
82
83
        if (! $root_statement) {
84
            return '';
85
        }
86
87
        return collect($root_statement->stmts)
88
                ->filter(function ($statement) {
89
                    return $statement instanceof Class_;
90
                })
91
                ->map(function (Class_ $statement) {
92
                    return $statement->namespacedName->toString();
93
                })
94
                ->first() ?? '';
95
    }
96
97
    protected function hasRelationWithFocus(string $className): bool
98
    {
99
        if (in_array($className, $this->focus)) {
100
            return true;
101
        }
102
103
        $relations = $this->relationFinder->getModelRelations($className);
104
        /** @var ModelRelation $relation */
105
        foreach ($relations as $relation) {
106
            if (in_array($relation->getModel(), $this->focus)) {
107
                return true;
108
            }
109
        }
110
        return false;
111
    }
112
}
113