Completed
Push — master ( eda34b...67914c )
by Matthias
02:34
created

RequestManager::_getRequestedUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 1
Metric Value
c 1
b 1
f 1
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
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
42
/**
43
 * The RequestManager parses the request params and creates
44
 * the Request object for the input data.
45
 * 
46
 * @author Matthias Zobrist <[email protected]>
47
 * @copyright Copyright (c) 2015 zepi
48
 */
49
class RequestManager
50
{
51
    /**
52
     * @access protected
53
     * @var Framework
54
     */
55
    protected $_framework;
56
    
57
    /**
58
     * Constructs the object
59
     * 
60
     * @access public
61
     * @param \Zepi\Turbo\Framework $framework
62
     */
63
    public function __construct(Framework $framework)
64
    {
65
        $this->_framework = $framework;
66
    }
67
    
68
    /**
69
     * Builds the RequestAbstract object for the given input
70
     * data.
71
     * 
72
     * @access public
73
     */
74
    public function buildRequest()
75
    {
76
        if (php_sapi_name() === 'cli') {
77
            return $this->_buildCliRequest();
78
        } else {
79
            return $this->_buildWebRequest();
80
        }
81
    }
82
    
83
    /**
84
     * Builds the cli request object
85
     * 
86
     * @access protected
87
     * @return \Zepi\Turbo\Request\CliRequest
88
     */
89
    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...
90
    {
91
        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...
92
        
93
        $args = $argv;
94
        $params = array();
95
        $route = '';
96
        
97
        foreach ($args as $arg) {
98
            if ($arg === $_SERVER['PHP_SELF']) {
99
                continue;
100
            }
101
            
102
            if (strpos($arg, '-') === 0) {
103
                $arg = ltrim($arg, '-');
104
                
105
                $key = $arg;
106
                $value = true;
107
                
108
                if (strpos($arg, '=') !== false) {
109
                    $key = substr($arg, 0, strpos($arg, '='));
110
                    $value = substr($arg, strpos($arg, '=') + 1);
111
                    
112
                    if (is_numeric($value)) {
113
                        // Transform the value into the correct data type
114
                        $value = $value * 1;
115
                    }
116
                }
117
                
118
                $params[$key] = $value;
119
            } else {
120
                if ($route !== '') {
121
                    $route .= ' ';
122
                }
123
                
124
                $route .= $arg;
125
            }
126
        }
127
        
128
        $base = $argv[0];
129
        
130
        return new CliRequest($route, $params, $base, 'en_US');
131
    }
132
133
    /**
134
     * Builds the html request object
135
     * 
136
     * @access protected
137
     * @return \Zepi\Turbo\Request\WebRequest
138
     */
139
    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...
140
    {
141
        $args = $_REQUEST;
142
        $params = array();
143
144
        $route = $_SERVER['REQUEST_URI'];
145
        $posQuestionMark = strpos($route, '?');
146
        if ($posQuestionMark !== false) {
147
            $route = substr($route, 0, $posQuestionMark);
148
        }
149
        
150
        $posIndex = strpos($route, 'index.php');
151
        if ($posIndex !== false) {
152
            $route = substr($route, $posIndex + strlen('index.php'));
153
        }
154
        
155
        // Transform the arguments
156
        foreach ($args as $key => $value) {
157
            if (is_numeric($value)) {
158
                // Transform the value into the correct data type
159
                $value = $value * 1;
160
            }
161
            
162
            $params[$key] = $value;
163
        }
164
165
        // Get the protocol
166
        $proto = 'http';
167
        $isSsl = false;
168
        if (isset($_SERVER['HTTPS'])) {
169
            $proto = 'https';
170
            $isSsl = true;
171
        }
172
173
        // Generate the full url and extract the base
174
        $fullUrl = $proto . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
175
        
176
        $routePosition = strlen($fullUrl);
177
        if ($route !== '') {
178
            $routePosition = strpos($fullUrl, $route);
179
        }
180
181
        $requestedUrl = $this->_getRequestedUrl();
182
        $base = substr($fullUrl, 0, $routePosition);
183
        $headers = $this->_getHeaders($_SERVER);
184
        $protocol = $_SERVER['SERVER_PROTOCOL'];
185
        
186
        $locale = 'en_US';
187
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
188
            $locale = $this->_getLocale($_SERVER['HTTP_ACCEPT_LANGUAGE']);
189
        }
190
191
        return new WebRequest($requestedUrl, $route, $params, $base, $locale, $isSsl, $headers, $protocol);
192
    }
193
    
194
    /**
195
     * Returns the requested url
196
     * 
197
     * @access protected
198
     * @return string
199
     */
200
    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...
201
    {
202
        return $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
203
    }
204
205
    /**
206
     * Returns an array with all headers of this request
207
     * 
208
     * @access protected
209
     * @param array $params
210
     * @return array
211
     */
212
    protected function _getHeaders($params)
213
    {
214
        $headers = array();
215
        
216
        foreach ($params as $name => $value) {
217
            if (substr($name, 0, 5) === 'HTTP_') {
218
                $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
219
                $headers[$key] = $value;
220
            }
221
        }
222
        
223
        return $headers;
224
    }
225
    
226
    /**
227
     * Returns the best acceptable locale from the language header.
228
     * 
229
     * @access protected
230
     * @param string $acceptLanguageHeader
231
     * @return string
232
     */
233
    protected function _getLocale($acceptLanguageHeader)
234
    {
235
        $acceptLanguageHeader = str_replace('-', '_', $acceptLanguageHeader);
236
        $locales = explode(',', $acceptLanguageHeader);
237
        
238
        $acceptableLocales = array();
239
        foreach ($locales as $locale) {
240
            $priority = 1;
241
            if (strpos($locale, ';') !== false) {
242
                $priority = floatval(substr($locale, strpos($locale, ';')));
243
                $locale = substr($locale, 0, strpos($locale, ';'));
244
            }
245
            
246
            $acceptableLocales[$priority] = $locale;
247
        }
248
        
249
        krsort($acceptableLocales);
250
        
251
        // Get the first locale - it will have the highest priority
252
        $locale = array_shift($acceptableLocales);
253
        
254
        if ($locale == '') {
255
            $locale = 'en_US';
256
        } else if (strpos($locale, '_') === false) {
257
            $locale = $locale . '_' . strtoupper($locale);
258
        }
259
        
260
        return $locale;
261
    }
262
}
263