Completed
Push — 9.0-dev ( 4472bc...95a6ae )
by Radu
05:24
created

Request::guessAppUrl()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 7
nc 2
nop 0
dl 0
loc 10
rs 9.4285
c 1
b 1
f 0
1
<?php
2
namespace WebServCo\Framework\Libraries;
3
4
final class Request extends \WebServCo\Framework\AbstractLibrary
5
{
6
    /**
7
     * Sanitized _SERVER data.
8
     */
9
    public $server = [];
10
    /**
11
     * Request method.
12
     */
13
    public $method;
14
    /**
15
     * Current script filename. Should most commonly be index.php
16
     */
17
    public $filename;
18
    /**
19
     * Script path.
20
     * For HTTP requests this will be public web server subdirectory
21
     * the project is located in.
22
     * For CLI request this will be the script path
23
     * (full or relative, depending on how the script was called).
24
     */
25
    public $path = '';
26
    /**
27
     * Sanitized Framework customized target path.
28
     */
29
    public $target = '';
30
    /**
31
     * Sanitized request query.
32
     */
33
    public $query = [];
34
    
35
    public function __construct($config, $server, $post = [])
36
    {
37
        parent::__construct($config);
38
        
39
        $this->init($server, $post);
40
    }
41
    
42
    private function init($server, $post = [])
43
    {
44
        $this->server = $this->sanitize($server);
45
        $this->method = $this->getMethod();
46
        $this->filename = $this->getFilename();
47
        $this->path = $this->getPath();
48
        $this->process();
49
        
50
        switch ($this->method) {
51
            case \WebServCo\Framework\Http::METHOD_GET:
52
            case \WebServCo\Framework\Http::METHOD_HEAD:
53
                break;
54
            case \WebServCo\Framework\Http::METHOD_POST:
55
                $this->processPost($post);
56
                break;
57
        }
58
        if ($this->setting('clear_globals', true)) {
59
            $this->clearGlobals();
60
        }
61
    }
62
    
63
    private function clearGlobals()
64
    {
65
        if (!empty($_GET)) {
66
            foreach ($_GET as $k => $v) {
67
                unset($_REQUEST[$k]);
68
            }
69
            $_GET = [];
70
        }
71
        if (!empty($_POST)) {
72
            $_POST = [];
73
        }
74
        return true;
75
    }
76
    
77
    private function processPost($post = [])
78
    {
79
        $this->data = [];
80
        foreach ($post as $k => $v) {
81
            $this->data[$this->sanitize($k)] = $v;
82
        }
83
        return true;
84
    }
85
    
86
    private function process()
87
    {
88
        $string = null;
89
        switch (true) {
90
            case isset($this->server['REQUEST_URI']):
91
                $string = $this->server['REQUEST_URI'];
92
                break;
93
            case isset($this->server['PATH_INFO']):
94
                $string = $this->server['PATH_INFO'];
95
                break;
96
            case isset($this->server['ORIG_PATH_INFO']):
97
                $string = $this->server['ORIG_PATH_INFO'];
98
                break;
99
            case !empty($this->server['QUERY_STRING']):
100
                $string = $this->server['ORIG_PATH_INFO'];
101
                break;
102
            default:
103
                break;
104
        }
105
        if (empty($string)) {
0 ignored issues
show
introduced by
The condition empty($string) can never be true.
Loading history...
106
            return false; //CLI
107
        }
108
        list ($target, $queryString) = $this->parse($string);
109
        $this->target = $this->sanitize(urldecode($target));
110
        $this->query = $this->format($this->sanitize($queryString));
111
    }
112
    
113
    private function getMethod()
114
    {
115
        return !empty($this->server['REQUEST_METHOD']) &&
116
        in_array(
117
            $this->server['REQUEST_METHOD'],
118
            \WebServCo\Framework\Http::getMethods()
119
        ) ?
120
        $this->server['REQUEST_METHOD'] : false;
121
    }
122
    
123
    private function getFilename()
124
    {
125
        return !empty($this->server['SCRIPT_NAME']) ?
126
        basename($this->server['SCRIPT_NAME']) : false;
127
    }
128
    
129
    private function getPath()
130
    {
131
        if (empty($this->server['SCRIPT_NAME'])) {
132
            return false;
133
        }
134
        $path = str_replace($this->getFilename(), '', $this->server['SCRIPT_NAME']);
0 ignored issues
show
Bug introduced by
It seems like $this->getFilename() can also be of type false; however, parameter $search of str_replace() does only seem to accept string|string[], maybe add an additional type check? ( Ignorable by Annotation )

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

134
        $path = str_replace(/** @scrutinizer ignore-type */ $this->getFilename(), '', $this->server['SCRIPT_NAME']);
Loading history...
135
        return rtrim($path, DIRECTORY_SEPARATOR);
136
    }
137
    
138
    public function sanitize($data)
139
    {
140
        if (is_array($data)) {
141
            array_walk_recursive($data, [$this, 'sanitizeString']);
142
            return $data;
143
        }
144
        return $this->sanitizeString($data);
145
    }
146
    
147
    protected function sanitizeString($string)
148
    {
149
        // Strip tags, optionally strip or encode special characters.
150
        $string = filter_var($string, FILTER_SANITIZE_STRING);
151
        $unwanted = [
152
            "`",
153
            //"'",
154
            //'"',
155
            "\b",
156
            "\n",
157
            "\r",
158
            "\t",
159
            //"?",
160
            //"!",
161
            //"~",
162
            //"#",
163
            //"^",
164
            //"&",
165
            //"*",
166
            //"=",
167
            //"[",
168
            //"]",
169
            //":",
170
            //";",
171
            //",",
172
            //"|",
173
            "\\",
174
            //"{",
175
            //"}",
176
            //"(",
177
            //")",
178
            "\$"
179
        ];
180
        $string = str_replace($unwanted, '', $string);
181
        return $string;
182
    }
183
    
184
    private function parse($string)
185
    {
186
        $pathLen = strlen($this->path);
187
        if (0 === strncasecmp($this->path, $string, $pathLen)) {
188
            $string = substr($string, $pathLen);
189
        }
190
        $filenameLen = strlen($this->filename);
191
        if (0 === strncasecmp($this->filename, $string, $filenameLen)) {
192
            $string = substr($string, $filenameLen);
193
        }
194
        list($target, $query) = $this->explode($string);
195
        $target = $this->removeSuffix($this->transform($target));
196
        $query = $this->transform($query);
197
        return [$target, $query];
198
    }
199
    
200
    private function explode($string)
201
    {
202
        if (false !== strpos($string, '?')) {
203
            return explode('?', $string, 2);
204
        } elseif (false !== strpos($string, '&')) {
205
            return explode('&', $string, 2);
206
        }
207
        return [$string, null];
208
    }
209
    
210
    private function transform($string)
211
    {
212
        $string = str_replace(['?','&','=','//'], ['','/','/','/0/'], $string);
213
        return trim($string, ' /');
214
    }
215
    
216
    private function removeSuffix($string)
217
    {
218
        $suffixes = $this->setting('suffixes');
219
        if (is_array($suffixes)) {
220
            $stringRev = strrev($string);
221
            foreach ($suffixes as $suffix) {
222
                $suffixRev = strrev($suffix);
223
                $suffixLen = strlen($suffix);
224
                if (0 === strncasecmp($suffixRev, $stringRev, $suffixLen)) {
225
                    return strrev(substr($stringRev, $suffixLen));
226
                }
227
            }
228
        }
229
        return $string;
230
    }
231
    
232
    private function format($string)
233
    {
234
        $data = [];
235
        $parts = $this->split($string);
236
        $num = count($parts);
237
        for ($position = 0; $position < $num; $position +=2) {
238
            $data[$parts[$position]] = $position == $num -1 ? null :
239
            $parts[$position + 1];
240
        }
241
        return $data;
242
    }
243
    
244
    public function split($string)
245
    {
246
        $parts = explode('/', $string);
247
        $parts = array_map('urldecode', $parts);
248
        return array_diff($parts, ['']);
249
    }
250
    
251
    public function getSchema()
252
    {
253
        if (\WebServCo\Framework\Framework::isCLI()) {
254
            return null;
255
        }
256
        
257
        if (isset($this->server['HTTPS']) && 'off' != $this->server['HTTPS']) {
258
            return 'https';
259
        } elseif (isset($this->server['HTTP_X_FORWARDED_PROTO']) &&
260
        'https' == $this->server['HTTP_X_FORWARDED_PROTO']) {
261
            return 'https';
262
        } elseif (isset($this->server['HTTP_X_FORWARDED_SSL']) &&
263
        'on' == $this->server['HTTP_X_FORWARDED_SSL']) {
264
            return 'https';
265
        }
266
        return 'http';
267
    }
268
    
269
    public function getReferer()
270
    {
271
        return isset($this->server['HTTP_REFERER']) ? $this->server['HTTP_REFERER'] : null;
272
    }
273
    
274
    public function getHost()
275
    {
276
        if (!empty($this->server['HTTP_HOST'])) {
277
            return $this->server['HTTP_HOST'];
278
        } elseif (!empty($this->server['SERVER_NAME'])) {
279
            return $this->server['SERVER_NAME'];
280
        } elseif (!empty($this->server['HOSTNAME'])) {
281
            return $this->server['HOSTNAME']; //CLI
282
        }
283
        return null;
284
    }
285
    
286
    public function guessAppUrl()
287
    {
288
        if (\WebServCo\Framework\Framework::isCLI()) {
289
            return false;
290
        }
291
        return $this->getSchema() .
292
        '://' .
293
        $this->getHost() .
294
        $this->path .
295
        DIRECTORY_SEPARATOR;
296
    }
297
}
298