Passed
Pull Request — master (#136)
by
unknown
19:08 queued 11:06
created

SearchableListFactory::isSearchableModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Matchish\ScoutElasticSearch\Searchable;
6
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Str;
9
use function in_array;
10
use Laravel\Scout\Searchable;
11
use Symfony\Component\Finder\Finder;
12
13
final class SearchableListFactory
14
{
15
    /**
16
     * @var array
17
     */
18
    private static $declaredClasses;
19
    /**
20
     * @var string
21
     */
22
    private $namespace;
23
    /**
24
     * @var string
25
     */
26
    private $appPath;
27
28
    /**
29
     * @param string $namespace
30
     */
31 12
    public function __construct(string $namespace, string $appPath)
32
    {
33 12
        $this->namespace = $namespace;
34 12
        $this->appPath = $appPath;
35 12
    }
36
37
    /**
38
     * Get a list of searchable models.
39
     *
40
     * @return string[]
41
     */
42 12
    private function find(): array
43
    {
44 12
        $appNamespace = $this->namespace;
45
46 12
        return array_values(array_filter($this->getProjectClasses(), function (string $class) use ($appNamespace) {
47 12
            return Str::startsWith($class, $appNamespace) && $this->isSearchableModel($class);
48 12
        }));
49
    }
50
51
    /**
52
     * @param  string $class
53
     *
54
     * @return bool
55
     */
56 12
    private function isSearchableModel($class): bool
57
    {
58 12
        return in_array(Searchable::class, class_uses_recursive($class), true);
59
    }
60
61
    /**
62
     * @return array
63
     */
64 12
    private function getProjectClasses(): array
65
    {
66 12
        if (self::$declaredClasses === null) {
0 ignored issues
show
introduced by
The condition self::declaredClasses === null is always false.
Loading history...
67
68 1
            self::$declaredClasses = [];
69
70 1
            $configFiles = Finder::create()->files()->name('*.php')->in($this->appPath);
71
72 1
            foreach ($configFiles->files() as $file) {
73 1
                if ($className = $this->classNameFromFileContents($file->getPathname())) {
74 1
                    self::$declaredClasses[] = $className;
75
                }
76
            }
77
        }
78
79 12
        return self::$declaredClasses;
80
    }
81
82
    /**
83
     * https://stackoverflow.com/a/7153391/1359273
84
     *
85
     * @param string $path
86
     * @return string|null
87
     */
88 1
    private function classNameFromFileContents($path)
89
    {
90 1
        $fp = fopen($path, 'r');
91
92 1
        if (false === $fp) {
93
            return null;
94
        }
95
96 1
        $class = $namespace = $buffer = '';
97 1
        $i = 0;
98 1
        while (!$class) {
99 1
            if (feof($fp)) break;
100
101 1
            $buffer .= fread($fp, 512);
102 1
            $tokens = token_get_all($buffer);
103
104 1
            if (strpos($buffer, '{') === false) continue;
105
106 1
            for (;$i<count($tokens);$i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
107 1
                if ($tokens[$i][0] === T_NAMESPACE) {
108 1
                    for ($j=$i+1;$j<count($tokens); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
109 1
                        if ($tokens[$j][0] === T_STRING) {
110 1
                            $namespace .= $tokens[$j][1];
111 1
                        } else if ($tokens[$j] === '{' || $tokens[$j] === ';') {
112 1
                            break;
113
                        }
114
                    }
115
                }
116
117 1
                if ($tokens[$i][0] === T_CLASS) {
118 1
                    for ($j=$i+1;$j<count($tokens);$j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
119 1
                        if ($tokens[$j] === '{') {
120 1
                            $class = $tokens[$i+2][1];
121
                        }
122
                    }
123
                }
124
            }
125
        }
126
127 1
        if (! $class) {
128
            return null;
129
        }
130
131 1
        return $namespace ? "{$namespace}\\{$class}" : $class;
132
    }
133
134
    /**
135
     * @return Collection
136
     */
137 12
    public function make(): Collection
138
    {
139 12
        return new Collection($this->find());
140
    }
141
}
142