Completed
Pull Request — master (#157)
by Kenji
02:29
created

CIPHPUnitTestRequest::setFiles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Part of ci-phpunit-test
4
 *
5
 * @author     Kenji Suzuki <https://github.com/kenjis>
6
 * @license    MIT License
7
 * @copyright  2015 Kenji Suzuki
8
 * @link       https://github.com/kenjis/ci-phpunit-test
9
 */
10
11
class CIPHPUnitTestRequest
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
12
{
13
	protected $testCase;
14
15
	/**
16
	 * @var CIPHPUnitTestSuperGlobal
17
	 */
18
	protected $superGlobal;
19
20
	/**
21
	 * @var CIPHPUnitTestRouter
22
	 */
23
	protected $router;
24
25
	/**
26
	 * @var callable[] callable called post controller constructor
27
	 */
28
	protected $callables = [];
29
	
30
	/**
31
	 * @var callable[] callable called pre controller constructor
32
	 */
33
	protected $callablePreConstructors = [];
34
35
	protected $enableHooks = false;
36
	
37
	/**
38
	 * @var CI_Hooks
39
	 */
40
	protected $hooks;
41
42
	public function __construct(PHPUnit_Framework_TestCase $testCase)
43
	{
44
		$this->testCase = $testCase;
45
		$this->superGlobal = new CIPHPUnitTestSuperGlobal();
46
		$this->router = new CIPHPUnitTestRouter();
47
	}
48
49
	/**
50
	 * Set HTTP request header
51
	 * 
52
	 * @param string $name  header name
53
	 * @param string $value value
54
	 */
55
	public function setHeader($name, $value)
56
	{
57
		$this->superGlobal->set_SERVER_HttpHeader($name, $value);
58
	}
59
60
	/**
61
	 * Set $_FILES
62
	 * 
63
	 * @param array $files
64
	 */
65
	public function setFiles(array $files)
66
	{
67
		$this->superGlobal->set_FILES($files);
68
	}
69
70
	/**
71
	 * Set (and Reset) callable
72
	 * 
73
	 * @param callable $callable function to run after controller instantiation
74
	 */
75
	public function setCallable(callable $callable)
76
	{
77
		$this->callables = [];
78
		$this->callables[] = $callable;
79
	}
80
81
	/**
82
	 * Add callable
83
	 * 
84
	 * @param callable $callable function to run after controller instantiation
85
	 */
86
	public function addCallable(callable $callable)
87
	{
88
		$this->callables[] = $callable;
89
	}
90
91
	/**
92
	 * Set (and Reset) callable pre constructor
93
	 * 
94
	 * @param callable $callable function to run before controller instantiation
95
	 */
96
	public function setCallablePreConstructor(callable $callable)
97
	{
98
		$this->callablePreConstructors = [];
99
		$this->callablePreConstructors[] = $callable;
100
	}
101
102
	/**
103
	 * Add callable pre constructor
104
	 * 
105
	 * @param callable $callable function to run before controller instantiation
106
	 */
107
	public function addCallablePreConstructor(callable $callable)
108
	{
109
		$this->callablePreConstructors[] = $callable;
110
	}
111
112
	/**
113
	 * Enable Hooks for Controllres
114
	 * This enables only pre_controller, post_controller_constructor, post_controller
115
	 */
116
	public function enableHooks()
117
	{
118
		$this->enableHooks = true;
119
		$this->hooks =& load_class('Hooks', 'core');
120
	}
121
122
	/**
123
	 * Request to Controller
124
	 *
125
	 * @param string       $http_method HTTP method
126
	 * @param array|string $argv        array of controller,method,arg|uri
127
	 * @param array|string $params      POST params/GET params|raw_input_stream
128
	 */
129
	public function request($http_method, $argv, $params = [])
0 ignored issues
show
Coding Style introduced by
request 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...
130
	{
131
		if (is_string($argv))
132
		{
133
			$argv = ltrim($argv, '/');
134
		}
135
136
		// Set super globals
137
		$_SERVER['REQUEST_METHOD'] = $http_method;
138
		$this->superGlobal->set_GET($argv, $params);
139
		$this->superGlobal->set_POST($params);
140
		$this->superGlobal->set_SERVER_REQUEST_URI($argv);
141
142
		try {
143
			if (is_array($argv))
144
			{
145
				return $this->callControllerMethod(
146
					$http_method, $argv, $params
147
				);
148
			}
149
			else
150
			{
151
				return $this->requestUri($http_method, $argv, $params);
152
			}
153
		}
154
		// redirect()
155
		catch (CIPHPUnitTestRedirectException $e)
156
		{
157
			if ($e->getCode() === 0)
158
			{
159
				set_status_header(200);
160
			}
161
			else
162
			{
163
				set_status_header($e->getCode());
164
			}
165
			$CI =& get_instance();
166
			$CI->output->_status['redirect'] = $e->getMessage();
167
		}
168
		// show_404()
169
		catch (CIPHPUnitTestShow404Exception $e)
170
		{
171
			$this->processError($e);
172
			return $e->getMessage();
173
		}
174
		// show_error()
175
		catch (CIPHPUnitTestShowErrorException $e)
176
		{
177
			$this->processError($e);
178
			return $e->getMessage();
179
		}
180
		// exit()
181
		catch (CIPHPUnitTestExitException $e)
182
		{
183
			$output = ob_get_clean();
184
			return $output;
185
		}
186
	}
187
188
	protected function processError(Exception $e)
189
	{
190
		set_status_header($e->getCode());
191
	}
192
193
	/**
194
	 * Call Controller Method
195
	 *
196
	 * @param string       $http_method    HTTP method
197
	 * @param array        $argv           controller, method [, arg1, ...]
198
	 * @param array|string $request_params POST params/GET params|raw_input_stream
199
	 */
200
	protected function callControllerMethod($http_method, $argv, $request_params)
0 ignored issues
show
Unused Code introduced by
The parameter $http_method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
callControllerMethod 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
		$_SERVER['argv'] = array_merge(['index.php'], $argv);
203
204
		$class  = ucfirst($argv[0]);
205
		$method = $argv[1];
206
207
		// Remove controller and method
208
		array_shift($argv);
209
		array_shift($argv);
210
211
//		$request = [
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
212
//			'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
213
//			'class' => $class,
214
//			'method' => $method,
215
//			'params' => $argv,
216
//			'$_GET' => $_GET,
217
//			'$_POST' => $_POST,
218
//		];
219
//		var_dump($request, $_SERVER['argv']);
220
221
		// Reset CodeIgniter instance state
222
		reset_instance();
223
224
		$this->setRawInputStream($request_params);
225
226
		// 404 checking
227
		if (! class_exists($class) || ! method_exists($class, $method))
228
		{
229
			// If 404, CodeIgniter instance is not created yet. So create it here
230
			// Because we need CI->output->_status to store info
231
			$CI =& get_instance();
232
			if ($CI instanceof CIPHPUnitTestNullCodeIgniter)
233
			{
234
				CIPHPUnitTest::createCodeIgniterInstance();
235
			}
236
237
			show_404($class.'::'.$method . '() is not found');
238
		}
239
240
		$params = $argv;
241
242
		return $this->createAndCallController($class, $method, $params, false);
243
	}
244
245
	/**
246
	 * Request to URI
247
	 *
248
	 * @param string       $http_method    HTTP method
249
	 * @param string       $uri            URI string
250
	 * @param array|string $request_params POST params/GET params|raw_input_stream
251
	 */
252
	protected function requestUri($http_method, $uri, $request_params)
0 ignored issues
show
Unused Code introduced by
The parameter $http_method is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Coding Style introduced by
requestUri 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...
253
	{
254
		$_SERVER['argv'] = ['index.php', $uri];
255
		$_SERVER['PATH_INFO'] = '/'.$uri;
256
257
		// Force cli mode because if not, it changes URI (and RTR) behavior
258
		$cli = is_cli();
259
		set_is_cli(TRUE);
0 ignored issues
show
Unused Code introduced by
The call to the function set_is_cli() seems unnecessary as the function has no side-effects.
Loading history...
260
261
		// Reset CodeIgniter instance state
262
		reset_instance();
263
264
		$this->setRawInputStream($request_params);
265
266
		// Get route
267
		list($class, $method, $params) = $this->router->getRoute();
268
269
		// Restore cli mode
270
		set_is_cli($cli);
0 ignored issues
show
Unused Code introduced by
The call to the function set_is_cli() seems unnecessary as the function has no side-effects.
Loading history...
271
272
//		$request = [
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
273
//			'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
274
//			'class' => $class,
275
//			'method' => $method,
276
//			'params' => $params,
277
//			'$_GET' => $_GET,
278
//			'$_POST' => $_POST,
279
//		];
280
//		var_dump($request, $_SERVER['argv']);
281
282
		return $this->createAndCallController($class, $method, $params);
283
	}
284
285
	protected function callHook($hook)
286
	{
287
		if ($this->enableHooks)
288
		{
289
			return $this->hooks->call_hook($hook);
290
		}
291
292
		return false;
293
	}
294
295
	protected function setRawInputStream($string)
296
	{
297
		if (is_string($string))
298
		{
299
			$INPUT =& load_class('Input', 'core');
300
			CIPHPUnitTestReflection::setPrivateProperty(
301
				$INPUT,
302
				'_raw_input_stream',
303
				$string
304
			);
305
		}
306
	}
307
308
	protected function createAndCallController($class, $method, $params, $call_display = true)
309
	{
310
		ob_start();
311
312
		$this->callHook('pre_controller');
313
314
		// Run callablePreConstructor
315
		if ($this->callablePreConstructors !== [])
316
		{
317
			foreach ($this->callablePreConstructors as $callable)
318
			{
319
				$callable();
320
			}
321
		}
322
323
		// Create controller
324
		if (CIPHPUnitTest::wiredesignzHmvcInstalled())
325
		{
326
			new CI();
327
		}
328
		$controller = new $class;
329
		$CI =& get_instance();
330
331
		// Set CodeIgniter instance to TestCase
332
		$this->testCase->setCI($CI);
333
334
		// Set default response code 200
335
		set_status_header(200);
336
		// Run callable
337
		if ($this->callables !== [])
338
		{
339
			foreach ($this->callables as $callable)
340
			{
341
				$callable($CI);
342
			}
343
		}
344
345
		$this->callHook('post_controller_constructor');
346
347
		// Call controller method
348
		call_user_func_array([$controller, $method], $params);
349
350
		$this->callHook('post_controller');
351
352
		if ($call_display && $this->callHook('display_override') === false)
353
		{
354
			$CI->output->_display();
355
		}
356
357
		$output = ob_get_clean();
358
359
		if ($output == '')
360
		{
361
			$output = $CI->output->get_output();
362
		}
363
364
		return $output;
365
	}
366
367
	/**
368
	 * Get HTTP Status Code Info
369
	 * 
370
	 * @return array ['code' => code, 'text' => text]
371
	 * @throws LogicException
372
	 */
373
	public function getStatus()
374
	{
375
		$CI =& get_instance();
376
		if (! isset($CI->output->_status))
377
		{
378
			throw new LogicException('Status code is not set. You must call $this->request() first');
379
		}
380
381
		return $CI->output->_status;
382
	}
383
}
384