Router   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 194
rs 9.92
c 0
b 0
f 0
wmc 31

9 Methods

Rating   Name   Duplication   Size   Complexity  
B resolveRouting() 0 49 10
A setAction() 0 4 2
A validate() 0 23 6
A setController() 0 5 2
A __construct() 0 5 1
A resolveStaticFilePath() 0 23 6
A getRoutingResult() 0 3 1
A __destruct() 0 3 1
A resolve() 0 6 2
1
<?php
2
namespace WebStream\Delegate;
3
4
use WebStream\DI\Injector;
5
use WebStream\Container\Container;
6
use WebStream\Util\Security;
7
use WebStream\Util\CommonUtils;
8
use WebStream\Exception\Extend\RouterException;
9
10
/**
11
 * ルーティングクラス
12
 * @author Ryuichi TANAKA.
13
 * @since 2011/08/19
14
 * @version 0.7
15
 */
16
class Router
17
{
18
    use Injector, CommonUtils;
19
20
    /**
21
     * @var array<string> ルーティングルール
22
     */
23
    private $rules;
24
25
    /**
26
     * @var Container リクエストコンテナ
27
     */
28
    private $request;
29
30
    /**
31
     * @var Container ルーティング結果
32
     */
33
    private $routingContainer;
34
35
    /**
36
     * コンストラクタ
37
     * @param Request リクエストオブジェクト
0 ignored issues
show
Documentation Bug introduced by
The doc comment リクエストオブジェクト at position 0 could not be parsed: Unknown type name 'リクエストオブジェクト' at position 0 in リクエストオブジェクト.
Loading history...
38
     */
39
    public function __construct(array $rules, Container $request)
40
    {
41
        $this->rules = $rules;
42
        $this->request = $request;
43
        $this->routingContainer = new Container(false);
44
    }
45
46
    /**
47
     * デストラクタ
48
     */
49
    public function __destruct()
50
    {
51
        $this->logger->debug("Router is clear.");
0 ignored issues
show
Bug Best Practice introduced by
The property logger does not exist on WebStream\Delegate\Router. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug introduced by
The method debug() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

51
        $this->logger->/** @scrutinizer ignore-call */ 
52
                       debug("Router is clear.");

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
52
    }
53
54
    /**
55
     * ルーティングを解決する
56
     */
57
    public function resolve()
58
    {
59
        // ルーティングルールの検証
60
        $this->validate();
61
        // ルーティング結果の格納
62
        $this->resolveRouting() ?: $this->resolveStaticFilePath();
63
    }
64
65
    /**
66
     * ルーティング結果を返却する
67
     * @return Container ルーティング結果
68
     */
69
    public function getRoutingResult()
70
    {
71
        return $this->routingContainer;
72
    }
73
74
    /**
75
     * ルーティングルールを検証する
76
     */
77
    private function validate()
78
    {
79
        // パス定義部分('/xxx')は禁止の定義がされた時点でエラーとする
80
        // CA部分('controller#action')はパスにアクセスされたときにチェックする
81
        foreach ($this->rules as $path => $ca) {
82
            // 静的ファイルへのパスがルーティングルールに定義された場合
83
            // パス定義された時点で弾く
84
            if (preg_match('/\/(img|js|css|file)(?:$|\/)/', $path)) {
85
                throw new RouterException("Include the prohibit routing path: " . $path);
86
            }
87
            // 許可したルーティングパス定義に合っていなければ弾く
88
            if (!preg_match('/^\/{1}(?:$|:?[a-zA-Z]{1}[a-zA-Z0-9-_\/\.:]{0,}$)/', $path)) {
89
                throw new RouterException("Invalid path defintion: " . $path);
90
            }
91
            // ルールとURLがマッチした場合に動的にチェックを掛ける
92
            // パスがマッチしたときにアクション名をチェックし、その時点で弾く
93
            if ($this->request->pathInfo === $path) {
0 ignored issues
show
Bug Best Practice introduced by
The property pathInfo does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
94
                // ルーティング定義(Controller#Action)が正しい場合
95
                // _(アンダースコア)は許可するが、2回以上の連続の場合、末尾につく場合は許可しない
96
                // NG例:my__blog, my_blog_
97
                if (!preg_match('/^(?:([a-z]{1}(?:_(?=[a-z])|[a-z0-9])+))#(?:([a-z]{1}(?:_(?=[a-z])|[a-z0-9])+))$/', $ca, $matches)) {
98
                    // ルーティング定義(Controller#Action)が正しくない場合
99
                    throw new RouterException("Invalid controller#action definition: " . $ca);
100
                }
101
            }
102
        }
103
    }
104
105
    /**
106
     * ルーティングを解決する
107
     */
108
    private function resolveRouting()
109
    {
110
        // ルーティングルールからController、Actionを取得
111
        foreach ($this->rules as $path => $ca) {
112
            $route = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $route is dead and can be removed.
Loading history...
113
            $tokens = explode("/", ltrim($path, "/"));
114
            $placeholderedParams = [];
115
            $keyList = [];
116
117
            for ($i = 0; $i < count($tokens); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
118
                $token = $tokens[$i];
119
                // PATH定義にプレースホルダがある場合は正規表現に置き換える
120
                if (preg_match('/:(.*?)(?:\/|$)/', $token, $matches)) {
121
                    $keyList[] = $matches[1];
122
                    $token = preg_replace('/(:.*?)(?:\/|$)/', '(.+)', $token);
123
                }
124
                $tokens[$i] = $token;
125
            }
126
            // プレースホルダのパラメータをセット
127
            $expantionPath = $path;
128
            // PATH_INFOの階層数とルーティング定義の階層数が一致すればルーティングがマッチ
129
            if (($this->request->pathInfo !== $path) &&
0 ignored issues
show
Bug Best Practice introduced by
The property pathInfo does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
130
                count(explode('/', $path)) === count(explode('/', $this->request->pathInfo))) {
131
                // プレースホルダと実URLをひもづける
132
                $pathPattern = "/^\/" . implode("\/", $tokens) . "$/";
133
                if (preg_match($pathPattern, $this->request->pathInfo, $matches)) {
134
                    for ($j = 1; $j < count($matches); $j++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
135
                        $key = $keyList[$j - 1];
136
                        $placeholderedParams[$key] = Security::safetyIn($matches[$j]);
137
                        // プレースホルダを一時展開する
138
                        $expantionPath = preg_replace('/:[a-zA-Z_]{1}[a-zA-Z0-9_]{0,}/', $matches[$j], $expantionPath, 1);
139
                    }
140
                }
141
            }
142
143
            // プレースホルダを展開済みのパス定義が完全一致したときはController、Actionを展開する
144
            if ($this->request->pathInfo === $expantionPath &&
145
                preg_match('/^(?:([a-z]{1}(?:_(?=[a-z])|[a-z0-9])+))#(?:([a-z]{1}(?:_(?=[a-z])|[a-z0-9])+))$/', $ca, $matches)) {
146
                $this->setController($matches[1]);
147
                $this->setAction($matches[2]);
148
                $this->routingContainer->params = $placeholderedParams;
0 ignored issues
show
Bug Best Practice introduced by
The property params does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
149
                $this->logger->info("Routed path: " . $matches[1] . "#" . $matches[2]);
0 ignored issues
show
Bug Best Practice introduced by
The property logger does not exist on WebStream\Delegate\Router. Since you implemented __get, consider adding a @property annotation.
Loading history...
150
151
                // ルーティングルールがマッチした場合は抜ける
152
                return true;
153
            }
154
        }
155
156
        return false;
157
    }
158
159
    /**
160
     * 静的ファイルパスを解決する
161
     */
162
    private function resolveStaticFilePath()
163
    {
164
        $staticFile = $this->applicationInfo->applicationRoot . "/app/views/" . $this->applicationInfo->publicDir . $this->request->pathInfo;
0 ignored issues
show
Bug Best Practice introduced by
The property applicationInfo does not exist on WebStream\Delegate\Router. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property pathInfo does not exist on WebStream\Container\Container. Since you implemented __get, consider adding a @property annotation.
Loading history...
165
166
        if (is_file($staticFile)) {
167
            $this->routingContainer->staticFile = $staticFile;
0 ignored issues
show
Bug Best Practice introduced by
The property staticFile does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
168
        } elseif (pathinfo($staticFile, PATHINFO_EXTENSION) == 'css') {
169
            // cssファイル指定かつ存在しない場合で、同ディレクトリ内にlessファイルがあればcssにコンパイルする
170
            $less = new \lessc();
0 ignored issues
show
Bug introduced by
The type lessc was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
171
            $dirpath = dirname($staticFile);
172
            $filenameWitoutExt = pathinfo($staticFile, PATHINFO_FILENAME);
173
            $lessFilepath = $dirpath . "/" . $filenameWitoutExt . ".less";
174
            // lessファイルも見つからない場合はエラー
175
            if (!file_exists($lessFilepath)) {
176
                $this->logger->error("The file of css has been specified, but not found even file of less:" . $lessFilepath);
0 ignored issues
show
Bug Best Practice introduced by
The property logger does not exist on WebStream\Delegate\Router. Since you implemented __get, consider adding a @property annotation.
Loading history...
177
178
                return;
179
            }
180
            if (@$less->checkedCompile($lessFilepath, $staticFile)) {
181
                if (is_file($staticFile)) {
182
                    $this->routingContainer->staticFile = $staticFile;
183
                } else {
184
                    $this->logger->error("Failed to file create, cause parmission denied: " . $dirpath);
185
                }
186
            }
187
        }
188
    }
189
190
    /**
191
     * コントローラを設定する
192
     * @param string コントローラ文字列
0 ignored issues
show
Documentation Bug introduced by
The doc comment コントローラ文字列 at position 0 could not be parsed: Unknown type name 'コントローラ文字列' at position 0 in コントローラ文字列.
Loading history...
193
     */
194
    private function setController($controller)
195
    {
196
        if (isset($controller)) {
197
            $this->routingContainer->pageName = $this->snake2ucamel($controller);
0 ignored issues
show
Bug Best Practice introduced by
The property pageName does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
198
            $this->routingContainer->controller = $this->snake2ucamel($controller) . "Controller";
0 ignored issues
show
Bug Best Practice introduced by
The property controller does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
199
        }
200
    }
201
202
    /**
203
     * アクションを設定する
204
     * @param string アクション文字列
0 ignored issues
show
Documentation Bug introduced by
The doc comment アクション文字列 at position 0 could not be parsed: Unknown type name 'アクション文字列' at position 0 in アクション文字列.
Loading history...
205
     */
206
    private function setAction($action)
207
    {
208
        if (isset($action)) {
209
            $this->routingContainer->action = $this->snake2lcamel($action);
0 ignored issues
show
Bug Best Practice introduced by
The property action does not exist on WebStream\Container\Container. Since you implemented __set, consider adding a @property annotation.
Loading history...
210
        }
211
    }
212
}
213