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) { |
||
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; |
||
0 ignored issues
–
show
|
|||
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 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.