Completed
Pull Request — master (#308)
by
unknown
04:37
created

Route::getRouteRecursive()   B

Complexity

Conditions 5
Paths 41

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 24
rs 8.5125
cc 5
eloc 16
nc 41
nop 2
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
    /**
24
     * Assign or remove items
25
     * @param array $routes
26
     * @return array
27
     */
28
    public function addNew($routes)
29
    {
30
        $manager = Configs::authManager();
31
        foreach ($routes as $route) {
32
            try {
33
                $r = explode('&', $route);
34
                $item = $manager->createPermission('/' . trim($route, '/'));
35
                if (count($r) > 1) {
36
                    $action = '/' . trim($r[0], '/');
37
                    if (($itemAction = $manager->getPermission($action)) === null) {
38
                        $itemAction = $manager->createPermission($action);
39
                        $manager->add($itemAction);
40
                    }
41
                    unset($r[0]);
42 View Code Duplication
                    foreach ($r as $part) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
                        $part = explode('=', $part);
44
                        $item->data['params'][$part[0]] = isset($part[1]) ? $part[1] : '';
45
                    }
46
                    $this->setDefaultRule();
47
                    $item->ruleName = RouteRule::RULE_NAME;
48
                    $manager->add($item);
49
                    $manager->addChild($item, $itemAction);
50
                } else {
51
                    $manager->add($item);
52
                }
53
            } catch (Exception $exc) {
54
                Yii::error($exc->getMessage(), __METHOD__);
55
            }
56
        }
57
        Helper::invalidate();
58
    }
59
60
    /**
61
     * Assign or remove items
62
     * @param array $routes
63
     * @return array
64
     */
65
    public function remove($routes)
66
    {
67
        $manager = Configs::authManager();
68
        foreach ($routes as $route) {
69
            try {
70
                $item = $manager->createPermission('/' . trim($route, '/'));
71
                $manager->remove($item);
72
            } catch (Exception $exc) {
73
                Yii::error($exc->getMessage(), __METHOD__);
74
            }
75
        }
76
        Helper::invalidate();
77
    }
78
79
    /**
80
     * Get available and assigned routes
81
     * @return array
82
     */
83
    public function getRoutes()
84
    {
85
        $manager = Configs::authManager();
86
        $routes = $this->getAppRoutes();
87
        $exists = [];
88
        foreach (array_keys($manager->getPermissions()) as $name) {
89
            if ($name[0] !== '/') {
90
                continue;
91
            }
92
            $exists[] = $name;
93
            unset($routes[$name]);
94
        }
95
        return [
96
            'available' => array_keys($routes),
97
            'assigned' => $exists,
98
        ];
99
    }
100
101
    /**
102
     * Get list of application routes
103
     * @return array
104
     */
105
    public function getAppRoutes($module = null)
106
    {
107
        if ($module === null) {
108
            $module = Yii::$app;
109
        } elseif (is_string($module)) {
110
            $module = Yii::$app->getModule($module);
111
        }
112
        $key = [__METHOD__, $module->getUniqueId()];
113
        $cache = Configs::instance()->cache;
114
        if ($cache === null || ($result = $cache->get($key)) === false) {
115
            $result = [];
116
            $this->getRouteRecursive($module, $result);
117
            if ($cache !== null) {
118
                $cache->set($key, $result, Configs::instance()->cacheDuration, new TagDependency([
119
                    'tags' => self::CACHE_TAG,
120
                ]));
121
            }
122
        }
123
124
        return $result;
125
    }
126
127
    /**
128
     * Get route(s) recursive
129
     * @param \yii\base\Module $module
130
     * @param array $result
131
     */
132
    protected function getRouteRecursive($module, &$result)
133
    {
134
        $token = "Get Route of '" . get_class($module) . "' with id '" . $module->uniqueId . "'";
135
        Yii::beginProfile($token, __METHOD__);
136
        try {
137
            foreach ($module->getModules() as $id => $child) {
138
                if (($child = $module->getModule($id)) !== null) {
139
                    $this->getRouteRecursive($child, $result);
140
                }
141
            }
142
143
            foreach ($module->controllerMap as $id => $type) {
144
                $this->getControllerActions($type, $id, $module, $result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by parameter $result on line 132 can also be of type array; however, mdm\admin\models\Route::getControllerActions() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
145
            }
146
147
            $namespace = trim($module->controllerNamespace, '\\') . '\\';
148
            $this->getControllerFiles($module, $namespace, '', $result);
149
            $all = '/' . ltrim($module->uniqueId . '/*', '/');
150
            $result[$all] = $all;
151
        } catch (\Exception $exc) {
152
            Yii::error($exc->getMessage(), __METHOD__);
153
        }
154
        Yii::endProfile($token, __METHOD__);
155
    }
156
157
    /**
158
     * Get list controller under module
159
     * @param \yii\base\Module $module
160
     * @param string $namespace
161
     * @param string $prefix
162
     * @param mixed $result
163
     * @return mixed
164
     */
165
    protected function getControllerFiles($module, $namespace, $prefix, &$result)
166
    {
167
        $path = Yii::getAlias('@' . str_replace('\\', '/', $namespace), false);
168
        $token = "Get controllers from '$path'";
169
        Yii::beginProfile($token, __METHOD__);
170
        try {
171
            if (!is_dir($path)) {
172
                return;
173
            }
174
            foreach (scandir($path) as $file) {
175
                if ($file == '.' || $file == '..') {
176
                    continue;
177
                }
178
                if (is_dir($path . '/' . $file) && preg_match('%^[a-z0-9_/]+$%i', $file . '/')) {
179
                    $this->getControllerFiles($module, $namespace . $file . '\\', $prefix . $file . '/', $result);
180
                } elseif (strcmp(substr($file, -14), 'Controller.php') === 0) {
181
                    $baseName = substr(basename($file), 0, -14);
182
                    $name = strtolower(preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $baseName));
183
                    $id = ltrim(str_replace(' ', '-', $name), '-');
184
                    $className = $namespace . $baseName . 'Controller';
185
                    if (strpos($className, '-') === false && class_exists($className) && is_subclass_of($className, 'yii\base\Controller')) {
186
                        $this->getControllerActions($className, $prefix . $id, $module, $result);
187
                    }
188
                }
189
            }
190
        } catch (\Exception $exc) {
191
            Yii::error($exc->getMessage(), __METHOD__);
192
        }
193
        Yii::endProfile($token, __METHOD__);
194
    }
195
196
    /**
197
     * Get list action of controller
198
     * @param mixed $type
199
     * @param string $id
200
     * @param \yii\base\Module $module
201
     * @param string $result
202
     */
203
    protected function getControllerActions($type, $id, $module, &$result)
204
    {
205
        $token = "Create controller with cofig=" . VarDumper::dumpAsString($type) . " and id='$id'";
206
        Yii::beginProfile($token, __METHOD__);
207
        try {
208
            /* @var $controller \yii\base\Controller */
209
            $controller = Yii::createObject($type, [$id, $module]);
0 ignored issues
show
Documentation introduced by
$type is of type *, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
210
            $this->getActionRoutes($controller, $result);
0 ignored issues
show
Documentation introduced by
$result is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
211
            $all = "/{$controller->uniqueId}/*";
212
            $result[$all] = $all;
213
        } catch (\Exception $exc) {
214
            Yii::error($exc->getMessage(), __METHOD__);
215
        }
216
        Yii::endProfile($token, __METHOD__);
217
    }
218
219
    /**
220
     * Get route of action
221
     * @param \yii\base\Controller $controller
222
     * @param array $result all controller action.
223
     */
224
    protected function getActionRoutes($controller, &$result)
225
    {
226
        $token = "Get actions of controller '" . $controller->uniqueId . "'";
227
        Yii::beginProfile($token, __METHOD__);
228
        try {
229
            $prefix = '/' . $controller->uniqueId . '/';
230
            foreach ($controller->actions() as $id => $value) {
231
                $result[$prefix . $id] = $prefix . $id;
232
            }
233
            $class = new \ReflectionClass($controller);
234
            foreach ($class->getMethods() as $method) {
235
                $name = $method->getName();
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
236
                if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
237
                    $name = strtolower(preg_replace('/(?<![A-Z])[A-Z]/', ' \0', substr($name, 6)));
238
                    $id = $prefix . ltrim(str_replace(' ', '-', $name), '-');
239
                    $result[$id] = $id;
240
                }
241
            }
242
        } catch (\Exception $exc) {
243
            Yii::error($exc->getMessage(), __METHOD__);
244
        }
245
        Yii::endProfile($token, __METHOD__);
246
    }
247
248
    /**
249
     * Ivalidate cache
250
     */
251
    public static function invalidate()
252
    {
253
        if (Configs::cache() !== null) {
254
            TagDependency::invalidate(Configs::cache(), self::CACHE_TAG);
255
        }
256
    }
257
258
    /**
259
     * Set default rule of parameterize route.
260
     */
261
    protected function setDefaultRule()
262
    {
263
        if (Configs::authManager()->getRule(RouteRule::RULE_NAME) === null) {
264
            Configs::authManager()->add(new RouteRule());
265
        }
266
    }
267
}
268