ModuleManager::getArraySidebarMenu()   A
last analyzed

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
        if (!is_dir($path)) {
120
            return [];
121
        }
122
        $directories = scandir($path);
123
        foreach ($directories as $directory) {
124
            if ($directory === '.' || $directory === '..' || !is_dir($path . '/' . $directory)) {
125
                continue;
126
            }
127
            $result = array_merge($result, self::getControllers($namespace . '\\' . $directory, $path, $directory));
128
        }
129
        return $result;
130
    }
131
132
    /**
133
     * Returns an array with the specified $path and $directory controllers.
134
     * The array contains the name, namespace and path of those controllers.
135
     *
136
     * @param string $namespace
137
     * @param string $path
138
     * @param string $directory
139
     * @return array
140
     */
141
    private static function getControllers(string $namespace, string $path, string $directory): array
142
    {
143
        $result = [];
144
        $files = scandir($path . '/' . $directory . '/Controller');
145
        foreach ($files as $file) {
146
            if ($file === '.' || $file === '..' || !str_ends_with($file, '.php')) {
147
                continue;
148
            }
149
            $name = substr($file, 0, -4);
150
            $result[] = [
151
                'name' => $name,
152
                'namespace' => $namespace . '\\Controller\\' . $name,
153
                'path' => $path . '/' . $directory . '/Controller/' . $file,
154
            ];
155
        }
156
        return $result;
157
    }
158
159
    /**
160
     * Regenerate the top menu.
161
     * This is temporary.
162
     * The menus to be displayed will be cached by user or role.
163
     *
164
     * @return array|mixed
165
     */
166
    public static function getArrayMenu()
167
    {
168
        return self::buildMultiLevelMenu(self::getMenu());
169
    }
170
171
    /**
172
     * Converts an array where the index is a menu path with the
173
     * hierarchy separated by pipelines, to a nested array.
174
     *
175
     * @param $menuOptions
176
     * @return array|mixed
177
     */
178
    private static function buildMultiLevelMenu($menuOptions)
179
    {
180
        $result = [];
181
182
        foreach ($menuOptions as $option => $value) {
183
            $levels = explode('|', $option);
184
            $numberOfLevels = count($levels);
185
            $currentLevel = &$result;
186
187
            foreach ($levels as $level) {
188
                $numberOfLevels--;
189
                if ($numberOfLevels === 0) {
190
                    $currentLevel[$level] = $value;
191
                    continue;
192
                }
193
                if (!isset($currentLevel[$level])) {
194
                    $currentLevel[$level] = [];
195
                }
196
                $currentLevel = &$currentLevel[$level];
197
            }
198
        }
199
200
        return $result;
201
    }
202
203
    /**
204
     * Obtains an array with all the menu options where the index is the
205
     * option and the value is the url of the controller to be executed.
206
     *
207
     * Example: ['admin|auth'] = "index.php?module=Admin&controller=Auth"
208
     *
209
     * @return array
210
     */
211
    private static function getMenu()
212
    {
213
        $result = [];
214
        $menuOptions = self::iterateFunction('getMenu');
215
        foreach ($menuOptions as $namespace => $option) {
216
            if ($option === false) {
217
                continue;
218
            }
219
            $url = self::getUrl($namespace);
220
            if ($url === false) {
221
                continue;
222
            }
223
            $result[$option] = $url;
224
        }
225
        return $result;
226
    }
227
228
    /**
229
     * Returns the URL that runs the controller with the given namespace.
230
     *
231
     * The namespace can be:
232
     * - CoreModules\\<module>\\Controller\\<controller>Controller
233
     * - Modules\\<module>\\Controller\\<controller>Controller
234
     *
235
     * @param $namespace
236
     * @return false|string
237
     */
238
    public static function getUrl($namespace)
239
    {
240
        $explode = explode('\\', $namespace);
241
        if (count($explode) < 4) {
242
            return false;
243
        }
244
        $name = $explode[3];
245
        if (!str_ends_with($name, 'Controller')) {
246
            return false;
247
        }
248
        $controller = substr($name, 0, -10);
249
        $module = $explode[1];
250
251
        return 'index.php?module=' . $module . '&controller=' . $controller;
252
    }
253
254
    /**
255
     * Regenerate the sidebar menu.
256
     * This is temporary.
257
     * The menus to be displayed will be cached by user or role.
258
     *
259
     * @return array|mixed
260
     */
261
    public static function getArraySidebarMenu()
262
    {
263
        return self::buildMultiLevelMenu(self::getSidebarMenu());
264
    }
265
266
    private static function getSidebarMenu()
267
    {
268
        $result = [];
269
        $menuOptions = self::iterateFunction('getSidebarMenu');
270
        foreach ($menuOptions as $namespace => $option) {
271
            if ($option === false) {
272
                continue;
273
            }
274
            $url = self::getUrl($namespace);
275
            if ($url === false) {
276
                continue;
277
            }
278
            foreach ($option['options'] as $value) {
279
                $result[$option['base']][$value['option']] = $url;
280
            }
281
        }
282
        return $result;
283
    }
284
}
285