Completed
Pull Request — development (#2329)
by Joshua
09:15
created

HttpReq::getQuery()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.2109
Metric Value
dl 0
loc 12
ccs 5
cts 8
cp 0.625
rs 9.4286
cc 2
eloc 6
nc 2
nop 3
crap 2.2109
1
<?php
2
3
/**
4
 * Http Request class for providing global vars to class for improved
5
 * encapsulation
6
 *
7
 * @name      ElkArte Forum
8
 * @copyright ElkArte Forum contributors
9
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
10
 *
11
 * @version 1.1
12
 *
13
 */
14
15
/**
16
 * Class used to interact with super globals, POST, GET, SERVER, COOKIES, SESSION
17
 *
18
 * - Currently only a 'getter' of values
19
 * - Can be passed DataValidation sanitation values to return sanitized values
20
 * - Fetch raw values as $instance->post->keyname
21
 *     - $this->-req->post->filename
22
 * - Fetch cleaned values with $instance->getPost('keyname', 'sanitation needs', 'default value')
23
 *     - $this->-req->getPost('filename', 'trim|strval', '');
24
 */
25
class HttpReq
26
{
27
	/**
28
	 * The returned POST values
29
	 * @var object
30
	 */
31
	public $post;
32
33
	/**
34
	 * The compiled post values (json and cleaned from request)
35
	 * @var array
36
	 */
37
	private $_derived_post;
38
39
	/**
40
	 * The returned GET values
41
	 * @var object
42
	 */
43
	public $query;
44
45
	/**
46
	 * The returned COOKIE values
47
	 * @var object
48
	 */
49
	public $cookie;
50
51
	/**
52
	 * The returned SESSION values
53
	 * @var object
54
	 */
55
	public $session;
56
57
	/**
58
	 * The returned SERVER values
59
	 * @var object
60
	 */
61
	public $server;
62
63
	/**
64
	 * Sole private HttpReq instance
65
	 * @var HttpReq
66
	 */
67
	private static $_req = null;
68
69
	/**
70
	 * Used to hold processed (sanitised) values
71
	 * @var array
72
	 */
73
	private $_param;
74
75
	/**
76
	 * holds instance of the validator
77
	 * @var Data_Validator
78
	 */
79
	protected $_dataValidator;
80
81
	/**
82
	 * Class constructor, sets PHP globals to class members
83
	 *
84
	 * @param $dataValidator Data_Validator|null Instance of the data validator
85
	 */
86 1
	public function __construct($dataValidator = null)
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_COOKIE 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 $_SESSION 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...
87
	{
88
		// Make sure the validator is initiated
89 1
		$this->_dataValidator = $dataValidator === null ? new Data_Validator : $dataValidator;
90
91
		// Make the superglobals available as R/W properties
92 1
		$this->cookie = new ArrayObject($_COOKIE, ArrayObject::ARRAY_AS_PROPS);
93 1
		$this->session = new ArrayObject($_SESSION, ArrayObject::ARRAY_AS_PROPS);
94 1
		$this->server = new ArrayObject($_SERVER, ArrayObject::ARRAY_AS_PROPS);
95
96 1
		$this->_loadParsed();
97 1
	}
98
99
	/**
100
	 * Certain variables are born in Request, others are sanitized, and stored in
101
	 * $_REQUEST, its a awful mess really.
102
	 * Once that mess is cleaned up this should not be needed.  But the basis is due to
103
	 * what function cleanRequest() does.
104
	 *
105
	 * What it does:
106
	 * - finds items added by cleanRequest to $_REQUEST
107
	 * - adds the above to both $_POST and $_GET
108
	 * - looks for duplicate items in $_REQUEST and $_POST and used the $_REQUEST
109
	 *   values, being they are "sanitized"  $_GET ones are re-stuffed by cleanRequest
110
	 */
111 1
	private function _loadParsed()
0 ignored issues
show
Coding Style introduced by
_loadParsed 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
_loadParsed 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
_loadParsed 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...
112
	{
113
		// Any that were born in cleanRequest, like start from topic=xyz.START
114
		// are added to the other supers
115 1
		$derived = array_diff_key($_REQUEST, $_POST, $_GET);
116 1
		$derived_get = array_merge($_GET, $derived);
117 1
		$this->_derived_post = array_merge($_POST, $derived);
118
119
		// Others may have been "sanitized" from either get or post and saved in request
120
		// these values replace the existing ones in $_POST
121 1
		$cleaned = array_intersect_key($_REQUEST, $this->_derived_post);
122 1
		$this->_derived_post = array_merge($this->_derived_post, $cleaned);
123
124
		// Load any json into _derived_post
125 1
		$this->_loadJson();
126
127
		// Make the $_GET $_POST super globals available as R/W properties
128 1
		$this->post = new ArrayObject($this->_derived_post, ArrayObject::ARRAY_AS_PROPS);
129 1
		$this->query = new ArrayObject($derived_get, ArrayObject::ARRAY_AS_PROPS);
130 1
	}
131
132
	/**
133
	 * Looks for the post value jsonString and expands it to POST
134
	 *
135
	 * What it does:
136
	 * - Looks for jsonString passed in post
137
	 * - json decodes the string and loads its values in to POST
138
	 * - Does not overwrite any existing keys
139
	 */
140 1
	private function _loadJson()
141
	{
142
		// Was the magic json value posted?
143 1
		if (!empty($this->_derived_post['jsonString']))
144 1
		{
145
			$json = json_decode($this->_derived_post['jsonString'], true);
146
147
			// Valid decode
148
			if (!empty($json))
149
			{
150
				// Maintain the original keys only add new ones
151
				$json = array_diff_key($json, $this->_derived_post);
152
				$this->_derived_post = array_merge($this->_derived_post, $json);
153
			}
154
155
			unset($this->_derived_post['jsonString']);
156
		}
157 1
	}
158
159
	/**
160
	 * Generic fetch access for values contained in the super globals
161
	 * - gets in order of param, get and post
162
	 *
163
	 * - $instance->keyanme will check cleaned params, get then post for values
164
	 *     - $_POST['foo'] = 'bar', $_GET['bar'] = 'foo'
165
	 *     - $this->req->post->foo is explicit and returns bar
166
	 *     - $this->req->foo is loose and will trigger this method, return foo as its a found key in GET
167
	 *
168
	 * @param string $key
169
	 */
170
	public function __get($key)
171
	{
172
		switch (true)
173
		{
174
			case isset($this->_param[$key]):
175
				return $this->_param[$key];
176
			case isset($this->query->$key):
177
				return $this->query->$key;
178
			case isset($this->post->$key):
179
				return $this->post->$key;
180
			default:
181
				return null;
182
		}
183
	}
184
185
	/**
186
	 * Alias to __get
187
	 *
188
	 * Allows lazy way to find and return a value from get or post key name
189
	 *
190
	 * @param string $name The key name of the value to return
191
	 * @param string|null $sanitize a comma separated list of sanitation rules to apply
192
	 * @param mixed|null $default default value to return if key value is not found
193
	 */
194
	public function get($name, $sanitize = null, $default = null)
195
	{
196
		// See if it exists in one of the supers
197
		$temp = $this->__get($name);
198
199
		$this->_param[$name] = $default;
200
201
		if (isset($temp))
202
		{
203
			$this->_param[$name] = $temp;
204
			$this->_param[$name] = $this->cleanValue($name, $sanitize);
205
		}
206
207
		return $this->_param[$name];
208
	}
209
210
	/**
211
	 * Generic check to see if a property is set in one of the super globals
212
	 *
213
	 * - checks in order of param, get, post
214
	 *
215
	 * @param string $key
216
	 */
217
	public function __isset($key)
218
	{
219
		switch (true)
220
		{
221
			case isset($this->_param[$key]):
222
				return true;
223
			case isset($this->query->$key):
224
				return true;
225
			case isset($this->post->$key):
226
				return true;
227
			default:
228
				return false;
229
		}
230
	}
231
232
	/**
233
	 * Alias to __isset()
234
	 *
235
	 * @param string $key
236
	 */
237
	public function is_set($key)
238
	{
239
		return $this->__isset($key);
240
	}
241
242
	/**
243
	 * Method to return a $_GET value
244
	 *
245
	 * - Uses any sanitize rule(s) that can be passed to the Data_Validator class
246
	 * - Returned value will be the sanitized value or null of the key is not in $_GET
247
	 * - If you just want a value back access it directly as $req->query->$name
248
	 *
249
	 * @param string $name The key name of the value to return
250
	 * @param string|null $sanitize a comma separated list of sanitation rules to apply
251
	 * @param mixed|null $default default value to return if key value is not found
252
	 */
253 1
	public function getQuery($name = '', $sanitize = null, $default = null)
254
	{
255 1
		$this->_param[$name] = $default;
256
257 1
		if (isset($this->query->$name))
258 1
		{
259
			$this->_param[$name] = $this->query->$name;
260
			$this->_param[$name] = $this->cleanValue($name, $sanitize);
261
		}
262
263 1
		return $this->_param[$name];
264
	}
265
266
	/**
267
	 * Method to return a $_POST value
268
	 *
269
	 * - Uses any sanitize rule(s) that can be passed to the Data_Validator class
270
	 * - Returned value will be the sanitized value or null of the key is not in $_POST
271
	 * - If you just want a value back access it directly as $req->post->$name
272
	 *
273
	 * @param string $name The key name of the value to return
274
	 * @param string|null $sanitize a comma separated list of sanitation rules to apply
275
	 * @param mixed|null $default default value to return if key value is not found
276
	 */
277
	public function getPost($name = '', $sanitize = null, $default = null)
278
	{
279
		$this->_param[$name] = $default;
280
281
		if (isset($this->post->$name))
282
		{
283
			$this->_param[$name] = $this->post->$name;
284
			$this->_param[$name] = $this->cleanValue($name, $sanitize);
285
		}
286
287
		return $this->_param[$name];
288
	}
289
290
	/**
291
	 * Method to return a $_COOKIE value
292
	 *
293
	 * - Does not provide sanitation capability
294
	 *
295
	 * @param string $name the name of the value to return
296
	 * @param mixed|null $default default value to return if key value is not found
297
	 */
298
	public function getCookie($name = '', $default = null)
299
	{
300
		if (isset($this->cookie->$name))
301
			return $this->cookie->$name;
302
		elseif ($default !== null)
303
			return $default;
304
		else
305
			return null;
306
	}
307
308
	/**
309
	 * Method to get a $_SESSION value
310
	 *
311
	 * - Does not provide sanitation capability
312
	 *
313
	 * @param string $name the name of the value to return
314
	 * @param mixed|null $default default value to return if key value is not found
315
	 */
316
	public function getSession($name = '', $default = null)
317
	{
318
		if (isset($this->session->$name))
319
			return $this->session->$name;
320
		elseif ($default !== null)
321
			return $default;
322
		else
323
			return null;
324
	}
325
326
	/**
327
	 * Runs sanitation rules against a single value
328
	 *
329
	 * @param string $name the key name in the _param array
330
	 * @param string|null $sanitize comma seperated list of rules
331
	 */
332
	public function cleanValue($name, $sanitize = null)
333
	{
334
		// No rules, then return the current value
335
		if ($sanitize === null)
336
			return $this->_param[$name];
337
338
		// To the validator
339
		$this->_dataValidator->validation_rules(array());
340
		$this->_dataValidator->sanitation_rules(array($name => $sanitize));
341
		$this->_dataValidator->validate($this->_param);
342
343
		// Return the clean value
344
		return $this->_dataValidator->validation_data($name);
345
	}
346
347
	/**
348
	 * Retrieve the sole instance of this class.
349
	 *
350
	 * @return HttpReq
351
	 */
352 7
	public static function instance()
353
	{
354 7
		if (self::$_req === null)
355 7
			self::$_req = new HttpReq();
356
357 7
		return self::$_req;
358
	}
359
}