Completed
Push — master ( cbc143...48298c )
by Misbahul D
06:23 queued 04:21
created

models/Route.php (3 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace mdm\admin\models;
4
5
use Exception;
6
use mdm\admin\components\Configs;
7
use mdm\admin\components\Helper;
8
use mdm\admin\components\RouteRule;
9
use Yii;
10
use yii\caching\TagDependency;
11
use yii\helpers\VarDumper;
12
13
/**
14
 * Description of Route
15
 *
16
 * @author Misbahul D Munir <[email protected]>
17
 * @since 1.0
18
 */
19
class Route extends \yii\base\Object
20
{
21
    const CACHE_TAG = 'mdm.admin.route';
22
23
    const PREFIX_ADVANCED = '@';
24
    const PREFIX_BASIC = '/';
25
26
    private $_routePrefix;
27
28
    /**
29
     * Assign or remove items
30
     * @param array $routes
31
     * @return array
32
     */
33
    public function addNew($routes)
34
    {
35
        $manager = Configs::authManager();
36
        foreach ($routes as $route) {
37
            try {
38
                $r = explode('&', $route);
39
                $item = $manager->createPermission($this->getPermissionName($route));
40
                if (count($r) > 1) {
41
                    $action = '/' . trim($r[0], '/');
42
                    if (($itemAction = $manager->getPermission($action)) === null) {
43
                        $itemAction = $manager->createPermission($action);
44
                        $manager->add($itemAction);
45
                    }
46
                    unset($r[0]);
47 View Code Duplication
                    foreach ($r as $part) {
48
                        $part = explode('=', $part);
49
                        $item->data['params'][$part[0]] = isset($part[1]) ? $part[1] : '';
50
                    }
51
                    $this->setDefaultRule();
52
                    $item->ruleName = RouteRule::RULE_NAME;
53
                    $manager->add($item);
54
                    $manager->addChild($item, $itemAction);
55
                } else {
56
                    $manager->add($item);
57
                }
58
            } catch (Exception $exc) {
59
                Yii::error($exc->getMessage(), __METHOD__);
60
            }
61
        }
62
        Helper::invalidate();
63
    }
64
65
    /**
66
     * Assign or remove items
67
     * @param array $routes
68
     * @return array
69
     */
70
    public function remove($routes)
71
    {
72
        $manager = Configs::authManager();
73
        foreach ($routes as $route) {
74
            try {
75
                $item = $manager->createPermission($this->getPermissionName($route));
76
                $manager->remove($item);
77
            } catch (Exception $exc) {
78
                Yii::error($exc->getMessage(), __METHOD__);
79
            }
80
        }
81
        Helper::invalidate();
82
    }
83
84
    /**
85
     * Returns route prefix depending on the configuration.
86
     * @return string Route prefix
87
     */
88
    public function getRoutePrefix()
89
    {
90
        if (!$this->_routePrefix) {
91
            $this->_routePrefix = Configs::instance()->advanced ? self::PREFIX_ADVANCED : self::PREFIX_BASIC;
92
        }
93
        return $this->_routePrefix;
94
    }
95
96
    /**
97
     * Returns the correct permission name depending on the configuration.
98
     * @param  string $route Route
99
     * @return string        Permission name
100
     */
101
    public function getPermissionName($route)
102
    {
103
        if (self::PREFIX_BASIC == $this->routePrefix) {
0 ignored issues
show
The property routePrefix does not seem to exist. Did you mean _routePrefix?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
104
            return self::PREFIX_BASIC . trim($route, self::PREFIX_BASIC);
105
        } else {
106
            return self::PREFIX_ADVANCED . ltrim(trim($route, self::PREFIX_BASIC), self::PREFIX_ADVANCED);
107
        }
108
    }
109
110
    /**
111
     * Get available and assigned routes
112
     * @return array
113
     */
114
    public function getRoutes()
115
    {
116
        $manager = Configs::authManager();
117
        // Get advanced configuration
118
        $advanced = Configs::instance()->advanced;
119
        if ($advanced) {
120
            // Use advanced route scheme.
121
            // Set advanced route prefix.
122
            $this->_routePrefix = self::PREFIX_ADVANCED;
123
            // Create empty routes array.
124
            $routes = [];
125
            // Save original app.
126
            $yiiApp = Yii::$app;
127
            // Step through each configured application
128
            foreach ($advanced as $id => $configPaths) {
129
                // Force correct id string.
130
                $id = $this->routePrefix . ltrim(trim($id), $this->routePrefix);
0 ignored issues
show
The property routePrefix does not seem to exist. Did you mean _routePrefix?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
131
                // Create empty config array.
132
                $config = [];
133
                // Assemble configuration for current app.
134
                foreach ($configPaths as $configPath) {
135
                    // Merge every new configuration with the old config array.
136
                    $config = yii\helpers\ArrayHelper::merge($config, require (Yii::getAlias($configPath)));
137
                }
138
                // Create new app using the config array.
139
                unset($config['bootstrap']);
140
                $app = new yii\web\Application($config);
141
                // Get all the routes of the newly created app.
142
                $r = $this->getAppRoutes($app);
143
                // Dump new app
144
                unset($app);
145
                // Prepend the app id to all routes.
146
                foreach ($r as $route) {
147
                    $routes[$id . $route] = $id . $route;
148
                }
149
            }
150
            // Switch back to original app.
151
            Yii::$app = $yiiApp;
152
            unset($yiiApp);
153
        } else {
154
            // Use basic route scheme.
155
            // Set basic route prefix
156
            $this->_routePrefix = self::PREFIX_BASIC;
157
            // Get basic app routes.
158
            $routes = $this->getAppRoutes();
159
        }
160
        $exists = [];
161
        foreach (array_keys($manager->getPermissions()) as $name) {
162
            if ($name[0] !== $this->routePrefix) {
0 ignored issues
show
The property routePrefix does not seem to exist. Did you mean _routePrefix?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
163
                continue;
164
            }
165
            $exists[] = $name;
166
            unset($routes[$name]);
167
        }
168
        return [
169
            'available' => array_keys($routes),
170
            'assigned' => $exists,
171
        ];
172
    }
173
174
    /**
175
     * Get list of application routes
176
     * @return array
177
     */
178
    public function getAppRoutes($module = null)
179
    {
180
        if ($module === null) {
181
            $module = Yii::$app;
182
        } elseif (is_string($module)) {
183
            $module = Yii::$app->getModule($module);
184
        }
185
        $key = [__METHOD__, Yii::$app->id, $module->getUniqueId()];
186
        $cache = Configs::instance()->cache;
187
        if ($cache === null || ($result = $cache->get($key)) === false) {
188
            $result = [];
189
            $this->getRouteRecursive($module, $result);
190
            if ($cache !== null) {
191
                $cache->set($key, $result, Configs::instance()->cacheDuration, new TagDependency([
192
                    'tags' => self::CACHE_TAG,
193
                ]));
194
            }
195
        }
196
197
        return $result;
198
    }
199
200
    /**
201
     * Get route(s) recursive
202
     * @param \yii\base\Module $module
203
     * @param array $result
204
     */
205
    protected function getRouteRecursive($module, &$result)
206
    {
207
        $token = "Get Route of '" . get_class($module) . "' with id '" . $module->uniqueId . "'";
208
        Yii::beginProfile($token, __METHOD__);
209
        try {
210
            foreach ($module->getModules() as $id => $child) {
211
                if (($child = $module->getModule($id)) !== null) {
212
                    $this->getRouteRecursive($child, $result);
213
                }
214
            }
215
216
            foreach ($module->controllerMap as $id => $type) {
217
                $this->getControllerActions($type, $id, $module, $result);
218
            }
219
220
            $namespace = trim($module->controllerNamespace, '\\') . '\\';
221
            $this->getControllerFiles($module, $namespace, '', $result);
222
            $all = '/' . ltrim($module->uniqueId . '/*', '/');
223
            $result[$all] = $all;
224
        } catch (\Exception $exc) {
225
            Yii::error($exc->getMessage(), __METHOD__);
226
        }
227
        Yii::endProfile($token, __METHOD__);
228
    }
229
230
    /**
231
     * Get list controller under module
232
     * @param \yii\base\Module $module
233
     * @param string $namespace
234
     * @param string $prefix
235
     * @param mixed $result
236
     * @return mixed
237
     */
238
    protected function getControllerFiles($module, $namespace, $prefix, &$result)
239
    {
240
        $path = Yii::getAlias('@' . str_replace('\\', '/', $namespace), false);
241
        $token = "Get controllers from '$path'";
242
        Yii::beginProfile($token, __METHOD__);
243
        try {
244
            if (!is_dir($path)) {
245
                return;
246
            }
247
            foreach (scandir($path) as $file) {
248
                if ($file == '.' || $file == '..') {
249
                    continue;
250
                }
251
                if (is_dir($path . '/' . $file) && preg_match('%^[a-z0-9_/]+$%i', $file . '/')) {
252
                    $this->getControllerFiles($module, $namespace . $file . '\\', $prefix . $file . '/', $result);
253
                } elseif (strcmp(substr($file, -14), 'Controller.php') === 0) {
254
                    $baseName = substr(basename($file), 0, -14);
255
                    $name = strtolower(preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $baseName));
256
                    $id = ltrim(str_replace(' ', '-', $name), '-');
257
                    $className = $namespace . $baseName . 'Controller';
258
                    if (strpos($className, '-') === false && class_exists($className) && is_subclass_of($className, 'yii\base\Controller')) {
259
                        $this->getControllerActions($className, $prefix . $id, $module, $result);
260
                    }
261
                }
262
            }
263
        } catch (\Exception $exc) {
264
            Yii::error($exc->getMessage(), __METHOD__);
265
        }
266
        Yii::endProfile($token, __METHOD__);
267
    }
268
269
    /**
270
     * Get list action of controller
271
     * @param mixed $type
272
     * @param string $id
273
     * @param \yii\base\Module $module
274
     * @param string $result
275
     */
276
    protected function getControllerActions($type, $id, $module, &$result)
277
    {
278
        $token = "Create controller with cofig=" . VarDumper::dumpAsString($type) . " and id='$id'";
279
        Yii::beginProfile($token, __METHOD__);
280
        try {
281
            /* @var $controller \yii\base\Controller */
282
            $controller = Yii::createObject($type, [$id, $module]);
283
            $this->getActionRoutes($controller, $result);
284
            $all = "/{$controller->uniqueId}/*";
285
            $result[$all] = $all;
286
        } catch (\Exception $exc) {
287
            Yii::error($exc->getMessage(), __METHOD__);
288
        }
289
        Yii::endProfile($token, __METHOD__);
290
    }
291
292
    /**
293
     * Get route of action
294
     * @param \yii\base\Controller $controller
295
     * @param array $result all controller action.
296
     */
297
    protected function getActionRoutes($controller, &$result)
298
    {
299
        $token = "Get actions of controller '" . $controller->uniqueId . "'";
300
        Yii::beginProfile($token, __METHOD__);
301
        try {
302
            $prefix = '/' . $controller->uniqueId . '/';
303
            foreach ($controller->actions() as $id => $value) {
304
                $result[$prefix . $id] = $prefix . $id;
305
            }
306
            $class = new \ReflectionClass($controller);
307
            foreach ($class->getMethods() as $method) {
308
                $name = $method->getName();
309
                if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
310
                    $name = strtolower(preg_replace('/(?<![A-Z])[A-Z]/', ' \0', substr($name, 6)));
311
                    $id = $prefix . ltrim(str_replace(' ', '-', $name), '-');
312
                    $result[$id] = $id;
313
                }
314
            }
315
        } catch (\Exception $exc) {
316
            Yii::error($exc->getMessage(), __METHOD__);
317
        }
318
        Yii::endProfile($token, __METHOD__);
319
    }
320
321
    /**
322
     * Ivalidate cache
323
     */
324
    public static function invalidate()
325
    {
326
        if (Configs::cache() !== null) {
327
            TagDependency::invalidate(Configs::cache(), self::CACHE_TAG);
328
        }
329
    }
330
331
    /**
332
     * Set default rule of parameterize route.
333
     */
334
    protected function setDefaultRule()
335
    {
336
        if (Configs::authManager()->getRule(RouteRule::RULE_NAME) === null) {
337
            Configs::authManager()->add(new RouteRule());
338
        }
339
    }
340
}
341