Completed
Push — master ( 800138...f42006 )
by Matthias
02:36
created

RequestManager::_getOperatingSystem()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 12
rs 9.4285
cc 3
eloc 8
nc 3
nop 0
1
<?php
2
/*
3
 * The MIT License (MIT)
4
 *
5
 * Copyright (c) 2015 zepi
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
 * THE SOFTWARE.
24
 *
25
 */
26
27
/**
28
 * The RequestManager parses the request params and creates
29
 * the Request object for the input data.
30
 * 
31
 * @package Zepi\Turbo\Manager
32
 * @author Matthias Zobrist <[email protected]>
33
 * @copyright Copyright (c) 2015 zepi
34
 */
35
36
namespace Zepi\Turbo\Manager;
37
38
use \Zepi\Turbo\Framework;
39
use \Zepi\Turbo\Request\CliRequest;
40
use \Zepi\Turbo\Request\WebRequest;
41
use Zepi\Turbo\Request\RequestAbstract;
42
43
/**
44
 * The RequestManager parses the request params and creates
45
 * the Request object for the input data.
46
 * 
47
 * @author Matthias Zobrist <[email protected]>
48
 * @copyright Copyright (c) 2015 zepi
49
 */
50
class RequestManager
51
{
52
    /**
53
     * @access protected
54
     * @var Framework
55
     */
56
    protected $_framework;
57
    
58
    /**
59
     * Constructs the object
60
     * 
61
     * @access public
62
     * @param \Zepi\Turbo\Framework $framework
63
     */
64
    public function __construct(Framework $framework)
65
    {
66
        $this->_framework = $framework;
67
    }
68
    
69
    /**
70
     * Builds the RequestAbstract object for the given input
71
     * data.
72
     * 
73
     * @access public
74
     */
75
    public function buildRequest()
76
    {
77
        if (php_sapi_name() === 'cli') {
78
            return $this->_buildCliRequest();
79
        } else {
80
            return $this->_buildWebRequest();
81
        }
82
    }
83
    
84
    /**
85
     * Builds the cli request object
86
     * 
87
     * @access protected
88
     * @return \Zepi\Turbo\Request\CliRequest
89
     */
90
    protected function _buildCliRequest()
0 ignored issues
show
Coding Style introduced by
_buildCliRequest 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...
91
    {
92
        global $argv;
1 ignored issue
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
93
        
94
        $args = $argv;
95
        $params = array();
96
        $route = '';
97
        
98
        foreach ($args as $arg) {
99
            if ($arg === $_SERVER['PHP_SELF']) {
100
                continue;
101
            }
102
            
103
            if (strpos($arg, '-') === 0) {
104
                $arg = ltrim($arg, '-');
105
                
106
                $key = $arg;
107
                $value = true;
108
                
109
                if (strpos($arg, '=') !== false) {
110
                    $key = substr($arg, 0, strpos($arg, '='));
111
                    $value = substr($arg, strpos($arg, '=') + 1);
112
                    
113
                    if (is_numeric($value)) {
114
                        // Transform the value into the correct data type
115
                        $value = $value * 1;
116
                    }
117
                }
118
                
119
                $params[$key] = $value;
120
            } else {
121
                if ($route !== '') {
122
                    $route .= ' ';
123
                }
124
                
125
                $route .= $arg;
126
            }
127
        }
128
        
129
        $base = $argv[0];
130
        
131
        $operatingSystem = $this->_getOperatingSystem();
132
        
133
        return new CliRequest($route, $params, $base, 'en_US', $operatingSystem);
134
    }
135
136
    /**
137
     * Builds the html request object
138
     * 
139
     * @access protected
140
     * @return \Zepi\Turbo\Request\WebRequest
141
     */
142
    protected function _buildWebRequest()
0 ignored issues
show
Coding Style introduced by
_buildWebRequest 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...
Coding Style introduced by
_buildWebRequest 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...
143
    {
144
        $args = $_REQUEST;
145
        $params = array();
146
147
        $route = $_SERVER['REQUEST_URI'];
148
        $posQuestionMark = strpos($route, '?');
149
        if ($posQuestionMark !== false) {
150
            $route = substr($route, 0, $posQuestionMark);
151
        }
152
        
153
        $posIndex = strpos($route, 'index.php');
154
        if ($posIndex !== false) {
155
            $route = substr($route, $posIndex + strlen('index.php'));
156
        }
157
        
158
        // Transform the arguments
159
        foreach ($args as $key => $value) {
160
            if (is_numeric($value)) {
161
                // Transform the value into the correct data type
162
                $value = $value * 1;
163
            }
164
            
165
            $params[$key] = $value;
166
        }
167
168
        // Generate the full url and extract the base
169
        $scheme = $this->_getScheme();
170
        $fullUrl = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
171
172
        $isSsl = false;
173
        if ($scheme == 'https') {
174
            $isSsl = true;
175
        }
176
        
177
        $routePosition = strlen($fullUrl);
178
        if ($route !== '' && $route !== '/') {
179
            $routePosition = strpos($fullUrl, $route);
180
        }
181
        
182
        $method = $_SERVER['REQUEST_METHOD'];
183
        $requestedUrl = $this->_getRequestedUrl();
184
        $base = substr($fullUrl, 0, $routePosition);
185
        $headers = $this->_getHeaders($_SERVER);
186
        $protocol = $_SERVER['SERVER_PROTOCOL'];
187
        
188
        $locale = 'en_US';
189
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
190
            $locale = $this->_getLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
191
        }
192
        
193
        $operatingSystem = $this->_getOperatingSystem();
194
195
        return new WebRequest($method, $requestedUrl, $route, $params, $base, $locale, $operatingSystem, $isSsl, $headers, $protocol);
196
    }
197
    
198
    /**
199
     * Returns the name of the operating system
200
     * 
201
     * @access protected
202
     * @return string
203
     */
204
    protected function _getOperatingSystem()
205
    {
206
        $osRaw = strtolower(PHP_OS);
207
        
208
        if (strpos($osRaw, 'linux') !== false) {
209
            return RequestAbstract::OS_LINUX;
210
        } else if (strpos($osRaw, 'windows') !== false) {
211
            return RequestAbstract::OS_WINDOWS;
212
        } else {
213
            return RequestAbstract::OS_UNKNOWN;
214
        }
215
    }
216
    
217
    /**
218
     * Returns the scheme of the request
219
     * 
220
     * @return string
221
     */
222
    protected function _getScheme()
0 ignored issues
show
Coding Style introduced by
_getScheme 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...
223
    {
224
        $scheme = '';
0 ignored issues
show
Unused Code introduced by
$scheme is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
225
        if (isset($_SERVER['REQUEST_SCHEME'])) {
226
            $scheme = $_SERVER['REQUEST_SCHEME'];
227
        } else if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']) {
228
            $scheme = 'https';
229
        } else {
230
            if ($_SERVER['SERVER_PORT'] == 443) {
231
                $scheme = 'https';
232
            } else {
233
                $scheme = 'http';
234
            }
235
        }
236
        
237
        return $scheme;
238
    }
239
    
240
    /**
241
     * Returns the requested url
242
     * 
243
     * @access protected
244
     * @return string
245
     */
246
    protected function _getRequestedUrl()
0 ignored issues
show
Coding Style introduced by
_getRequestedUrl 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...
247
    {
248
        $scheme = $this->_getScheme();
249
250
        return $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
251
    }
252
253
    /**
254
     * Returns an array with all headers of this request
255
     * 
256
     * @access protected
257
     * @param array $params
258
     * @return array
259
     */
260
    protected function _getHeaders($params)
261
    {
262
        $headers = array();
263
        
264
        foreach ($params as $name => $value) {
265
            if (substr($name, 0, 5) === 'HTTP_') {
266
                $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
267
                $headers[$key] = $value;
268
            }
269
        }
270
        
271
        return $headers;
272
    }
273
    
274
    /**
275
     * Returns the best acceptable locale from the language header.
276
     * 
277
     * @access protected
278
     * @param string $acceptLanguageHeader
279
     * @return string
280
     */
281
    protected function _getLocale($acceptLanguageHeader)
282
    {
283
        $acceptLanguageHeader = str_replace('-', '_', $acceptLanguageHeader);
284
        $locales = explode(',', $acceptLanguageHeader);
285
        
286
        $acceptableLocales = array();
287
        foreach ($locales as $locale) {
288
            $priority = 1;
289
            if (strpos($locale, ';') !== false) {
290
                $priority = floatval(substr($locale, strpos($locale, ';')));
291
                $locale = substr($locale, 0, strpos($locale, ';'));
292
            }
293
            
294
            $acceptableLocales[$priority] = $locale;
295
        }
296
        
297
        krsort($acceptableLocales);
298
        
299
        // Get the first locale - it will have the highest priority
300
        $locale = array_shift($acceptableLocales);
301
        
302
        if ($locale == '') {
303
            $locale = 'en_US';
304
        } else if (strpos($locale, '_') === false) {
305
            $locale = $locale . '_' . strtoupper($locale);
306
        }
307
        
308
        return $locale;
309
    }
310
}
311