DetectController::checkClassType()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 4
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace CSlant\LaraGenAdv\Http\Controllers\Detect;
4
5
use Illuminate\Routing\Controller;
6
use RecursiveDirectoryIterator;
7
use RecursiveIteratorIterator;
8
use ReflectionClass;
9
use ReflectionMethod;
10
11
class DetectController extends Controller
12
{
13
    /**
14
     * Get the type of all classes in the app folder.
15
     *
16
     * @return array[]
17
     */
18
    public function detect(): array
19
    {
20
        $recursiveDirectoryIterator = new RecursiveDirectoryIterator(app_path());
21
        $files = new RecursiveIteratorIterator($recursiveDirectoryIterator);
22
        $type = [];
23
24
        foreach ($files as $file) {
25
            if (!$file->isFile() || $file->getExtension() !== 'php') {
26
                continue;
27
            }
28
29
            $class = $this->getClassFromFile($file);
30
            if ($class !== null) {
31
                $type[] = $this->getClassType($class);
32
            }
33
        }
34
35
        return $type;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $type returns an array which contains values of type string which are incompatible with the documented value type array.
Loading history...
36
    }
37
38
    /**
39
     * @param $file
40
     * @return ReflectionClass|null
41
     */
42
    public function getClassFromFile($file): ?ReflectionClass
43
    {
44
        $content = file_get_contents($file);
45
        $matches = [];
46
47
        // Match namespace and class name
48
        preg_match('/namespace\s+(.*?);.*?class\s+(\w+)/s', $content, $matches);
49
        if (!isset($matches[1]) || !isset($matches[2])) {
50
            return null;
51
        }
52
53
        $namespace = $matches[1];
54
        $class = $namespace.'\\'.$matches[2];
55
56
        return class_exists($class) ? new ReflectionClass($class) : null;
57
    }
58
59
    /**
60
     * Get the type of the given class.
61
     *
62
     * @param  ReflectionClass  $class
63
     * @return string
64
     */
65
    protected function getClassType(ReflectionClass $class): string
66
    {
67
        $type = 'other';
68
69
        switch (true) {
70
            case $this->isRepositoryClass($class):
71
                $type = 'repository';
72
73
                break;
74
            case $this->isServiceClass($class):
75
                $type = 'service';
76
77
                break;
78
            case $this->isControllerClass($class):
79
                $type = 'controller';
80
81
                break;
82
            case $this->isActionClass($class):
83
                $type = 'action';
84
85
                break;
86
        }
87
88
        return $type;
89
    }
90
91
    /**
92
     * Check if the class is a repository class
93
     * A repository class must have a name ending with "Repository" or "EloquentRepository"
94
     * and implement the CRUD methods
95
     * and have a dependency on a model.
96
     *
97
     * @param  ReflectionClass  $class
98
     * @return bool
99
     */
100
    public function isRepositoryClass(ReflectionClass $class): bool
101
    {
102
        return $this->checkClassType($class, 'repository');
103
    }
104
105
    /**
106
     * Check if the class is a class of the given type
107
     * A class of the given type must have a name ending with the given type or "Eloquent" + the given type.
108
     *
109
     * @param  ReflectionClass  $class
110
     * @param $type
111
     * @return bool
112
     */
113
    protected function checkClassType(ReflectionClass $class, $type): bool
114
    {
115
        $type = ucfirst($type);
116
117
        return preg_match('/'.$type.'$/', $class->getName()) === 1
118
            || preg_match('/Eloquent'.$type.'$/', $class->getName()) === 1
119
            && $this->implementsCrudMethods($class)
120
            && $this->dependsOnModels($class);
121
    }
122
123
    /**
124
     * Check if the class implements the CRUD methods.
125
     *
126
     * @param  ReflectionClass  $class
127
     * @return bool
128
     */
129
    protected function implementsCrudMethods(ReflectionClass $class): bool
130
    {
131
        $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
132
        $crudMethods = [
133
            'create',
134
            'read',
135
            'update',
136
            'delete',
137
        ];
138
139
        foreach ($methods as $method) {
140
            if (in_array($method->name, $crudMethods)) {
141
                return true;
142
            }
143
        }
144
145
        return false;
146
    }
147
148
    /**
149
     * @param  ReflectionClass  $class
150
     * @return bool
151
     */
152
    private function dependsOnModels(ReflectionClass $class): bool
153
    {
154
        $dependencies = $class->getConstructor()->getParameters();
155
        foreach ($dependencies as $dependency) {
156
            if (preg_match('/Model$/', $dependency->getClass()->getName()) === 1) {
157
                return true;
158
            }
159
        }
160
161
        return false;
162
    }
163
164
    /**
165
     * Check if the class is a service class
166
     * A service class must have a name ending with "Service" or "EloquentService".
167
     *
168
     * @param  ReflectionClass  $class
169
     * @return bool
170
     */
171
    public function isServiceClass(ReflectionClass $class): bool
172
    {
173
        return $this->checkClassType($class, 'service');
174
    }
175
176
    /**
177
     * Check if the class is a controller class
178
     * A controller class must have a name ending with "Controller" or "EloquentController"
179
     * and implement the CRUD methods
180
     * and have a dependency on a model.
181
     *
182
     * @param  ReflectionClass  $class
183
     * @return bool
184
     */
185
    public function isControllerClass(ReflectionClass $class): bool
186
    {
187
        return $this->checkClassType($class, 'controller');
188
    }
189
190
    /**
191
     * Check if the class is an action class
192
     * An action class must have a name ending with "Action" or "EloquentAction".
193
     *
194
     * @param  ReflectionClass  $class
195
     * @return bool
196
     */
197
    public function isActionClass(ReflectionClass $class): bool
198
    {
199
        return $this->checkClassType($class, 'action');
200
    }
201
}
202