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) { |
|
|
|
|
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); |
|
|
|
|
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
|
|
|
|
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.