Issues (81)

models/Route.php (2 issues)

1
<?php
2
3
namespace toir427\admin\models;
4
5
use Exception;
6
use toir427\admin\components\Configs;
7
use toir427\admin\components\Helper;
8
use toir427\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 \toir427\admin\BaseObject
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
                    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
Bug Best Practice introduced by
The property routePrefix does not exist on toir427\admin\models\Route. Since you implemented __get, consider adding a @property annotation.
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
Bug Best Practice introduced by
The property routePrefix does not exist on toir427\admin\models\Route. Since you implemented __get, consider adding a @property annotation.
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) {
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