Issues (1519)

system/Inji/Module.php (8 issues)

1
<?php
2
3
namespace Inji;
4
/**
5
 * Module
6
 *
7
 * @author Alexey Krupskiy <[email protected]>
8
 * @link http://inji.ru/
9
 * @copyright 2015 Alexey Krupskiy
10
 * @license https://github.com/injitools/cms-Inji/blob/master/LICENSE
11
 */
12
class Module {
13
14
    /**
15
     * Storage of cur requested module
16
     *
17
     * @var Module
18
     */
19
    public static $cur = null;
20
21
    /**
22
     * Module name
23
     *
24
     * @var string
25
     */
26
    public $name = '';
27
28
    /**
29
     * Module config
30
     *
31
     * @var array
32
     */
33
    public $config = [];
34
35
    /**
36
     * Module info
37
     *
38
     * @var array
39
     */
40
    public $info = [];
41
42
    /**
43
     * Requested module params
44
     *
45
     * @var array
46
     */
47
    public $params = [];
48
49
    /**
50
     * Module directory path
51
     *
52
     * @var string
53
     */
54
    public $path = '';
55
56
    /**
57
     * Module app
58
     *
59
     * @var App
60
     */
61
    public $app = null;
62
63
    /**
64
     * Parse cur module
65
     *
66
     * @param App $app
67
     */
68 1
    public function __construct($app) {
69 1
        $this->app = $app;
70 1
        if (!$this->name) {
71
            $this->name = get_class($this);
72
        }
73 1
        $this->path = Router::getLoadedClassPath(get_class($this));
74 1
        $this->info = $this->getInfo();
75 1
        $this->config = Config::module($this->name, !empty($this->info['systemConfig']));
0 ignored issues
show
! empty($this->info['systemConfig']) of type boolean is incompatible with the type Inji\App expected by parameter $app of Inji\Config::module(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

75
        $this->config = Config::module($this->name, /** @scrutinizer ignore-type */ !empty($this->info['systemConfig']));
Loading history...
76 1
        $that = $this;
77 1
        \Inji::$inst->listen('Config-change-module-' . $this->app->name . '-' . $this->name, $this->app->name . '-' . $this->name . 'config', function ($event) use ($that) {
0 ignored issues
show
function(...) { /* ... */ } of type callable is incompatible with the type string|array|closure expected by parameter $callback of Inji::listen(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

77
        \Inji::$inst->listen('Config-change-module-' . $this->app->name . '-' . $this->name, $this->app->name . '-' . $this->name . 'config', /** @scrutinizer ignore-type */ function ($event) use ($that) {
Loading history...
78
            $that->config = $event['eventObject'];
79
            return $event['eventObject'];
80 1
        });
81 1
    }
82
83
    /**
84
     * Get all posible directorys for module files
85
     *
86
     * @param string $moduleName
87
     * @return array
88
     */
89 1
    public static function getModulePaths($moduleName) {
90 1
        $moduleName = ucfirst($moduleName);
91 1
        $paths = [];
92 1
        if (App::$cur !== App::$primary) {
93
            $paths['primaryAppPath'] = App::$primary->path . '/modules/' . $moduleName;
94
        }
95 1
        $paths['curAppPath'] = App::$cur->path . '/modules/' . $moduleName;
96 1
        $paths['systemPath'] = INJI_SYSTEM_DIR . '/modules/' . $moduleName;
97 1
        return $paths;
98
    }
99
100
    /**
101
     * Return directory where places module file
102
     *
103
     * @param string $moduleName
104
     * @return string
105
     */
106
    public static function getModulePath($moduleName) {
107
        $moduleName = ucfirst($moduleName);
108
        $paths = Module::getModulePaths($moduleName);
109
        foreach ($paths as $path) {
110
            if (file_exists($path . '/' . $moduleName . '.php')) {
111
                return $path;
112
            }
113
        }
114
    }
115
116
    /**
117
     * Check module for installed
118
     *
119
     * @param string $moduleName
120
     * @param \Inji\App $app
121
     * @return boolean
122
     */
123
    public static function installed($moduleName, $app) {
124
        if (in_array($moduleName, self::getInstalled($app))) {
125
            return true;
126
        }
127
        return false;
128
    }
129
130
    /**
131
     * Get installed modules for app
132
     *
133
     * @param \Inji\App $app
134
     * @param App $primary
135
     * @return array
136
     */
137 1
    public static function getInstalled($app, $primary = false) {
138 1
        if (!$primary) {
139 1
            $primary = \Inji\App::$primary;
140
        }
141 1
        $system = !empty(\Inji::$config['modules']) ? \Inji::$config['modules'] : [];
142 1
        $primary = !empty($primary->config['modules']) ? $primary->config['modules'] : [];
143 1
        $actual = $app !== $primary && !empty($app->config['modules']) ? $app->config['modules'] : [];
144 1
        $modules = array_unique(array_merge($system, $primary, $actual));
145 1
        return $modules;
146
    }
147
148
    /**
149
     * Find module controllers
150
     *
151
     * @param string $moduleName
152
     * @return array
153
     */
154
    public static function getModuleControllers($moduleName) {
155
        $controllers = [];
156
        $moduleDirs = static::getModulePaths($moduleName);
157
        foreach ($moduleDirs as $moduleDir) {
158
            if (is_dir($moduleDir)) {
159
                foreach (scandir($moduleDir) as $dir) {
160
                    if (preg_match('!Controllers$!', $dir) && is_dir($moduleDir . '/' . $dir)) {
161
                        $path = $moduleDir . '/' . $dir;
162
                        foreach (scandir($path) as $file) {
163
                            if (preg_match('!Controller\.php$!', $file) && is_file($path . '/' . $file)) {
164
                                $controllerName = preg_replace('!Controller\.php$!', '', $file);
165
                                $controllers[preg_replace('!Controllers$!', '', $dir)][$controllerName] = $path . '/' . $file;
166
                            }
167
                        }
168
                    }
169
                }
170
            }
171
        }
172
        return $controllers;
173
    }
174
175
    /**
176
     * Find module by request
177
     *
178
     * @param \Inji\App $app
179
     * @param array|null $params
180
     * @return \Inji\Module
181
     */
182
    public static function resolveModule($app, $params = null) {
183
        $search = is_array($params) ? $params : $app->params;
184
        if (!empty($search[0]) && $app->{$search[0]}) {
185
            $module = $app->{$search[0]};
186
            $module->params = array_slice($search, 1);
187
            return $module;
188
        }
189
        if (!empty($app->config['defaultModule']) && $app->{$app->config['defaultModule']}) {
190
            $module = $app->{$app->config['defaultModule']};
191
            $module->params = $app->params;
192
            return $module;
193
        }
194
195
        if ($app->Main) {
0 ignored issues
show
Bug Best Practice introduced by
The property Main does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
196
            $module = $app->Main;
197
            $module->params = $app->params;
198
            return $module;
199
        }
200
        return null;
201
    }
202
203
    /**
204
     * Get posible path for controller
205
     *
206
     * @return array
207
     */
208
    public function getPossibleControllers() {
209
        $possibleClasses = [];
210
        if (!empty($this->params[0]) && ucfirst($this->params[0]) != $this->name) {
211
            $possibleClasses['curApp_slice'] = $this->app->namespace . '\\' . $this->name . '\\' . ucfirst($this->params[0]) . ucfirst($this->app->type) . 'Controller';
212
            $possibleClasses['system_slice'] = 'Inji\\' . $this->name . '\\' . ucfirst($this->params[0]) . ucfirst($this->app->type) . 'Controller';
213
            $possibleClasses['universal_curApp_slice'] = $this->app->namespace . '\\' . $this->name . '\\' . ucfirst($this->params[0]) . 'Controller';
214
            $possibleClasses['universal_system_slice'] = 'Inji\\' . $this->name . '\\' . ucfirst($this->params[0]) . 'Controller';
215
        }
216
        $possibleClasses['curApp'] = $this->app->namespace . '\\' . $this->name . '\\' . $this->name . ucfirst($this->app->type) . 'Controller';
217
        $possibleClasses['system'] = 'Inji\\' . $this->name . '\\' . $this->name . ucfirst($this->app->type) . 'Controller';
218
219
        $possibleClasses['universal_curApp'] = $this->app->namespace . '\\' . $this->name . '\\' . $this->name . 'Controller';
220
        $possibleClasses['universal_system'] = 'Inji\\' . $this->name . '\\' . $this->name . 'Controller';
221
        return $possibleClasses;
222
    }
223
224
    /**
225
     * Find controller by request
226
     *
227
     * @return \Inji\Controller
228
     */
229
    public function findController() {
230
        $possibleClasses = $this->getPossibleControllers();
231
        foreach ($possibleClasses as $possibleClassType => $possibleClass) {
232
            if (class_exists($possibleClass)) {
233
                if (strpos($possibleClassType, 'slice')) {
234
                    $controllerName = ucfirst($this->params[0]);
235
                    $params = array_slice($this->params, 1);
236
                } else {
237
                    $controllerName = $this->name;
238
                    $params = $this->params;
239
                }
240
                $controller = new $possibleClass();
241
                $controller->params = $params;
242
                $controller->module = $this;
243
                $controller->path = Router::getLoadedClassPath($possibleClass);
244
                $controller->name = $controllerName;
245
                return $controller;
246
            }
247
        }
248
    }
249
250
    /**
251
     * Return module info
252
     *
253
     * @param string $moduleName
254
     * @return array
255
     */
256 1
    public static function getInfo($moduleName = '') {
257 1
        if (!$moduleName && get_called_class()) {
258 1
            $moduleName = get_called_class();
259
        } elseif (!$moduleName) {
260
            return [];
261
        }
262 1
        $paths = Module::getModulePaths($moduleName);
263 1
        foreach ($paths as $path) {
264 1
            if (file_exists($path . '/info.php')) {
265 1
                return include $path . '/info.php';
266
            }
267
        }
268 1
        return [];
269
    }
270
271
    /**
272
     * Return snippets by name
273
     *
274
     * @param string $snippetsPath
275
     * @param boolean $extensions
276
     * @param string $dir
277
     * @param string $moduleName
278
     * @return array
279
     */
280
    public function getSnippets($snippetsPath, $extensions = true, $dir = '/snippets', $moduleName = '') {
281
        $moduleName = $moduleName ? $moduleName : $this->name;
282
        $modulePaths = Module::getModulePaths($moduleName);
283
        $modulePaths = array_reverse($modulePaths);
284
        $modulePaths['templatePath'] = App::$cur->view->template->path . '/modules/' . ucfirst($moduleName);
0 ignored issues
show
Bug Best Practice introduced by
The property view does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
285
        $snippets = [];
286
        foreach ($modulePaths as $path) {
287
            if (file_exists($path . $dir . '/' . $snippetsPath)) {
288
                $snippetsPaths = array_slice(scandir($path . $dir . '/' . $snippetsPath), 2);
289
                foreach ($snippetsPaths as $snippetPath) {
290
                    if (is_dir($path . $dir . '/' . $snippetsPath . '/' . $snippetPath)) {
291
                        $snippets[$snippetPath] = include $path . $dir . '/' . $snippetsPath . '/' . $snippetPath . '/info.php';
292
                    } else {
293
                        $snippets[pathinfo($snippetPath, PATHINFO_FILENAME)] = include $path . $dir . '/' . $snippetsPath . '/' . $snippetPath;
294
                    }
295
                }
296
            }
297
        }
298
        if ($extensions) {
299
            $snippets = array_merge($snippets, $this->getExtensions('snippets', $snippetsPath));
300
        }
301
        return $snippets;
302
    }
303
304
    /**
305
     * Return module objects
306
     *
307
     * @return array
308
     */
309
    public function getObjects($filterNamespace = '') {
310
        $moduleName = $this->name;
311
        $modulePaths = Module::getModulePaths($moduleName);
312
        $modulePaths = array_reverse($modulePaths);
313
        $scanFn = function ($path, $namespace, &$files = []) use (&$scanFn, $filterNamespace) {
314
            if (file_exists($path)) {
315
                foreach (scandir($path) as $item) {
316
                    if (in_array($item, ['..', '.'])) {
317
                        continue;
318
                    }
319
                    $filename = pathinfo($item)['filename'];
320
                    if (is_dir($path . '/' . $item)) {
321
                        $scanFn($path . '/' . $item, $namespace . '\\' . $filename, $files);
322
                    } else {
323
                        if (!$filterNamespace || strpos($namespace, $filterNamespace) === 0) {
324
                            $files[$path . '/' . $item] = $namespace . '\\' . $filename;
325
                        }
326
                    }
327
                }
328
            }
329
            return $files;
330
        };
331
        $files = [];
332
        foreach ($modulePaths as $path) {
333
            $scanFn($path . '/objects', $moduleName, $files);
334
        }
335
        return $files;
336
    }
337
338
    /**
339
     * Return module models
340
     *
341
     * @return array
342
     */
343
    public static function getModels($moduleName, $filterNamespace = '') {
344
        $modulePaths = Module::getModulePaths($moduleName);
345
        $modulePaths = array_reverse($modulePaths);
346
        $scanFn = function ($path, $namespace, &$files = []) use (&$scanFn, $filterNamespace) {
347
            if (file_exists($path)) {
348
                foreach (scandir($path) as $item) {
349
                    if (in_array($item, ['..', '.'])) {
350
                        continue;
351
                    }
352
                    $filename = pathinfo($item)['filename'];
353
                    if (is_dir($path . '/' . $item)) {
354
                        $scanFn($path . '/' . $item, $namespace . '\\' . $filename, $files);
355
                    } else {
356
                        if (!$filterNamespace || strpos($namespace, $filterNamespace) === 0) {
357
                            $files[$path . '/' . $item] = $namespace . '\\' . $filename;
358
                        }
359
                    }
360
                }
361
            }
362
            return $files;
363
        };
364
        $files = [];
365
        foreach ($modulePaths as $path) {
366
            $scanFn($path . '/models', $moduleName, $files);
367
        }
368
        return $files;
369
    }
370
371
    /**
372
     * Return extensions for type
373
     *
374
     * @param string $extensionType
375
     * @param string $request
376
     * @return array
377
     */
378
    public function getExtensions($extensionType, $request) {
379
        $extensions = [];
380
        $modules = Module::getInstalled(App::$cur);
381
        $method = 'get' . ucfirst($extensionType);
382
        foreach ($modules as $module) {
383
            $extensions = array_merge($extensions, $this->{$method}($request, false, "/extensions/{$this->name}/" . $extensionType, $module));
384
        }
385
        return $extensions;
386
    }
387
388 1
    public function checkDbMigration() {
389 1
        if (empty($this->info['migrations'])) {
390 1
            return true;
391
        }
392
        $code = 'module:' . get_called_class();
393
        $newMigrations = App::$cur->db->compareMigrations($code, $this->info['migrations']);
0 ignored issues
show
The method compareMigrations() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

393
        /** @scrutinizer ignore-call */ 
394
        $newMigrations = App::$cur->db->compareMigrations($code, $this->info['migrations']);
Loading history...
Bug Best Practice introduced by
The property db does not exist on Inji\App. Since you implemented __get, consider adding a @property annotation.
Loading history...
The method compareMigrations() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

393
        /** @scrutinizer ignore-call */ 
394
        $newMigrations = App::$cur->db->compareMigrations($code, $this->info['migrations']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
394
        foreach ($newMigrations as $version => $migrationOption) {
395
            $migration = include $this->path . '/migrations/' . $migrationOption . '.php';
396
            App::$cur->db->makeMigration($code, $version, $migration);
0 ignored issues
show
The method makeMigration() does not exist on Inji\Module. It seems like you code against a sub-type of Inji\Module such as Inji\Db. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

396
            App::$cur->db->/** @scrutinizer ignore-call */ 
397
                           makeMigration($code, $version, $migration);
Loading history...
397
        }
398
    }
399
400
    public function sitemap() {
401
        return [];
402
    }
403
}