Passed
Push — master ( db429d...823adb )
by El
03:24
created

Request::__construct()   D

Complexity

Conditions 19
Paths 40

Size

Total Lines 42
Code Lines 29

Duplication

Lines 12
Ratio 28.57 %

Code Coverage

Tests 26
CRAP Score 19

Importance

Changes 0
Metric Value
dl 12
loc 42
ccs 26
cts 26
cp 1
rs 4.9141
c 0
b 0
f 0
cc 19
eloc 29
nc 40
nop 0
crap 19

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
 * PrivateBin
4
 *
5
 * a zero-knowledge paste bin
6
 *
7
 * @link      https://github.com/PrivateBin/PrivateBin
8
 * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
9
 * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
10
 * @version   1.1
11
 */
12
13
namespace PrivateBin;
14
15
/**
16
 * Request
17
 *
18
 * parses request parameters and provides helper functions for routing
19
 */
20
class Request
21
{
22
    /**
23
     * MIME type for JSON
24
     *
25
     * @const string
26
     */
27
    const MIME_JSON = 'application/json';
28
29
    /**
30
     * MIME type for HTML
31
     *
32
     * @const string
33
     */
34
    const MIME_HTML = 'text/html';
35
36
    /**
37
     * MIME type for XHTML
38
     *
39
     * @const string
40
     */
41
    const MIME_XHTML = 'application/xhtml+xml';
42
43
    /**
44
     * Input stream to use for PUT parameter parsing.
45
     *
46
     * @access private
47
     * @var string
48
     */
49
    private static $_inputStream = 'php://input';
50
51
    /**
52
     * Operation to perform.
53
     *
54
     * @access private
55
     * @var string
56
     */
57
    private $_operation = 'view';
58
59
    /**
60
     * Request parameters.
61
     *
62
     * @access private
63
     * @var array
64
     */
65
    private $_params = array();
66
67
    /**
68
     * If we are in a JSON API context.
69
     *
70
     * @access private
71
     * @var bool
72
     */
73
    private $_isJsonApi = false;
74
75
    /**
76
     * Constructor.
77
     *
78
     * @access public
79
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
80
     */
81 105
    public function __construct()
0 ignored issues
show
Coding Style introduced by
__construct 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...
Coding Style introduced by
__construct 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...
Coding Style introduced by
__construct 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...
82
    {
83
        // decide if we are in JSON API or HTML context
84 105
        $this->_isJsonApi = $this->_detectJsonRequest();
85
86
        // parse parameters, depending on request type
87 105
        switch (array_key_exists('REQUEST_METHOD', $_SERVER) ? $_SERVER['REQUEST_METHOD'] : 'GET') {
88 105
            case 'DELETE':
89 104
            case 'PUT':
90 3
                parse_str(file_get_contents(self::$_inputStream), $this->_params);
91 3
                break;
92 102
            case 'POST':
93 48
                $this->_params = $_POST;
94 48
                break;
95
            default:
96 54
                $this->_params = $_GET;
97
        }
98
        if (
99 105
            !array_key_exists('pasteid', $this->_params) &&
100 105
            !array_key_exists('jsonld', $this->_params) &&
101 105
            array_key_exists('QUERY_STRING', $_SERVER) &&
102 105
            !empty($_SERVER['QUERY_STRING'])
103
        ) {
104 32
            $this->_params['pasteid'] = $_SERVER['QUERY_STRING'];
105
        }
106
107
        // prepare operation, depending on current parameters
108
        if (
109 105
            (array_key_exists('data', $this->_params) && !empty($this->_params['data'])) ||
110 105
            (array_key_exists('attachment', $this->_params) && !empty($this->_params['attachment']))
111
        ) {
112 44
            $this->_operation = 'create';
113 61
        } elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
114 45 View Code Duplication
            if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
115 20
                $this->_operation = 'delete';
116
            } else {
117 45
                $this->_operation = 'read';
118
            }
119 16 View Code Duplication
        } elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120 5
            $this->_operation = 'jsonld';
121
        }
122 105
    }
123
124
    /**
125
     * Get current operation.
126
     *
127
     * @access public
128
     * @return string
129
     */
130 105
    public function getOperation()
131
    {
132 105
        return $this->_operation;
133
    }
134
135
    /**
136
     * Get a request parameter.
137
     *
138
     * @access public
139
     * @param  string $param
140
     * @param  string $default
141
     * @return string
142
     */
143 94
    public function getParam($param, $default = '')
144
    {
145 94
        return array_key_exists($param, $this->_params) ? $this->_params[$param] : $default;
146
    }
147
148
    /**
149
     * If we are in a JSON API context.
150
     *
151
     * @access public
152
     * @return bool
153
     */
154 100
    public function isJsonApiCall()
155
    {
156 100
        return $this->_isJsonApi;
157
    }
158
159
    /**
160
     * Override the default input stream source, used for unit testing.
161
     *
162
     * @param string $input
163
     */
164 3
    public static function setInputStream($input)
165
    {
166 3
        self::$_inputStream = $input;
167 3
    }
168
169
    /**
170
     * detect the clients supported media type and decide if its a JSON API call or not
171
     *
172
     * Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
173
     *
174
     * @access private
175
     * @return bool
176
     */
177 105
    private function _detectJsonRequest()
0 ignored issues
show
Coding Style introduced by
_detectJsonRequest 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...
178
    {
179 105
        $hasAcceptHeader = array_key_exists('HTTP_ACCEPT', $_SERVER);
180 105
        $acceptHeader    = $hasAcceptHeader ? $_SERVER['HTTP_ACCEPT'] : '';
181
182
        // simple cases
183
        if (
184 105
            (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) &&
185 55
                $_SERVER['HTTP_X_REQUESTED_WITH'] == 'JSONHttpRequest') ||
186 50
            ($hasAcceptHeader &&
187 50
                strpos($acceptHeader, self::MIME_JSON) !== false &&
188 50
                strpos($acceptHeader, self::MIME_HTML) === false &&
189 105
                strpos($acceptHeader, self::MIME_XHTML) === false)
190
        ) {
191 57
            return true;
192
        }
193
194
        // advanced case: media type negotiation
195 48
        $mediaTypes = array();
196 48
        if ($hasAcceptHeader) {
197 4
            $mediaTypeRanges = explode(',', trim($acceptHeader));
198 4 View Code Duplication
            foreach ($mediaTypeRanges as $mediaTypeRange) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
199 4
                if (preg_match(
200 4
                    '#(\*/\*|[a-z\-]+/[a-z\-+*]+(?:\s*;\s*[^q]\S*)*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?#',
201
                    trim($mediaTypeRange), $match
202
                )) {
203 4
                    if (!isset($match[2])) {
204 4
                        $match[2] = '1.0';
205
                    } else {
206 4
                        $match[2] = (string) floatval($match[2]);
207
                    }
208 4
                    if (!isset($mediaTypes[$match[2]])) {
209 4
                        $mediaTypes[$match[2]] = array();
210
                    }
211 4
                    $mediaTypes[$match[2]][] = strtolower($match[1]);
212
                }
213
            }
214 4
            krsort($mediaTypes);
215 4
            foreach ($mediaTypes as $acceptedQuality => $acceptedValues) {
216 4
                if ($acceptedQuality === 0.0) {
217
                    continue;
218
                }
219 4
                foreach ($acceptedValues as $acceptedValue) {
220
                    if (
221 4
                        strpos($acceptedValue, self::MIME_HTML) === 0 ||
222 4
                        strpos($acceptedValue, self::MIME_XHTML) === 0
223
                    ) {
224 2
                        return false;
225 2
                    } elseif (strpos($acceptedValue, self::MIME_JSON) === 0) {
226 2
                        return true;
227
                    }
228
                }
229
            }
230
        }
231 45
        return false;
232
    }
233
}
234