Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.08% covered (warning)
73.08%
57 / 78
73.68% covered (warning)
73.68%
14 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
request
73.08% covered (warning)
73.08%
57 / 78
73.68% covered (warning)
73.68%
14 / 19
95.86
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 super_globals_disabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 disable_super_globals
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 enable_super_globals
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 overwrite
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 variable
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 untrimmed_variable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 raw_variable
86.67% covered (warning)
86.67%
13 / 15
0.00% covered (danger)
0.00%
0 / 1
11.29
 server
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 header
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 file
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_set_post
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_set
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_ajax
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_secure
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 variable_names
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 _variable
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 get_super_global
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 escape
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3*
4* This file is part of the phpBB Forum Software package.
5*
6* @copyright (c) phpBB Limited <https://www.phpbb.com>
7* @license GNU General Public License, version 2 (GPL-2.0)
8*
9* For full copyright and license information, please see
10* the docs/CREDITS.txt file.
11*
12*/
13
14namespace phpbb\request;
15
16/**
17* All application input is accessed through this class.
18*
19* It provides a method to disable access to input data through super globals.
20* This should force MOD authors to read about data validation.
21*/
22class request implements request_interface
23{
24    /**
25    * @var    array    The names of super global variables that this class should protect if super globals are disabled.
26    */
27    protected $super_globals = array(
28        request_interface::POST => '_POST',
29        request_interface::GET => '_GET',
30        request_interface::REQUEST => '_REQUEST',
31        request_interface::COOKIE => '_COOKIE',
32        request_interface::SERVER => '_SERVER',
33        request_interface::FILES => '_FILES',
34    );
35
36    /**
37    * @var    array    Stores original contents of $_REQUEST array.
38    */
39    protected $original_request = null;
40
41    /**
42    * @var bool
43    */
44    protected $super_globals_disabled = false;
45
46    /**
47    * @var    array    An associative array that has the value of super global constants as keys and holds their data as values.
48    */
49    protected $input;
50
51    /**
52    * @var    \phpbb\request\type_cast_helper_interface    An instance of a type cast helper providing convenience methods for type conversions.
53    */
54    protected $type_cast_helper;
55
56    /**
57    * Initialises the request class, that means it stores all input data in {@link $input input}
58    * and then calls {@link \phpbb\request\deactivated_super_global \phpbb\request\deactivated_super_global}
59    */
60    public function __construct(\phpbb\request\type_cast_helper_interface $type_cast_helper = null, $disable_super_globals = true)
61    {
62        if ($type_cast_helper)
63        {
64            $this->type_cast_helper = $type_cast_helper;
65        }
66        else
67        {
68            $this->type_cast_helper = new \phpbb\request\type_cast_helper();
69        }
70
71        foreach ($this->super_globals as $const => $super_global)
72        {
73            $this->input[$const] = isset($GLOBALS[$super_global]) ? $GLOBALS[$super_global] : array();
74        }
75
76        // simulate request_order = GP
77        $this->original_request = $this->input[request_interface::REQUEST];
78        $this->input[request_interface::REQUEST] = $this->input[request_interface::POST] + $this->input[request_interface::GET];
79
80        if ($disable_super_globals)
81        {
82            $this->disable_super_globals();
83        }
84    }
85
86    /**
87    * Getter for $super_globals_disabled
88    *
89    * @return    bool    Whether super globals are disabled or not.
90    */
91    public function super_globals_disabled()
92    {
93        return $this->super_globals_disabled;
94    }
95
96    /**
97    * Disables access of super globals specified in $super_globals.
98    * This is achieved by overwriting the super globals with instances of {@link \phpbb\request\deactivated_super_global \phpbb\request\deactivated_super_global}
99    */
100    public function disable_super_globals()
101    {
102        if (!$this->super_globals_disabled)
103        {
104            foreach ($this->super_globals as $const => $super_global)
105            {
106                unset($GLOBALS[$super_global]);
107                $GLOBALS[$super_global] = new \phpbb\request\deactivated_super_global($this, $super_global, $const);
108            }
109
110            $this->super_globals_disabled = true;
111        }
112    }
113
114    /**
115    * Enables access of super globals specified in $super_globals if they were disabled by {@link disable_super_globals disable_super_globals}.
116    * This is achieved by making the super globals point to the data stored within this class in {@link $input input}.
117    */
118    public function enable_super_globals()
119    {
120        if ($this->super_globals_disabled)
121        {
122            foreach ($this->super_globals as $const => $super_global)
123            {
124                $GLOBALS[$super_global] = $this->input[$const];
125            }
126
127            $GLOBALS['_REQUEST'] = $this->original_request;
128
129            $this->super_globals_disabled = false;
130        }
131    }
132
133    /**
134    * This function allows overwriting or setting a value in one of the super global arrays.
135    *
136    * Changes which are performed on the super globals directly will not have any effect on the results of
137    * other methods this class provides. Using this function should be avoided if possible! It will
138    * consume twice the the amount of memory of the value
139    *
140    * @param    string    $var_name        The name of the variable that shall be overwritten
141    * @param    mixed    $value            The value which the variable shall contain.
142    *                                     If this is null the variable will be unset.
143    * @param    int        $super_global    (\phpbb\request\request_interface::POST|GET|REQUEST|COOKIE)
144    *                                     Specifies which super global shall be changed
145    */
146    public function overwrite($var_name, $value, $super_global = request_interface::REQUEST)
147    {
148        if (!isset($this->super_globals[$super_global]))
149        {
150            return;
151        }
152
153        // setting to null means unsetting
154        if ($value === null)
155        {
156            unset($this->input[$super_global][$var_name]);
157            if (!$this->super_globals_disabled())
158            {
159                unset($GLOBALS[$this->super_globals[$super_global]][$var_name]);
160            }
161        }
162        else
163        {
164            $this->input[$super_global][$var_name] = $value;
165            if (!$this->super_globals_disabled())
166            {
167                $GLOBALS[$this->super_globals[$super_global]][$var_name] = $value;
168            }
169        }
170    }
171
172    /**
173    * Central type safe input handling function.
174    * All variables in GET or POST requests should be retrieved through this function to maximise security.
175    *
176    * @param    string|array    $var_name        The form variable's name from which data shall be retrieved.
177    *                                             If the value is an array this may be an array of indizes which will give
178    *                                             direct access to a value at any depth. E.g. if the value of "var" is array(1 => "a")
179    *                                             then specifying array("var", 1) as the name will return "a".
180    * @param    mixed            $default        A default value that is returned if the variable was not set.
181    *                                             This function will always return a value of the same type as the default.
182    * @param    bool            $multibyte        If $default is a string this parameter has to be true if the variable may contain any UTF-8 characters
183    *                                            Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks
184    * @param    int                $super_global    (\phpbb\request\request_interface::POST|GET|REQUEST|COOKIE)
185    *                                            Specifies which super global should be used
186    *
187    * @return    mixed    The value of $_REQUEST[$var_name] run through {@link set_var set_var} to ensure that the type is the
188    *                    the same as that of $default. If the variable is not set $default is returned.
189    */
190    public function variable($var_name, $default, $multibyte = false, $super_global = request_interface::REQUEST)
191    {
192        return $this->_variable($var_name, $default, $multibyte, $super_global, true);
193    }
194
195    /**
196    * Get a variable, but without trimming strings.
197    * Same functionality as variable(), except does not run trim() on strings.
198    * This method should be used when handling passwords.
199    *
200    * @param    string|array    $var_name        The form variable's name from which data shall be retrieved.
201    *                                             If the value is an array this may be an array of indizes which will give
202    *                                             direct access to a value at any depth. E.g. if the value of "var" is array(1 => "a")
203    *                                             then specifying array("var", 1) as the name will return "a".
204    * @param    mixed            $default        A default value that is returned if the variable was not set.
205    *                                             This function will always return a value of the same type as the default.
206    * @param    bool            $multibyte        If $default is a string this parameter has to be true if the variable may contain any UTF-8 characters
207    *                                            Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks
208    * @param    int                $super_global    (\phpbb\request\request_interface::POST|GET|REQUEST|COOKIE)
209    *                                             Specifies which super global should be used
210    *
211    * @return    mixed    The value of $_REQUEST[$var_name] run through {@link set_var set_var} to ensure that the type is the
212    *                    the same as that of $default. If the variable is not set $default is returned.
213    */
214    public function untrimmed_variable($var_name, $default, $multibyte = false, $super_global = request_interface::REQUEST)
215    {
216        return $this->_variable($var_name, $default, $multibyte, $super_global, false);
217    }
218
219    /**
220     * {@inheritdoc}
221     */
222    public function raw_variable($var_name, $default, $super_global = request_interface::REQUEST)
223    {
224        $path = false;
225
226        // deep direct access to multi dimensional arrays
227        if (is_array($var_name))
228        {
229            $path = $var_name;
230            // make sure at least the variable name is specified
231            if (empty($path))
232            {
233                return (is_array($default)) ? array() : $default;
234            }
235            // the variable name is the first element on the path
236            $var_name = array_shift($path);
237        }
238
239        if (!isset($this->input[$super_global][$var_name]))
240        {
241            return (is_array($default)) ? array() : $default;
242        }
243        $var = $this->input[$super_global][$var_name];
244
245        if ($path)
246        {
247            // walk through the array structure and find the element we are looking for
248            foreach ($path as $key)
249            {
250                if (is_array($var) && isset($var[$key]))
251                {
252                    $var = $var[$key];
253                }
254                else
255                {
256                    return (is_array($default)) ? array() : $default;
257                }
258            }
259        }
260
261        return $var;
262    }
263
264    /**
265    * Shortcut method to retrieve SERVER variables.
266    *
267    * Also fall back to getenv(), some CGI setups may need it (probably not, but
268    * whatever).
269    *
270    * @param    string|array    $var_name        See \phpbb\request\request_interface::variable
271    * @param    mixed            $default        See \phpbb\request\request_interface::variable
272    *
273    * @return    mixed    The server variable value.
274    */
275    public function server($var_name, $default = '')
276    {
277        $multibyte = true;
278
279        if ($this->is_set($var_name, request_interface::SERVER))
280        {
281            return $this->variable($var_name, $default, $multibyte, request_interface::SERVER);
282        }
283        else
284        {
285            $var = getenv($var_name);
286            $this->type_cast_helper->recursive_set_var($var, $default, $multibyte);
287            return $var;
288        }
289    }
290
291    /**
292    * Shortcut method to retrieve the value of client HTTP headers.
293    *
294    * @param    string|array    $header_name    The name of the header to retrieve.
295    * @param    mixed            $default        See \phpbb\request\request_interface::variable
296    *
297    * @return    mixed    The header value.
298    */
299    public function header($header_name, $default = '')
300    {
301        $var_name = 'HTTP_' . str_replace('-', '_', strtoupper($header_name));
302        return $this->server($var_name, $default);
303    }
304
305    /**
306    * Shortcut method to retrieve $_FILES variables
307    *
308    * @param string $form_name The name of the file input form element
309    *
310    * @return array The uploaded file's information or an empty array if the
311    * variable does not exist in _FILES.
312    */
313    public function file($form_name)
314    {
315        return $this->variable($form_name, array('name' => 'none'), true, request_interface::FILES);
316    }
317
318    /**
319    * Checks whether a certain variable was sent via POST.
320    * To make sure that a request was sent using POST you should call this function
321    * on at least one variable.
322    *
323    * @param    string    $name    The name of the form variable which should have a
324    *                            _p suffix to indicate the check in the code that creates the form too.
325    *
326    * @return    bool            True if the variable was set in a POST request, false otherwise.
327    */
328    public function is_set_post($name)
329    {
330        return $this->is_set($name, request_interface::POST);
331    }
332
333    /**
334    * Checks whether a certain variable is set in one of the super global
335    * arrays.
336    *
337    * @param    string    $var            Name of the variable
338    * @param    int        $super_global    (\phpbb\request\request_interface::POST|GET|REQUEST|COOKIE)
339    *                                    Specifies the super global which shall be checked
340    *
341    * @return    bool                    True if the variable was sent as input
342    */
343    public function is_set($var, $super_global = request_interface::REQUEST)
344    {
345        return isset($this->input[$super_global][$var]);
346    }
347
348    /**
349    * Checks whether the current request is an AJAX request (XMLHttpRequest)
350    *
351    * @return    bool            True if the current request is an ajax request
352    */
353    public function is_ajax()
354    {
355        return $this->header('X-Requested-With') == 'XMLHttpRequest';
356    }
357
358    /**
359    * Checks if the current request is happening over HTTPS.
360    *
361    * @return    bool            True if the request is secure.
362    */
363    public function is_secure()
364    {
365        $https = $this->server('HTTPS');
366        $https = $this->server('HTTP_X_FORWARDED_PROTO') === 'https' ? 'on' : $https;
367        return !empty($https) && $https !== 'off';
368    }
369
370    /**
371    * Returns all variable names for a given super global
372    *
373    * @param    int        $super_global    (\phpbb\request\request_interface::POST|GET|REQUEST|COOKIE)
374    *                                    The super global from which names shall be taken
375    *
376    * @return    array    All variable names that are set for the super global.
377    *                    Pay attention when using these, they are unsanitised!
378    */
379    public function variable_names($super_global = request_interface::REQUEST)
380    {
381        if (!isset($this->input[$super_global]))
382        {
383            return array();
384        }
385
386        return array_keys($this->input[$super_global]);
387    }
388
389    /**
390    * Helper function used by variable() and untrimmed_variable().
391    *
392    * @param    string|array    $var_name        The form variable's name from which data shall be retrieved.
393    *                                             If the value is an array this may be an array of indizes which will give
394    *                                             direct access to a value at any depth. E.g. if the value of "var" is array(1 => "a")
395    *                                             then specifying array("var", 1) as the name will return "a".
396    * @param    mixed            $default        A default value that is returned if the variable was not set.
397    *                                             This function will always return a value of the same type as the default.
398    * @param    bool            $multibyte        If $default is a string this parameter has to be true if the variable may contain any UTF-8 characters
399    *                                            Default is false, causing all bytes outside the ASCII range (0-127) to be replaced with question marks
400    * @param    int                $super_global    (\phpbb\request\request_interface::POST|GET|REQUEST|COOKIE)
401    *                                             Specifies which super global should be used
402    * @param    bool            $trim            Indicates whether trim() should be applied to string values.
403    *
404    * @return    mixed    The value of $_REQUEST[$var_name] run through {@link set_var set_var} to ensure that the type is the
405    *                    the same as that of $default. If the variable is not set $default is returned.
406    */
407    protected function _variable($var_name, $default, $multibyte = false, $super_global = request_interface::REQUEST, $trim = true)
408    {
409        $var = $this->raw_variable($var_name, $default, $super_global);
410
411        // Return prematurely if raw variable is empty array or the same as
412        // the default. Using strict comparison to ensure that one can't
413        // prevent proper type checking on any input variable
414        if ($var === array() || $var === $default)
415        {
416            return $var;
417        }
418
419        $this->type_cast_helper->recursive_set_var($var, $default, $multibyte, $trim);
420
421        return $var;
422    }
423
424    /**
425    * {@inheritdoc}
426    */
427    public function get_super_global($super_global = request_interface::REQUEST)
428    {
429        return $this->input[$super_global];
430    }
431
432    /**
433     * {@inheritdoc}
434     */
435    public function escape($value, $multibyte)
436    {
437        if (is_array($value))
438        {
439            $result = array();
440            foreach ($value as $key => $array_value)
441            {
442                $this->type_cast_helper->set_var($key, $key, gettype($key), $multibyte);
443                $result[$key] = $this->escape($array_value, $multibyte);
444            }
445            $value = $result;
446        }
447        else
448        {
449            $this->type_cast_helper->set_var($value, $value, 'string', $multibyte);
450        }
451
452        return $value;
453    }
454}