Passed
Pull Request — master (#136)
by
unknown
07:11
created

SearchableListFactory::getProjectClasses()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 16
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 16
ccs 8
cts 8
cp 1
crap 4
rs 10
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
     * @param string $path
84
     * @return string|null
85
     * @link https://stackoverflow.com/a/7153391/1359273
86
     */
87 1
    private function classNameFromFileContents($path)
88
    {
89 1
        $fp = fopen($path, 'r');
90
91 1
        if (false === $fp) {
92
            return null;
93
        }
94
95 1
        $class = $namespace = $buffer = '';
96
97 1
        while (!$class) {
98 1
            if (feof($fp)) {
99
                break;
100
            }
101
102 1
            $buffer .= fread($fp, 512);
103 1
            $tokens = token_get_all($buffer);
104
105 1
            if (strpos($buffer, '{') === false) {
106
                continue;
107
            }
108
109 1
            for ($i = 0, $iMax = count($tokens); $i < $iMax; $i++) {
110
111 1
                if ($tokens[$i][0] === T_NAMESPACE) {
112 1
                    for ($j = $i+1, $jMax = count($tokens); $j < $jMax; $j++) {
113 1
                        if ($tokens[$j][0] === T_STRING) {
114 1
                            $namespace .= $tokens[$j][1];
115 1
                        } else if ($tokens[$j] === '{' || $tokens[$j] === ';') {
116 1
                            break;
117
                        }
118
                    }
119
                }
120
121 1
                if ($tokens[$i][0] === T_CLASS) {
122 1
                    for ($j = $i+1, $jMax = count($tokens); $j < $jMax; $j++) {
123 1
                        if ($tokens[$j] === '{') {
124 1
                            $class = $tokens[$i+2][1];
125
                        }
126
                    }
127
                }
128
            }
129
        }
130
131 1
        if (! $class) {
132
            return null;
133
        }
134
135 1
        return $namespace ? "{$namespace}\\{$class}" : $class;
136
    }
137
138
    /**
139
     * @return Collection
140
     */
141 12
    public function make(): Collection
142
    {
143 12
        return new Collection($this->find());
144
    }
145
}
146