Passed
Push — master ( 34a83f...053766 )
by kill
03:49
created

Request::url()   B

Complexity

Conditions 11
Paths 17

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 16
nc 17
nop 1
dl 0
loc 20
rs 7.1162
c 0
b 0
f 0
ccs 0
cts 20
cp 0
crap 132

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Created by rozbo at 2017/3/19 下午6:21
4
 */
5
6
namespace puck;
7
class Request
8
{
9
    /**
10
     * @var object 对象实例
11
     */
12
    protected $instance;
13
    protected $config;
14
15
    protected $method;
16
    /**
17
     * @var string 域名(含协议和端口)
18
     */
19
    protected $domain;
20
21
    /**
22
     * @var string URL地址
23
     */
24
    protected $url;
25
26
    /**
27
     * @var string 基础URL
28
     */
29
    protected $baseUrl;
30
31
    /**
32
     * @var string 当前执行的文件
33
     */
34
    protected $baseFile;
35
36
    /**
37
     * @var string 访问的ROOT地址
38
     */
39
    protected $root;
40
41
    /**
42
     * @var string pathinfo
43
     */
44
    protected $pathinfo;
45
46
    /**
47
     * @var string pathinfo(不含后缀)
48
     */
49
    protected $path;
50
51
    /**
52
     * @var array 当前路由信息
53
     */
54
    protected $routeInfo = [];
55
56
    /**
57
     * @var array 当前调度信息
58
     */
59
    protected $dispatch = [];
60
    protected $module;
61
    protected $controller;
62
    protected $action;
63
    // 当前语言集
64
    protected $langset;
65
66
    /**
67
     * @var array 请求参数
68
     */
69
    protected $param   = [];
70
    protected $get     = [];
71
    protected $post    = [];
72
    protected $request = [];
73
    protected $route   = [];
74
    protected $put;
75
    protected $session = [];
76
    protected $file    = [];
77
    protected $cookie  = [];
78
    protected $server  = [];
79
    protected $header  = [];
80
81
    /**
82
     * @var array 资源类型
83
     */
84
    protected $mimeType = [
85
        'xml'  => 'application/xml,text/xml,application/x-xml',
86
        'json' => 'application/json,text/x-json,application/jsonrequest,text/json',
87
        'js'   => 'text/javascript,application/javascript,application/x-javascript',
88
        'css'  => 'text/css',
89
        'rss'  => 'application/rss+xml',
90
        'yaml' => 'application/x-yaml,text/yaml',
91
        'atom' => 'application/atom+xml',
92
        'pdf'  => 'application/pdf',
93
        'text' => 'text/plain',
94
        'png'  => 'image/png',
95
        'jpg'  => 'image/jpg,image/jpeg,image/pjpeg',
96
        'gif'  => 'image/gif',
97
        'csv'  => 'text/csv',
98
        'html' => 'text/html,application/xhtml+xml,*/*',
99
    ];
100
101
    protected $content;
102
103
    // 全局过滤规则
104
    protected $filter;
105
    // Hook扩展方法
106
    protected static $hook = [];
107
    // 绑定的属性
108
    protected $bind = [];
109
    // php://input
110
    protected $input;
111
    // 请求缓存
112
    protected $cache;
113
    // 缓存是否检查
114
    protected $isCheckCache;
115
116
    /**
117
     * 架构函数
118
     * @access public
119
     * @param array $options 参数
120
     */
121
    public function __construct(Config $config, $options = [])
122
    {
123
        $this->server();
124
        foreach ($options as $name => $item) {
125
            if (property_exists($this, $name)) {
126
                $this->$name = $item;
127
            }
128
        }
129
        $this->config = $config;
130
        if (is_null($this->filter)) {
131
            $this->filter = $this->config->get('default_filter');
132
        }
133
        // 保存 php://input
134
        $this->input = file_get_contents('php://input');
135
    }
136
137
    public function __call($method, $args)
138
    {
139
        if (array_key_exists($method, $this->hook)) {
140
            array_unshift($args, $this);
141
            return call_user_func_array($this->hook[$method], $args);
142
        } else {
143
            throw new Exception('method not exists:' . __CLASS__ . '->' . $method);
144
        }
145
    }
146
147
    /**
148
     * Hook 方法注入
149
     * @access public
150
     * @param string|array  $method 方法名
151
     * @param mixed         $callback callable
152
     * @return void
153
     */
154
    public function hook($method, $callback = null)
155
    {
156
        if (is_array($method)) {
157
            $this->hook = array_merge($this->hook, $method);
158
        } else {
159
            $this->hook[$method] = $callback;
160
        }
161
    }
162
163
    /**
164
     * 创建一个URL请求
165
     * @access public
166
     * @param string    $uri URL地址
167
     * @param string    $method 请求类型
168
     * @param array     $params 请求参数
169
     * @param array     $cookie
170
     * @param array     $files
171
     * @param array     $server
172
     * @param string    $content
173
     * @return \think\Request
174
     */
175
    public function create($uri, $method = 'GET', $params = [], $cookie = [], $files = [], $server = [], $content = null)
176
    {
177
        $server['PATH_INFO']      = '';
178
        $server['REQUEST_METHOD'] = strtoupper($method);
179
        $info                     = parse_url($uri);
180
        if (isset($info['host'])) {
181
            $server['SERVER_NAME'] = $info['host'];
182
            $server['HTTP_HOST']   = $info['host'];
183
        }
184
        if (isset($info['scheme'])) {
185
            if ('https' === $info['scheme']) {
186
                $server['HTTPS']       = 'on';
187
                $server['SERVER_PORT'] = 443;
188
            } else {
189
                unset($server['HTTPS']);
190
                $server['SERVER_PORT'] = 80;
191
            }
192
        }
193
        if (isset($info['port'])) {
194
            $server['SERVER_PORT'] = $info['port'];
195
            $server['HTTP_HOST']   = $server['HTTP_HOST'] . ':' . $info['port'];
196
        }
197
        if (isset($info['user'])) {
198
            $server['PHP_AUTH_USER'] = $info['user'];
199
        }
200
        if (isset($info['pass'])) {
201
            $server['PHP_AUTH_PW'] = $info['pass'];
202
        }
203
        if (!isset($info['path'])) {
204
            $info['path'] = '/';
205
        }
206
        $options                      = [];
207
        $options[strtolower($method)] = $params;
208
        $queryString                  = '';
209
        if (isset($info['query'])) {
210
            parse_str(html_entity_decode($info['query']), $query);
211
            if (!empty($params)) {
212
                $params      = array_replace($query, $params);
213
                $queryString = http_build_query($query, '', '&');
214
            } else {
215
                $params      = $query;
216
                $queryString = $info['query'];
217
            }
218
        } elseif (!empty($params)) {
219
            $queryString = http_build_query($params, '', '&');
220
        }
221
        if ($queryString) {
222
            parse_str($queryString, $get);
223
            $options['get'] = isset($options['get']) ? array_merge($get, $options['get']) : $get;
224
        }
225
226
        $server['REQUEST_URI']  = $info['path'] . ('' !== $queryString ? '?' . $queryString : '');
227
        $server['QUERY_STRING'] = $queryString;
228
        $options['cookie']      = $cookie;
229
        $options['param']       = $params;
230
        $options['file']        = $files;
231
        $options['server']      = $server;
232
        $options['url']         = $server['REQUEST_URI'];
233
        $options['baseUrl']     = $info['path'];
234
        $options['pathinfo']    = '/' == $info['path'] ? '/' : ltrim($info['path'], '/');
235
        $options['method']      = $server['REQUEST_METHOD'];
236
        $options['domain']      = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : '';
237
        $options['content']     = $content;
238
        foreach ($options as $name => $item) {
239
            if (property_exists($this, $name)) {
240
                $this->$name = $item;
241
            }
242
        }
243
        return $this;
244
    }
245
246
    /**
247
     * 设置或获取当前包含协议的域名
248
     * @access public
249
     * @param string $domain 域名
250
     * @return string
251
     */
252
    public function domain($domain = null)
253
    {
254
        if (!is_null($domain)) {
255
            $this->domain = $domain;
256
            return $this;
257
        } elseif (!$this->domain) {
258
            $this->domain = $this->scheme() . '://' . $this->host();
259
        }
260
        return $this->domain;
261
    }
262
263
    /**
264
     * 设置或获取当前完整URL 包括QUERY_STRING
265
     * @access public
266
     * @param string|true $url URL地址 true 带域名获取
267
     * @return string
268
     */
269
    public function url($url = null)
0 ignored issues
show
Coding Style introduced by
url uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
270
    {
271
        if (!is_null($url) && true !== $url) {
272
            $this->url = $url;
0 ignored issues
show
Documentation Bug introduced by
It seems like $url can also be of type object<puck\true>. However, the property $url is declared as type string. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
273
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (puck\Request) is incompatible with the return type documented by puck\Request::url of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
274
        } elseif (!$this->url) {
275
            if ($this->isCli()) {
276
                $this->url = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
277
            } elseif (isset($_SERVER['HTTP_X_REWRITE_URL'])) {
278
                $this->url = $_SERVER['HTTP_X_REWRITE_URL'];
279
            } elseif (isset($_SERVER['REQUEST_URI'])) {
280
                $this->url = $_SERVER['REQUEST_URI'];
281
            } elseif (isset($_SERVER['ORIG_PATH_INFO'])) {
282
                $this->url = $_SERVER['ORIG_PATH_INFO'] . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
283
            } else {
284
                $this->url = '';
285
            }
286
        }
287
        return true === $url ? $this->domain() . $this->url : $this->url;
288
    }
289
290
    /**
291
     * 设置或获取当前URL 不含QUERY_STRING
292
     * @access public
293
     * @param string $url URL地址
294
     * @return string
295
     */
296
    public function baseUrl($url = null)
297
    {
298
        if (!is_null($url) && true !== $url) {
299
            $this->baseUrl = $url;
300
            return $this;
301
        } elseif (!$this->baseUrl) {
302
            $str           = $this->url();
303
            $this->baseUrl = strpos($str, '?') ? strstr($str, '?', true) : $str;
304
        }
305
        return true === $url ? $this->domain() . $this->baseUrl : $this->baseUrl;
306
    }
307
308
    /**
309
     * 设置或获取当前执行的文件 SCRIPT_NAME
310
     * @access public
311
     * @param string $file 当前执行的文件
312
     * @return string
313
     */
314
    public function baseFile($file = null)
0 ignored issues
show
Coding Style introduced by
baseFile uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
315
    {
316
        if (!is_null($file) && true !== $file) {
317
            $this->baseFile = $file;
318
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (puck\Request) is incompatible with the return type documented by puck\Request::baseFile of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
319
        } elseif (!$this->baseFile) {
320
            $url = '';
321
            if (!$this->isCli()) {
322
                $script_name = basename($_SERVER['SCRIPT_FILENAME']);
323
                if (basename($_SERVER['SCRIPT_NAME']) === $script_name) {
324
                    $url = $_SERVER['SCRIPT_NAME'];
325
                } elseif (basename($_SERVER['PHP_SELF']) === $script_name) {
326
                    $url = $_SERVER['PHP_SELF'];
327
                } elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $script_name) {
328
                    $url = $_SERVER['ORIG_SCRIPT_NAME'];
329
                } elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $script_name)) !== false) {
330
                    $url = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $script_name;
331
                } elseif (isset($_SERVER['DOCUMENT_ROOT']) && strpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['DOCUMENT_ROOT']) === 0) {
332
                    $url = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $_SERVER['SCRIPT_FILENAME']));
333
                }
334
            }
335
            $this->baseFile = $url;
336
        }
337
        return true === $file ? $this->domain() . $this->baseFile : $this->baseFile;
338
    }
339
340
    /**
341
     * 设置或获取URL访问根地址
342
     * @access public
343
     * @param string $url URL地址
344
     * @return string
345
     */
346
    public function root($url = null)
347
    {
348
        if (!is_null($url) && true !== $url) {
349
            $this->root = $url;
350
            return $this;
351
        } elseif (!$this->root) {
352
            $file = $this->baseFile();
353
            if ($file && 0 !== strpos($this->url(), $file)) {
354
                $file = str_replace('\\', '/', dirname($file));
355
            }
356
            $this->root = rtrim($file, '/');
357
        }
358
        return true === $url ? $this->domain() . $this->root : $this->root;
359
    }
360
361
    /**
362
     * 获取URL访问根目录
363
     * @access public
364
     * @return string
365
     */
366
    public function rootUrl()
367
    {
368
        $base = $this->root();
369
        $root = strpos($base, '.') ? ltrim(dirname($base), DIRECTORY_SEPARATOR) : $base;
370
        if ('' != $root) {
371
            $root = '/' . ltrim($root, '/');
372
        }
373
        return $root;
374
    }
375
376
377
    /**
378
     * 获取当前请求URL的pathinfo信息(不含URL后缀)
379
     * @access public
380
     * @return string
381
     */
382
    public function path()
383
    {
384
        if (is_null($this->path)) {
385
            $suffix   = $this->config->get('core.url_html_suffix');
386
            $pathinfo = $this->baseUrl();
387
            if (false === $suffix) {
388
                // 禁止伪静态访问
389
                $this->path = $pathinfo;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pathinfo can also be of type this<puck\Request>. However, the property $path is declared as type string. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
390
            } elseif ($suffix) {
391
                // 去除正常的URL后缀
392
                $this->path = preg_replace('/\.(' . ltrim($suffix, '.') . ')$/i', '', $pathinfo);
0 ignored issues
show
Documentation Bug introduced by
It seems like preg_replace('/\\.(' . l... ')$/i', '', $pathinfo) can also be of type array<integer,string>. However, the property $path is declared as type string. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
393
            } else {
394
                // 允许任何后缀访问
395
                $this->path = preg_replace('/\.' . $this->ext() . '$/i', '', $pathinfo);
0 ignored issues
show
Documentation Bug introduced by
It seems like preg_replace('/\\.' . $t.... '$/i', '', $pathinfo) can also be of type array<integer,string>. However, the property $path is declared as type string. Maybe add an additional type check?

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 the id property of an instance of the Account 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.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
396
            }
397
        }
398
        return $this->path;
399
    }
400
401
    /**
402
     * 当前URL的访问后缀
403
     * @access public
404
     * @return string
405
     */
406
    public function ext()
407
    {
408
        return pathinfo($this->pathinfo(), PATHINFO_EXTENSION);
0 ignored issues
show
Documentation Bug introduced by
The method pathinfo does not exist on object<puck\Request>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
409
    }
410
411
    /**
412
     * 获取当前请求的时间
413
     * @access public
414
     * @param bool $float 是否使用浮点类型
415
     * @return integer|float
416
     */
417
    public function time($float = false)
0 ignored issues
show
Coding Style introduced by
time uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
418
    {
419
        return $float ? $_SERVER['REQUEST_TIME_FLOAT'] : $_SERVER['REQUEST_TIME'];
420
    }
421
422
    /**
423
     * 当前请求的资源类型
424
     * @access public
425
     * @return false|string
426
     */
427
    public function type()
428
    {
429
        $accept = $this->server('HTTP_ACCEPT');
430
        if (empty($accept)) {
431
            return false;
432
        }
433
434
        foreach ($this->mimeType as $key => $val) {
435
            $array = explode(',', $val);
436
            foreach ($array as $k => $v) {
437
                if (stristr($accept, $v)) {
438
                    return $key;
439
                }
440
            }
441
        }
442
        return false;
443
    }
444
445
    /**
446
     * 设置资源类型
447
     * @access public
448
     * @param string|array  $type 资源类型名
449
     * @param string        $val 资源类型
450
     * @return void
451
     */
452
    public function mimeType($type, $val = '')
453
    {
454
        if (is_array($type)) {
455
            $this->mimeType = array_merge($this->mimeType, $type);
456
        } else {
457
            $this->mimeType[$type] = $val;
458
        }
459
    }
460
461
    /**
462
     * 当前的请求类型
463
     * @access public
464
     * @param bool $method  true 获取原始请求类型
465
     * @return string
466
     */
467
    public function method($method = false)
0 ignored issues
show
Coding Style introduced by
method uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
method uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
468
    {
469
        if (true === $method) {
470
            // 获取原始请求类型
471
            return $this->isCli() ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
472
        } elseif (!$this->method) {
473
            if (isset($_POST[$this->config->get('var_method')])) {
474
                $this->method = strtoupper($_POST[$this->config->get('var_method')]);
475
                $this->{$this->method}($_POST);
476
            } elseif (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
477
                $this->method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
478
            } else {
479
                $this->method = $this->isCli() ? 'GET' : (isset($this->server['REQUEST_METHOD']) ? $this->server['REQUEST_METHOD'] : $_SERVER['REQUEST_METHOD']);
480
            }
481
        }
482
        return $this->method;
483
    }
484
485
    /**
486
     * 是否为GET请求
487
     * @access public
488
     * @return bool
489
     */
490
    public function isGet()
491
    {
492
        return $this->method() == 'GET';
493
    }
494
495
    /**
496
     * 是否为POST请求
497
     * @access public
498
     * @return bool
499
     */
500
    public function isPost()
501
    {
502
        return $this->method() == 'POST';
503
    }
504
505
    /**
506
     * 是否为PUT请求
507
     * @access public
508
     * @return bool
509
     */
510
    public function isPut()
511
    {
512
        return $this->method() == 'PUT';
513
    }
514
515
    /**
516
     * 是否为DELTE请求
517
     * @access public
518
     * @return bool
519
     */
520
    public function isDelete()
521
    {
522
        return $this->method() == 'DELETE';
523
    }
524
525
    /**
526
     * 是否为HEAD请求
527
     * @access public
528
     * @return bool
529
     */
530
    public function isHead()
531
    {
532
        return $this->method() == 'HEAD';
533
    }
534
535
    /**
536
     * 是否为PATCH请求
537
     * @access public
538
     * @return bool
539
     */
540
    public function isPatch()
541
    {
542
        return $this->method() == 'PATCH';
543
    }
544
545
    /**
546
     * 是否为OPTIONS请求
547
     * @access public
548
     * @return bool
549
     */
550
    public function isOptions()
551
    {
552
        return $this->method() == 'OPTIONS';
553
    }
554
555
    /**
556
     * 是否为cli
557
     * @access public
558
     * @return bool
559
     */
560
    public function isCli()
561
    {
562
        return PHP_SAPI == 'cli';
563
    }
564
565
    /**
566
     * 是否为cgi
567
     * @access public
568
     * @return bool
569
     */
570
    public function isCgi()
571
    {
572
        return strpos(PHP_SAPI, 'cgi') === 0;
573
    }
574
575
    /**
576
     * 获取获取当前请求的参数
577
     * @access public
578
     * @param string|array  $name 变量名
579
     * @param mixed         $default 默认值
580
     * @param string|array  $filter 过滤方法
581
     * @return mixed
582
     */
583
    public function param($name = '', $default = null, $filter = '')
584
    {
585
        if (empty($this->param)) {
586
            $method = $this->method(true);
587
            // 自动获取请求变量
588
            switch ($method) {
589
                case 'POST':
590
                    $vars = $this->post(false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

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...
591
                    break;
592
                case 'PUT':
593
                case 'DELETE':
594
                case 'PATCH':
595
                    $vars = $this->put(false);
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|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...
596
                    break;
597
                default:
598
                    $vars = [];
599
            }
600
            // 当前请求参数和URL地址中的参数合并
601
            $this->param = array_merge($this->get(false), $vars, $this->route(false));
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|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...
602
        }
603
        if (true === $name) {
604
            // 获取包含文件上传信息的数组
605
            $file = $this->file();
606
            $data = is_array($file) ? array_merge($this->param, $file) : $this->param;
607
            return $this->input($data, '', $default, $filter);
608
        }
609
        return $this->input($this->param, $name, $default, $filter);
0 ignored issues
show
Bug introduced by
It seems like $name defined by parameter $name on line 583 can also be of type array; however, puck\Request::input() does only seem to accept string|false, 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...
610
    }
611
612
    /**
613
     * 设置获取获取路由参数
614
     * @access public
615
     * @param string|array  $name 变量名
616
     * @param mixed         $default 默认值
617
     * @param string|array  $filter 过滤方法
618
     * @return mixed
619
     */
620
    public function route($name = '', $default = null, $filter = '')
621
    {
622
        if (is_array($name)) {
623
            $this->param        = [];
624
            return $this->route = array_merge($this->route, $name);
625
        }
626
        return $this->input($this->route, $name, $default, $filter);
627
    }
628
629
    /**
630
     * 设置获取获取GET参数
631
     * @access public
632
     * @param string|array  $name 变量名
633
     * @param mixed         $default 默认值
634
     * @param string|array  $filter 过滤方法
635
     * @return mixed
636
     */
637
    public function get($name = '', $default = null, $filter = '')
0 ignored issues
show
Coding Style introduced by
get uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
638
    {
639
        if (empty($this->get)) {
640
            $this->get = $_GET;
641
        }
642
        if (is_array($name)) {
643
            $this->param      = [];
644
            return $this->get = array_merge($this->get, $name);
645
        }
646
        return $this->input($this->get, $name, $default, $filter);
647
    }
648
649
    /**
650
     * 设置获取获取POST参数
651
     * @access public
652
     * @param string        $name 变量名
653
     * @param mixed         $default 默认值
654
     * @param string|array  $filter 过滤方法
655
     * @return mixed
656
     */
657
    public function post($name = '', $default = null, $filter = '')
0 ignored issues
show
Coding Style introduced by
post uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
658
    {
659
        if (empty($this->post)) {
660
            $content = $this->input;
661
            if (empty($_POST) && false !== strpos($this->contentType(), 'application/json')) {
662
                $this->post = (array) json_decode($content, true);
663
            } else {
664
                $this->post = $_POST;
665
            }
666
        }
667
        if (is_array($name)) {
668
            $this->param       = [];
669
            return $this->post = array_merge($this->post, $name);
670
        }
671
        return $this->input($this->post, $name, $default, $filter);
672
    }
673
674
    /**
675
     * 设置获取获取PUT参数
676
     * @access public
677
     * @param string|array      $name 变量名
678
     * @param mixed             $default 默认值
679
     * @param string|array      $filter 过滤方法
680
     * @return mixed
681
     */
682
    public function put($name = '', $default = null, $filter = '')
683
    {
684
        if (is_null($this->put)) {
685
            $content = $this->input;
686
            if (false !== strpos($this->contentType(), 'application/json')) {
687
                $this->put = (array) json_decode($content, true);
688
            } else {
689
                parse_str($content, $this->put);
690
            }
691
        }
692
        if (is_array($name)) {
693
            $this->param      = [];
694
            return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name);
695
        }
696
697
        return $this->input($this->put, $name, $default, $filter);
698
    }
699
700
    /**
701
     * 设置获取获取DELETE参数
702
     * @access public
703
     * @param string|array      $name 变量名
704
     * @param mixed             $default 默认值
705
     * @param string|array      $filter 过滤方法
706
     * @return mixed
707
     */
708
    public function delete($name = '', $default = null, $filter = '')
709
    {
710
        return $this->put($name, $default, $filter);
711
    }
712
713
    /**
714
     * 设置获取获取PATCH参数
715
     * @access public
716
     * @param string|array      $name 变量名
717
     * @param mixed             $default 默认值
718
     * @param string|array      $filter 过滤方法
719
     * @return mixed
720
     */
721
    public function patch($name = '', $default = null, $filter = '')
722
    {
723
        return $this->put($name, $default, $filter);
724
    }
725
726
    /**
727
     * 获取request变量
728
     * @param string        $name 数据名称
729
     * @param string        $default 默认值
730
     * @param string|array  $filter 过滤方法
731
     * @return mixed
732
     */
733
    public function request($name = '', $default = null, $filter = '')
0 ignored issues
show
Coding Style introduced by
request uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
734
    {
735
        if (empty($this->request)) {
736
            $this->request = $_REQUEST;
737
        }
738
        if (is_array($name)) {
739
            $this->param          = [];
740
            return $this->request = array_merge($this->request, $name);
741
        }
742
        return $this->input($this->request, $name, $default, $filter);
743
    }
744
745
    /**
746
     * 获取session数据
747
     * @access public
748
     * @param string|array  $name 数据名称
749
     * @param string        $default 默认值
750
     * @param string|array  $filter 过滤方法
751
     * @return mixed
752
     */
753
    public function session($name = '', $default = null, $filter = '')
754
    {
755
        if (empty($this->session)) {
756
            $this->session = Session::get();
757
        }
758
        if (is_array($name)) {
759
            return $this->session = array_merge($this->session, $name);
760
        }
761
        return $this->input($this->session, $name, $default, $filter);
762
    }
763
764
    /**
765
     * 获取cookie参数
766
     * @access public
767
     * @param string|array  $name 数据名称
768
     * @param string        $default 默认值
769
     * @param string|array  $filter 过滤方法
770
     * @return mixed
771
     */
772
    public function cookie($name = '', $default = null, $filter = '')
0 ignored issues
show
Coding Style introduced by
cookie uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
773
    {
774
        if (empty($this->cookie)) {
775
            $this->cookie = $_COOKIE;
776
        }
777
        if (is_array($name)) {
778
            return $this->cookie = array_merge($this->cookie, $name);
779
        }
780
        return $this->input($this->cookie, $name, $default, $filter);
781
    }
782
783
    /**
784
     * 获取server参数
785
     * @access public
786
     * @param string|array  $name 数据名称
787
     * @param string        $default 默认值
788
     * @param string|array  $filter 过滤方法
789
     * @return mixed
790
     */
791
    public function server($name = '', $default = null, $filter = '')
0 ignored issues
show
Coding Style introduced by
server uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
792
    {
793
        if (empty($this->server)) {
794
            $this->server = $_SERVER;
795
        }
796
        if (is_array($name)) {
797
            return $this->server = array_merge($this->server, $name);
798
        }
799
        return $this->input($this->server, false === $name ? false : strtoupper($name), $default, $filter);
800
    }
801
802
    /**
803
     * 获取上传的文件信息
804
     * @access public
805
     * @param string|array $name 名称
806
     * @return null|array|\think\File
807
     */
808
    public function file($name = '')
0 ignored issues
show
Coding Style introduced by
file uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
809
    {
810
        if (empty($this->file)) {
811
            $this->file = isset($_FILES) ? $_FILES : [];
812
        }
813
        if (is_array($name)) {
814
            return $this->file = array_merge($this->file, $name);
815
        }
816
        $files = $this->file;
817
        if (!empty($files)) {
818
            // 处理上传文件
819
            $array = [];
820
            foreach ($files as $key => $file) {
821
                if (is_array($file['name'])) {
822
                    $item  = [];
823
                    $keys  = array_keys($file);
824
                    $count = count($file['name']);
825
                    for ($i = 0; $i < $count; $i++) {
826
                        if (empty($file['tmp_name'][$i]) || !is_file($file['tmp_name'][$i])) {
827
                            continue;
828
                        }
829
                        $temp['key'] = $key;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$temp was never initialized. Although not strictly required by PHP, it is generally a good practice to add $temp = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
830
                        foreach ($keys as $_key) {
831
                            $temp[$_key] = $file[$_key][$i];
832
                        }
833
                        $item[] = (new File($temp['tmp_name']))->setUploadInfo($temp);
834
                    }
835
                    $array[$key] = $item;
836
                } else {
837
                    if ($file instanceof File) {
0 ignored issues
show
Bug introduced by
The class puck\File does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
838
                        $array[$key] = $file;
839
                    } else {
840
                        if (empty($file['tmp_name']) || !is_file($file['tmp_name'])) {
841
                            continue;
842
                        }
843
                        $array[$key] = (new File($file['tmp_name']))->setUploadInfo($file);
844
                    }
845
                }
846
            }
847
            if (strpos($name, '.')) {
848
                list($name, $sub) = explode('.', $name);
849
            }
850
            if ('' === $name) {
851
                // 获取全部文件
852
                return $array;
853
            } elseif (isset($sub) && isset($array[$name][$sub])) {
854
                return $array[$name][$sub];
855
            } elseif (isset($array[$name])) {
856
                return $array[$name];
857
            }
858
        }
859
        return;
860
    }
861
862
    /**
863
     * 获取环境变量
864
     * @param string|array  $name 数据名称
865
     * @param string        $default 默认值
866
     * @param string|array  $filter 过滤方法
867
     * @return mixed
868
     */
869
    public function env($name = '', $default = null, $filter = '')
0 ignored issues
show
Coding Style introduced by
env uses the super-global variable $_ENV which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
870
    {
871
        if (empty($this->env)) {
0 ignored issues
show
Documentation introduced by
The property env does not exist on object<puck\Request>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
872
            $this->env = $_ENV;
0 ignored issues
show
Documentation introduced by
The property env does not exist on object<puck\Request>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
873
        }
874
        if (is_array($name)) {
875
            return $this->env = array_merge($this->env, $name);
0 ignored issues
show
Documentation introduced by
The property env does not exist on object<puck\Request>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property env does not exist on object<puck\Request>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
876
        }
877
        return $this->input($this->env, false === $name ? false : strtoupper($name), $default, $filter);
0 ignored issues
show
Documentation introduced by
The property env does not exist on object<puck\Request>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
878
    }
879
880
    /**
881
     * 设置或者获取当前的Header
882
     * @access public
883
     * @param string|array  $name header名称
884
     * @param string        $default 默认值
885
     * @return string
886
     */
887
    public function header($name = '', $default = null)
0 ignored issues
show
Coding Style introduced by
header uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
888
    {
889
        if (empty($this->header)) {
890
            $header = [];
891
            if (function_exists('apache_request_headers') && $result = apache_request_headers()) {
892
                $header = $result;
893
            } else {
894
                $server = $this->server ?: $_SERVER;
895
                foreach ($server as $key => $val) {
896
                    if (0 === strpos($key, 'HTTP_')) {
897
                        $key          = str_replace('_', '-', strtolower(substr($key, 5)));
898
                        $header[$key] = $val;
899
                    }
900
                }
901
                if (isset($server['CONTENT_TYPE'])) {
902
                    $header['content-type'] = $server['CONTENT_TYPE'];
903
                }
904
                if (isset($server['CONTENT_LENGTH'])) {
905
                    $header['content-length'] = $server['CONTENT_LENGTH'];
906
                }
907
            }
908
            $this->header = array_change_key_case($header);
909
        }
910
        if (is_array($name)) {
911
            return $this->header = array_merge($this->header, $name);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->header = a...($this->header, $name); (array) is incompatible with the return type documented by puck\Request::header of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
912
        }
913
        if ('' === $name) {
914
            return $this->header;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->header; (array) is incompatible with the return type documented by puck\Request::header of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
915
        }
916
        $name = str_replace('_', '-', strtolower($name));
917
        return isset($this->header[$name]) ? $this->header[$name] : $default;
918
    }
919
920
    /**
921
     * 获取变量 支持过滤和默认值
922
     * @param array         $data 数据源
923
     * @param string|false  $name 字段名
924
     * @param mixed         $default 默认值
925
     * @param string|array  $filter 过滤函数
926
     * @return mixed
927
     */
928
    public function input($data = [], $name = '', $default = null, $filter = '')
929
    {
930
        if (false === $name) {
931
            // 获取原始数据
932
            return $data;
933
        }
934
        $name = (string) $name;
935
        if ('' != $name) {
936
            // 解析name
937
            if (strpos($name, '/')) {
938
                list($name, $type) = explode('/', $name);
939
            } else {
940
                $type = 's';
941
            }
942
            // 按.拆分成多维数组进行判断
943
            foreach (explode('.', $name) as $val) {
944
                if (isset($data[$val])) {
945
                    $data = $data[$val];
946
                } else {
947
                    // 无输入数据,返回默认值
948
                    return $default;
949
                }
950
            }
951
            if (is_object($data)) {
952
                return $data;
953
            }
954
        }
955
956
        // 解析过滤器
957
        if (is_null($filter)) {
958
            $filter = [];
959
        } else {
960
            $filter = $filter ?: $this->filter;
961
            if (is_string($filter)) {
962
                $filter = explode(',', $filter);
963
            } else {
964
                $filter = (array) $filter;
965
            }
966
        }
967
968
        $filter[] = $default;
969
        if (is_array($data)) {
970
            array_walk_recursive($data, [$this, 'filterValue'], $filter);
971
            reset($data);
972
        } else {
973
            $this->filterValue($data, $name, $filter);
974
        }
975
976
        if (isset($type) && $data !== $default) {
977
            // 强制类型转换
978
            $this->typeCast($data, $type);
979
        }
980
        return $data;
981
    }
982
983
    /**
984
     * 设置或获取当前的过滤规则
985
     * @param mixed $filter 过滤规则
986
     * @return mixed
987
     */
988
    public function filter($filter = null)
989
    {
990
        if (is_null($filter)) {
991
            return $this->filter;
992
        } else {
993
            $this->filter = $filter;
994
        }
995
    }
996
997
    /**
998
     * 递归过滤给定的值
999
     * @param mixed     $value 键值
1000
     * @param mixed     $key 键名
1001
     * @param array     $filters 过滤方法+默认值
1002
     * @return mixed
1003
     */
1004
    private function filterValue(&$value, $key, $filters)
1005
    {
1006
        $default = array_pop($filters);
1007
        foreach ($filters as $filter) {
1008
            if (is_callable($filter)) {
1009
                // 调用函数或者方法过滤
1010
                $value = call_user_func($filter, $value);
1011
            } elseif (is_scalar($value)) {
1012
                if (strpos($filter, '/')) {
1013
                    // 正则过滤
1014
                    if (!preg_match($filter, $value)) {
1015
                        // 匹配不成功返回默认值
1016
                        $value = $default;
1017
                        break;
1018
                    }
1019
                } elseif (!empty($filter)) {
1020
                    // filter函数不存在时, 则使用filter_var进行过滤
1021
                    // filter为非整形值时, 调用filter_id取得过滤id
1022
                    $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
1023
                    if (false === $value) {
1024
                        $value = $default;
1025
                        break;
1026
                    }
1027
                }
1028
            }
1029
        }
1030
        return $this->filterExp($value);
1031
    }
1032
1033
    /**
1034
     * 过滤表单中的表达式
1035
     * @param string $value
1036
     * @return void
1037
     */
1038
    public function filterExp(&$value)
1039
    {
1040
        // 过滤查询特殊字符
1041
        if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) {
1042
            $value .= ' ';
1043
        }
1044
        // TODO 其他安全过滤
1045
    }
1046
1047
    /**
1048
     * 强制类型转换
1049
     * @param string $data
1050
     * @param string $type
1051
     * @return mixed
1052
     */
1053
    private function typeCast(&$data, $type)
1054
    {
1055
        switch (strtolower($type)) {
1056
            // 数组
1057
            case 'a':
1058
                $data = (array) $data;
1059
                break;
1060
            // 数字
1061
            case 'd':
1062
                $data = (int) $data;
1063
                break;
1064
            // 浮点
1065
            case 'f':
1066
                $data = (float) $data;
1067
                break;
1068
            // 布尔
1069
            case 'b':
1070
                $data = (boolean) $data;
1071
                break;
1072
            // 字符串
1073
            case 's':
1074
            default:
1075
                if (is_scalar($data)) {
1076
                    $data = (string) $data;
1077
                } else {
1078
                    throw new \InvalidArgumentException('variable type error:' . gettype($data));
1079
                }
1080
        }
1081
    }
1082
1083
    /**
1084
     * 是否存在某个请求参数
1085
     * @access public
1086
     * @param string    $name 变量名
1087
     * @param string    $type 变量类型
1088
     * @param bool      $checkEmpty 是否检测空值
1089
     * @return mixed
1090
     */
1091
    public function has($name, $type = 'param', $checkEmpty = false)
1092
    {
1093
        if (empty($this->$type)) {
1094
            $param = $this->$type();
1095
        } else {
1096
            $param = $this->$type;
1097
        }
1098
        // 按.拆分成多维数组进行判断
1099
        foreach (explode('.', $name) as $val) {
1100
            if (isset($param[$val])) {
1101
                $param = $param[$val];
1102
            } else {
1103
                return false;
1104
            }
1105
        }
1106
        return ($checkEmpty && '' === $param) ? false : true;
1107
    }
1108
1109
    /**
1110
     * 获取指定的参数
1111
     * @access public
1112
     * @param string|array  $name 变量名
1113
     * @param string        $type 变量类型
1114
     * @return mixed
1115
     */
1116
    public function only($name, $type = 'param')
1117
    {
1118
        $param = $this->$type();
1119
        if (is_string($name)) {
1120
            $name = explode(',', $name);
1121
        }
1122
        $item = [];
1123
        foreach ($name as $key) {
1124
            if (isset($param[$key])) {
1125
                $item[$key] = $param[$key];
1126
            }
1127
        }
1128
        return $item;
1129
    }
1130
1131
    /**
1132
     * 排除指定参数获取
1133
     * @access public
1134
     * @param string|array  $name 变量名
1135
     * @param string        $type 变量类型
1136
     * @return mixed
1137
     */
1138
    public function except($name, $type = 'param')
1139
    {
1140
        $param = $this->$type();
1141
        if (is_string($name)) {
1142
            $name = explode(',', $name);
1143
        }
1144
        foreach ($name as $key) {
1145
            if (isset($param[$key])) {
1146
                unset($param[$key]);
1147
            }
1148
        }
1149
        return $param;
1150
    }
1151
1152
    /**
1153
     * 当前是否ssl
1154
     * @access public
1155
     * @return bool
1156
     */
1157
    public function isSsl()
0 ignored issues
show
Coding Style introduced by
isSsl uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1158
    {
1159
        $server = array_merge($_SERVER, $this->server);
1160
        if (isset($server['HTTPS']) && ('1' == $server['HTTPS'] || 'on' == strtolower($server['HTTPS']))) {
1161
            return true;
1162
        } elseif (isset($server['REQUEST_SCHEME']) && 'https' == $server['REQUEST_SCHEME']) {
1163
            return true;
1164
        } elseif (isset($server['SERVER_PORT']) && ('443' == $server['SERVER_PORT'])) {
1165
            return true;
1166
        } elseif (isset($server['HTTP_X_FORWARDED_PROTO']) && 'https' == $server['HTTP_X_FORWARDED_PROTO']) {
1167
            return true;
1168
        }
1169
        return false;
1170
    }
1171
1172
    /**
1173
     * 当前是否Ajax请求
1174
     * @access public
1175
     * @param bool $ajax  true 获取原始ajax请求
1176
     * @return bool
1177
     */
1178
    public function isAjax($ajax = false)
1179
    {
1180
        $value  = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower');
1181
        $result = ('xmlhttprequest' == $value) ? true : false;
1182
        if (true === $ajax) {
1183
            return $result;
1184
        } else {
1185
            return $this->param($this->config->get('var_ajax')) ? true : $result;
1186
        }
1187
    }
1188
1189
    /**
1190
     * 当前是否Pjax请求
1191
     * @access public
1192
     * @param bool $pjax  true 获取原始pjax请求
1193
     * @return bool
1194
     */
1195
    public function isPjax($pjax = false)
1196
    {
1197
        $result = !is_null($this->server('HTTP_X_PJAX')) ? true : false;
1198
        if (true === $pjax) {
1199
            return $result;
1200
        } else {
1201
            return $this->param($this->config->get('var_pjax')) ? true : $result;
1202
        }
1203
    }
1204
1205
    /**
1206
     * 获取客户端IP地址
1207
     * @param integer   $type 返回类型 0 返回IP地址 1 返回IPV4地址数字
1208
     * @param boolean   $adv 是否进行高级模式获取(有可能被伪装)
1209
     * @return mixed
1210
     */
1211
    public function ip($type = 0, $adv = false)
0 ignored issues
show
Coding Style introduced by
ip uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1212
    {
1213
        $type      = $type ? 1 : 0;
1214
        static $ip = null;
1215
        if (null !== $ip) {
1216
            return $ip[$type];
1217
        }
1218
1219
        if ($adv) {
1220
            if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
1221
                $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
1222
                $pos = array_search('unknown', $arr);
1223
                if (false !== $pos) {
1224
                    unset($arr[$pos]);
1225
                }
1226
                $ip = trim(current($arr));
1227
            } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
1228
                $ip = $_SERVER['HTTP_CLIENT_IP'];
1229
            } elseif (isset($_SERVER['REMOTE_ADDR'])) {
1230
                $ip = $_SERVER['REMOTE_ADDR'];
1231
            }
1232
        } elseif (isset($_SERVER['REMOTE_ADDR'])) {
1233
            $ip = $_SERVER['REMOTE_ADDR'];
1234
        }
1235
        // IP地址合法验证
1236
        $long = sprintf("%u", ip2long($ip));
1237
        $ip   = $long ? [$ip, $long] : ['0.0.0.0', 0];
1238
        return $ip[$type];
1239
    }
1240
1241
    /**
1242
     * 检测是否使用手机访问
1243
     * @access public
1244
     * @return bool
1245
     */
1246
    public function isMobile()
0 ignored issues
show
Coding Style introduced by
isMobile uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1247
    {
1248
        if (isset($_SERVER['HTTP_VIA']) && stristr($_SERVER['HTTP_VIA'], "wap")) {
1249
            return true;
1250
        } elseif (isset($_SERVER['HTTP_ACCEPT']) && strpos(strtoupper($_SERVER['HTTP_ACCEPT']), "VND.WAP.WML")) {
1251
            return true;
1252
        } elseif (isset($_SERVER['HTTP_X_WAP_PROFILE']) || isset($_SERVER['HTTP_PROFILE'])) {
1253
            return true;
1254
        } elseif (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/(blackberry|configuration\/cldc|hp |hp-|htc |htc_|htc-|iemobile|kindle|midp|mmp|motorola|mobile|nokia|opera mini|opera |Googlebot-Mobile|YahooSeeker\/M1A1-R2D2|android|iphone|ipod|mobi|palm|palmos|pocket|portalmmm|ppc;|smartphone|sonyericsson|sqh|spv|symbian|treo|up.browser|up.link|vodafone|windows ce|xda |xda_)/i', $_SERVER['HTTP_USER_AGENT'])) {
1255
            return true;
1256
        } else {
1257
            return false;
1258
        }
1259
    }
1260
1261
    /**
1262
     * 当前URL地址中的scheme参数
1263
     * @access public
1264
     * @return string
1265
     */
1266
    public function scheme()
1267
    {
1268
        return $this->isSsl() ? 'https' : 'http';
1269
    }
1270
1271
    /**
1272
     * 当前请求URL地址中的query参数
1273
     * @access public
1274
     * @return string
1275
     */
1276
    public function query()
1277
    {
1278
        return $this->server('QUERY_STRING');
1279
    }
1280
1281
    /**
1282
     * 当前请求的host
1283
     * @access public
1284
     * @return string
1285
     */
1286
    public function host()
1287
    {
1288
        return $this->server('HTTP_HOST');
1289
    }
1290
1291
    /**
1292
     * 当前请求URL地址中的port参数
1293
     * @access public
1294
     * @return integer
1295
     */
1296
    public function port()
1297
    {
1298
        return $this->server('SERVER_PORT');
1299
    }
1300
1301
    /**
1302
     * 当前请求 SERVER_PROTOCOL
1303
     * @access public
1304
     * @return integer
1305
     */
1306
    public function protocol()
1307
    {
1308
        return $this->server('SERVER_PROTOCOL');
1309
    }
1310
1311
    /**
1312
     * 当前请求 REMOTE_PORT
1313
     * @access public
1314
     * @return integer
1315
     */
1316
    public function remotePort()
1317
    {
1318
        return $this->server('REMOTE_PORT');
1319
    }
1320
1321
    /**
1322
     * 当前请求 HTTP_CONTENT_TYPE
1323
     * @access public
1324
     * @return string
1325
     */
1326
    public function contentType()
1327
    {
1328
        $contentType = $this->server('CONTENT_TYPE');
1329
        if ($contentType) {
1330
            if (strpos($contentType, ';')) {
1331
                list($type) = explode(';', $contentType);
1332
            } else {
1333
                $type = $contentType;
1334
            }
1335
            return trim($type);
1336
        }
1337
        return '';
1338
    }
1339
1340
    /**
1341
     * 获取当前请求的路由信息
1342
     * @access public
1343
     * @param array $route 路由名称
1344
     * @return array
1345
     */
1346
    public function routeInfo($route = [])
1347
    {
1348
        if (!empty($route)) {
1349
            $this->routeInfo = $route;
1350
        } else {
1351
            return $this->routeInfo;
1352
        }
1353
    }
1354
1355
    /**
1356
     * 设置或者获取当前请求的调度信息
1357
     * @access public
1358
     * @param array  $dispatch 调度信息
1359
     * @return array
1360
     */
1361
    public function dispatch($dispatch = null)
1362
    {
1363
        if (!is_null($dispatch)) {
1364
            $this->dispatch = $dispatch;
1365
        }
1366
        return $this->dispatch;
1367
    }
1368
1369
    /**
1370
     * 设置或者获取当前的模块名
1371
     * @access public
1372
     * @param string $module 模块名
1373
     * @return string|Request
1374
     */
1375
    public function module($module = null)
1376
    {
1377
        if (!is_null($module)) {
1378
            $this->module = $module;
1379
            return $this;
1380
        } else {
1381
            return $this->module ?: '';
1382
        }
1383
    }
1384
1385
    /**
1386
     * 设置或者获取当前的控制器名
1387
     * @access public
1388
     * @param string $controller 控制器名
1389
     * @return string|Request
1390
     */
1391
    public function controller($controller = null)
1392
    {
1393
        if (!is_null($controller)) {
1394
            $this->controller = $controller;
1395
            return $this;
1396
        } else {
1397
            return $this->controller ?: '';
1398
        }
1399
    }
1400
1401
    /**
1402
     * 设置或者获取当前的操作名
1403
     * @access public
1404
     * @param string $action 操作名
1405
     * @return string|Request
1406
     */
1407
    public function action($action = null)
1408
    {
1409
        if (!is_null($action)) {
1410
            $this->action = $action;
1411
            return $this;
1412
        } else {
1413
            return $this->action ?: '';
1414
        }
1415
    }
1416
1417
    /**
1418
     * 设置或者获取当前的语言
1419
     * @access public
1420
     * @param string $lang 语言名
1421
     * @return string|Request
1422
     */
1423
    public function langset($lang = null)
1424
    {
1425
        if (!is_null($lang)) {
1426
            $this->langset = $lang;
1427
            return $this;
1428
        } else {
1429
            return $this->langset ?: '';
1430
        }
1431
    }
1432
1433
    /**
1434
     * 设置或者获取当前请求的content
1435
     * @access public
1436
     * @return string
1437
     */
1438
    public function getContent()
1439
    {
1440
        if (is_null($this->content)) {
1441
            $this->content = $this->input;
1442
        }
1443
        return $this->content;
1444
    }
1445
1446
    /**
1447
     * 获取当前请求的php://input
1448
     * @access public
1449
     * @return string
1450
     */
1451
    public function getInput()
1452
    {
1453
        return $this->input;
1454
    }
1455
1456
    /**
1457
     * 生成请求令牌
1458
     * @access public
1459
     * @param string $name 令牌名称
1460
     * @param mixed  $type 令牌生成方法
1461
     * @return string
1462
     */
1463
    public function token($name = '__token__', $type = 'md5')
0 ignored issues
show
Coding Style introduced by
token uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1464
    {
1465
        $type  = is_callable($type) ? $type : 'md5';
1466
        $token = call_user_func($type, $_SERVER['REQUEST_TIME_FLOAT']);
1467
        if ($this->isAjax()) {
1468
            header($name . ': ' . $token);
1469
        }
1470
        Session::set($name, $token);
1471
        return $token;
1472
    }
1473
1474
    /**
1475
     * 设置当前地址的请求缓存
1476
     * @access public
1477
     * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
1478
     * @param mixed  $expire 缓存有效期
1479
     * @param array  $except 缓存排除
1480
     * @return void
1481
     */
1482
    public function cache($key, $expire = null, $except = [])
0 ignored issues
show
Coding Style introduced by
cache uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
1483
    {
1484
        if (false !== $key && $this->isGet() && !$this->isCheckCache) {
1485
            // 标记请求缓存检查
1486
            $this->isCheckCache = true;
1487
            if (false === $expire) {
1488
                // 关闭当前缓存
1489
                return;
1490
            }
1491
            if ($key instanceof \Closure) {
1492
                $key = call_user_func_array($key, [$this]);
1493
            } elseif (true === $key) {
1494
                foreach ($except as $rule) {
1495
                    if (0 === strpos($this->url(), $rule)) {
1496
                        return;
1497
                    }
1498
                }
1499
                // 自动缓存功能
1500
                $key = md5($this->host()) . '__URL__';
1501
            } elseif (strpos($key, '|')) {
1502
                list($key, $fun) = explode('|', $key);
1503
            }
1504
            // 特殊规则替换
1505
            if (false !== strpos($key, '__')) {
1506
                $key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__', ''], [$this->module, $this->controller, $this->action, md5($this->url())], $key);
1507
            }
1508
1509
            if (false !== strpos($key, ':')) {
1510
                $param = $this->param();
1511
                foreach ($param as $item => $val) {
1512
                    if (is_string($val) && false !== strpos($key, ':' . $item)) {
1513
                        $key = str_replace(':' . $item, $val, $key);
1514
                    }
1515
                }
1516
            } elseif (strpos($key, ']')) {
1517
                if ('[' . $this->ext() . ']' == $key) {
1518
                    // 缓存某个后缀的请求
1519
                    $key = md5($this->url());
1520
                } else {
1521
                    return;
1522
                }
1523
            }
1524
            if (isset($fun)) {
1525
                $key = $fun($key);
1526
            }
1527
1528
            if (strtotime($this->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $_SERVER['REQUEST_TIME']) {
1529
                // 读取缓存
1530
                $response = Response::create()->code(304);
1531
                throw new \think\exception\HttpResponseException($response);
1532
            } elseif (Cache::has($key)) {
1533
                list($content, $header) = Cache::get($key);
1534
                $response               = Response::create($content)->header($header);
1535
                throw new \think\exception\HttpResponseException($response);
1536
            } else {
1537
                $this->cache = [$key, $expire];
1538
            }
1539
        }
1540
    }
1541
1542
    /**
1543
     * 读取请求缓存设置
1544
     * @access public
1545
     * @return array
1546
     */
1547
    public function getCache()
1548
    {
1549
        return $this->cache;
1550
    }
1551
1552
    /**
1553
     * 设置当前请求绑定的对象实例
1554
     * @access public
1555
     * @param string $name 绑定的对象标识
1556
     * @param mixed  $obj 绑定的对象实例
1557
     * @return mixed
1558
     */
1559
    public function bind($name, $obj = null)
1560
    {
1561
        if (is_array($name)) {
1562
            $this->bind = array_merge($this->bind, $name);
1563
        } else {
1564
            $this->bind[$name] = $obj;
1565
        }
1566
    }
1567
1568
    public function getBind($name)
1569
    {
1570
        return isset($this->bind[$name]) ? $this->bind[$name] : null;
1571
    }
1572
1573
    public function __set($name, $value)
1574
    {
1575
        $this->bind[$name] = $value;
1576
    }
1577
1578
    public function __get($name)
1579
    {
1580
        return isset($this->bind[$name]) ? $this->bind[$name] : null;
1581
    }
1582
1583
    public function __isset($name)
1584
    {
1585
        return isset($this->bind[$name]);
1586
    }
1587
}