Passed
Push — main ( c92e2e...1ca8df )
by Rafael
05:03
created

ModuleManager::getArraySidebarMenu()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 1
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
/* Copyright (C) 2024      Rafael San José      <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <https:\\www.gnu.org/licenses/>.
17
 */
18
19
namespace Alxarafe\Tools;
20
21
abstract class ModuleManager
22
{
23
    /**
24
     * Regenerate menus and actions.
25
     * This is temporary, everything will be cached by user or role.
26
     *
27
     * @return void
28
     */
29
    public static function regenerate()
30
    {
31
        dump([
32
            'acciones' => self::getActions(),
33
            'menú refactorizado' => self::getArrayMenu(),
34
            'menú lateral' => self::getArraySidebarMenu()
35
        ]);
36
    }
37
38
    /**
39
     * Returns an associative array with the actions of each controller,
40
     * the index being the namespace of the controller.
41
     *
42
     * @return array
43
     */
44
    private static function getActions()
45
    {
46
        $result = [];
47
        $menuOptions = self::iterateFunction('getActions');
48
        foreach ($menuOptions as $option) {
49
            if ($option === false) {
50
                continue;
51
            }
52
            $result = array_merge($result, $option);
53
        }
54
        return $result;
55
    }
56
57
    /**
58
     * Generates an array in which each element is the result of calling the
59
     * $function function for each controller in the application.
60
     *
61
     * @param $function
62
     * @return array
63
     */
64
    private static function iterateFunction($function)
65
    {
66
        $iterate = static::iterate();
67
        $result = [];
68
        foreach ($iterate as $module) {
69
            $namespace = $module['namespace'];
70
            if (!method_exists($namespace, $function)) {
71
                continue;
72
            }
73
            $result[$namespace] = $namespace::$function();
74
        }
75
        return $result;
76
    }
77
78
    private static function iterate()
79
    {
80
        $routes = self::routes();
81
        $data = [];
82
        foreach ($routes as $route) {
83
            $data = array_merge($data, self::getModuleControllers($route['namespace'], $route['path']));
84
        }
85
        return $data;
86
    }
87
88
    /**
89
     * Returns the paths that can contain modules.
90
     *
91
     * @return string[]
92
     */
93
    private static function routes(): array
94
    {
95
        $base_path = realpath(constant('BASE_PATH') . '/..');
96
        $result = [];
97
        $result[] = [
98
            'path' => $base_path . '/vendor/rsanjoseo/alxarafe/src/Modules',
99
            'namespace' => 'CoreModules',
100
        ];
101
        $result[] = [
102
            'path' => $base_path . '/Modules',
103
            'namespace' => 'Modules',
104
        ];
105
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result returns the type array<mixed,array<string,string>> which is incompatible with the documented return type string[].
Loading history...
106
    }
107
108
    /**
109
     * Returns an array with the module drivers included in the $path folder.
110
     * The array contains the name, namespace and path of those controllers.
111
     *
112
     * @param string $namespace
113
     * @param string $path
114
     * @return array
115
     */
116
    private static function getModuleControllers(string $namespace, string $path): array
117
    {
118
        $result = [];
119
        $directories = scandir($path);
120
        foreach ($directories as $directory) {
121
            if ($directory === '.' || $directory === '..' || !is_dir($path . '/' . $directory)) {
122
                continue;
123
            }
124
            $result = array_merge($result, self::getControllers($namespace . '\\' . $directory, $path, $directory));
125
        }
126
        return $result;
127
    }
128
129
    /**
130
     * Returns an array with the specified $path and $directory controllers.
131
     * The array contains the name, namespace and path of those controllers.
132
     *
133
     * @param string $namespace
134
     * @param string $path
135
     * @param string $directory
136
     * @return array
137
     */
138
    private static function getControllers(string $namespace, string $path, string $directory): array
139
    {
140
        $result = [];
141
        $files = scandir($path . '/' . $directory . '/Controller');
142
        foreach ($files as $file) {
143
            if ($file === '.' || $file === '..' || !str_ends_with($file, '.php')) {
144
                continue;
145
            }
146
            $name = substr($file, 0, -4);
147
            $result[] = [
148
                'name' => $name,
149
                'namespace' => $namespace . '\\Controller\\' . $name,
150
                'path' => $path . '/' . $directory . '/Controller/' . $file,
151
            ];
152
        }
153
        return $result;
154
    }
155
156
    /**
157
     * Regenerate the top menu.
158
     * This is temporary.
159
     * The menus to be displayed will be cached by user or role.
160
     *
161
     * @return array|mixed
162
     */
163
    public static function getArrayMenu()
164
    {
165
        return self::buildMultiLevelMenu(self::getMenu());
166
    }
167
168
    /**
169
     * Converts an array where the index is a menu path with the
170
     * hierarchy separated by pipelines, to a nested array.
171
     *
172
     * @param $menuOptions
173
     * @return array|mixed
174
     */
175
    private static function buildMultiLevelMenu($menuOptions)
176
    {
177
        $result = [];
178
179
        foreach ($menuOptions as $option => $value) {
180
            $levels = explode('|', $option);
181
            $numberOfLevels = count($levels);
182
            $currentLevel = &$result;
183
184
            foreach ($levels as $level) {
185
                $numberOfLevels--;
186
                if ($numberOfLevels === 0) {
187
                    $currentLevel[$level] = $value;
188
                    continue;
189
                }
190
                if (!isset($currentLevel[$level])) {
191
                    $currentLevel[$level] = [];
192
                }
193
                $currentLevel = &$currentLevel[$level];
194
            }
195
        }
196
197
        return $result;
198
    }
199
200
    /**
201
     * Obtains an array with all the menu options where the index is the
202
     * option and the value is the url of the controller to be executed.
203
     *
204
     * Example: ['admin|auth'] = "index.php?module=Admin&controller=Auth"
205
     *
206
     * @return array
207
     */
208
    private static function getMenu()
209
    {
210
        $result = [];
211
        $menuOptions = self::iterateFunction('getMenu');
212
        foreach ($menuOptions as $namespace => $option) {
213
            if ($option === false) {
214
                continue;
215
            }
216
            $url = self::getUrl($namespace);
217
            if ($url === false) {
218
                continue;
219
            }
220
            $result[$option] = $url;
221
        }
222
        return $result;
223
    }
224
225
    /**
226
     * Returns the URL that runs the controller with the given namespace.
227
     *
228
     * The namespace can be:
229
     * - CoreModules\\<module>\\Controller\\<controller>Controller
230
     * - Modules\\<module>\\Controller\\<controller>Controller
231
     *
232
     * @param $namespace
233
     * @return false|string
234
     */
235
    public static function getUrl($namespace)
236
    {
237
        $explode = explode('\\', $namespace);
238
        if (count($explode) < 4) {
239
            return false;
240
        }
241
        $name = $explode[3];
242
        if (!str_ends_with($name, 'Controller')) {
243
            return false;
244
        }
245
        $controller = substr($name, 0, -10);
246
        $module = $explode[1];
247
248
        return 'index.php?module=' . $module . '&controller=' . $controller;
249
    }
250
251
    /**
252
     * Regenerate the sidebar menu.
253
     * This is temporary.
254
     * The menus to be displayed will be cached by user or role.
255
     *
256
     * @return array|mixed
257
     */
258
    public static function getArraySidebarMenu()
259
    {
260
        return self::buildMultiLevelMenu(self::getSidebarMenu());
261
    }
262
263
    private static function getSidebarMenu()
264
    {
265
        $result = [];
266
        $menuOptions = self::iterateFunction('getSidebarMenu');
267
        foreach ($menuOptions as $namespace => $option) {
268
            if ($option === false) {
269
                continue;
270
            }
271
            $url = self::getUrl($namespace);
272
            if ($url === false) {
273
                continue;
274
            }
275
            foreach ($option['options'] as $value) {
276
                $result[$option['base']][$value['option']] = $url;
277
            }
278
        }
279
        return $result;
280
    }
281
}
282