Completed
Push — master ( 5536b6...134bc1 )
by Kenji
02:32
created

CIPHPUnitTestRequest::enableHooks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
rs 9.4285
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 (and Reset) callable
62
	 * 
63
	 * @param callable $callable function to run after controller instantiation
64
	 */
65
	public function setCallable(callable $callable)
66
	{
67
		$this->callables = [];
68
		$this->callables[] = $callable;
69
	}
70
71
	/**
72
	 * Add callable
73
	 * 
74
	 * @param callable $callable function to run after controller instantiation
75
	 */
76
	public function addCallable(callable $callable)
77
	{
78
		$this->callables[] = $callable;
79
	}
80
81
	/**
82
	 * Set (and Reset) callable pre constructor
83
	 * 
84
	 * @param callable $callable function to run before controller instantiation
85
	 */
86
	public function setCallablePreConstructor(callable $callable)
87
	{
88
		$this->callablePreConstructors = [];
89
		$this->callablePreConstructors[] = $callable;
90
	}
91
92
	/**
93
	 * Add callable pre constructor
94
	 * 
95
	 * @param callable $callable function to run before controller instantiation
96
	 */
97
	public function addCallablePreConstructor(callable $callable)
98
	{
99
		$this->callablePreConstructors[] = $callable;
100
	}
101
102
	/**
103
	 * Enable Hooks for Controllres
104
	 * This enables only pre_controller, post_controller_constructor, post_controller
105
	 */
106
	public function enableHooks()
107
	{
108
		$this->enableHooks = true;
109
		$this->hooks =& load_class('Hooks', 'core');
110
	}
111
112
	/**
113
	 * Request to Controller
114
	 *
115
	 * @param string       $http_method HTTP method
116
	 * @param array|string $argv        array of controller,method,arg|uri
117
	 * @param array|string $params      POST params/GET params|raw_input_stream
118
	 */
119
	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...
120
	{
121
		if (is_string($argv))
122
		{
123
			$argv = ltrim($argv, '/');
124
		}
125
126
		// Set super globals
127
		$_SERVER['REQUEST_METHOD'] = $http_method;
128
		$this->superGlobal->set_GET($argv, $params);
129
		$this->superGlobal->set_POST($params);
130
		$this->superGlobal->set_SERVER_REQUEST_URI($argv);
131
132
		try {
133
			if (is_array($argv))
134
			{
135
				return $this->callControllerMethod(
136
					$http_method, $argv, $params
137
				);
138
			}
139
			else
140
			{
141
				return $this->requestUri($http_method, $argv, $params);
142
			}
143
		}
144
		// redirect()
145
		catch (CIPHPUnitTestRedirectException $e)
146
		{
147
			if ($e->getCode() === 0)
148
			{
149
				set_status_header(200);
150
			}
151
			else
152
			{
153
				set_status_header($e->getCode());
154
			}
155
			$CI =& get_instance();
156
			$CI->output->_status['redirect'] = $e->getMessage();
157
		}
158
		// show_404()
159
		catch (CIPHPUnitTestShow404Exception $e)
160
		{
161
			$this->processError($e);
162
			return $e->getMessage();
163
		}
164
		// show_error()
165
		catch (CIPHPUnitTestShowErrorException $e)
166
		{
167
			$this->processError($e);
168
			return $e->getMessage();
169
		}
170
		// exit()
171
		catch (CIPHPUnitTestExitException $e)
172
		{
173
			$output = ob_get_clean();
174
			return $output;
175
		}
176
	}
177
178
	protected function processError(Exception $e)
179
	{
180
		set_status_header($e->getCode());
181
	}
182
183
	/**
184
	 * Call Controller Method
185
	 *
186
	 * @param string       $http_method    HTTP method
187
	 * @param array        $argv           controller, method [, arg1, ...]
188
	 * @param array|string $request_params POST params/GET params|raw_input_stream
189
	 */
190
	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...
191
	{
192
		$_SERVER['argv'] = array_merge(['index.php'], $argv);
193
194
		$class  = ucfirst($argv[0]);
195
		$method = $argv[1];
196
197
		// Remove controller and method
198
		array_shift($argv);
199
		array_shift($argv);
200
201
//		$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...
202
//			'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
203
//			'class' => $class,
204
//			'method' => $method,
205
//			'params' => $argv,
206
//			'$_GET' => $_GET,
207
//			'$_POST' => $_POST,
208
//		];
209
//		var_dump($request, $_SERVER['argv']);
210
211
		// Reset CodeIgniter instance state
212
		reset_instance();
213
214
		$this->setRawInputStream($request_params);
215
216
		// 404 checking
217
		if (! class_exists($class) || ! method_exists($class, $method))
218
		{
219
			// If 404, CodeIgniter instance is not created yet. So create it here
220
			// Because we need CI->output->_status to store info
221
			$CI =& get_instance();
222
			if ($CI instanceof CIPHPUnitTestNullCodeIgniter)
223
			{
224
				CIPHPUnitTest::createCodeIgniterInstance();
225
			}
226
227
			show_404($class.'::'.$method . '() is not found');
228
		}
229
230
		$params = $argv;
231
232
		return $this->createAndCallController($class, $method, $params, false);
233
	}
234
235
	/**
236
	 * Request to URI
237
	 *
238
	 * @param string       $http_method    HTTP method
239
	 * @param string       $uri            URI string
240
	 * @param array|string $request_params POST params/GET params|raw_input_stream
241
	 */
242
	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...
243
	{
244
		$_SERVER['argv'] = ['index.php', $uri];
245
		$_SERVER['PATH_INFO'] = '/'.$uri;
246
247
		// Force cli mode because if not, it changes URI (and RTR) behavior
248
		$cli = is_cli();
249
		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...
250
251
		// Reset CodeIgniter instance state
252
		reset_instance();
253
254
		$this->setRawInputStream($request_params);
255
256
		// Get route
257
		list($class, $method, $params) = $this->router->getRoute();
258
259
		// Restore cli mode
260
		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...
261
262
//		$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...
263
//			'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'],
264
//			'class' => $class,
265
//			'method' => $method,
266
//			'params' => $params,
267
//			'$_GET' => $_GET,
268
//			'$_POST' => $_POST,
269
//		];
270
//		var_dump($request, $_SERVER['argv']);
271
272
		return $this->createAndCallController($class, $method, $params);
273
	}
274
275
	protected function callHook($hook)
276
	{
277
		if ($this->enableHooks)
278
		{
279
			return $this->hooks->call_hook($hook);
280
		}
281
282
		return false;
283
	}
284
285
	protected function setRawInputStream($string)
286
	{
287
		if (is_string($string))
288
		{
289
			$INPUT =& load_class('Input', 'core');
290
			CIPHPUnitTestReflection::setPrivateProperty(
291
				$INPUT,
292
				'_raw_input_stream',
293
				$string
294
			);
295
		}
296
	}
297
298
	protected function createAndCallController($class, $method, $params, $call_display = true)
299
	{
300
		ob_start();
301
302
		$this->callHook('pre_controller');
303
304
		// Run callablePreConstructor
305
		if ($this->callablePreConstructors !== [])
306
		{
307
			foreach ($this->callablePreConstructors as $callable)
308
			{
309
				$callable();
310
			}
311
		}
312
313
		// Create controller
314
		if (CIPHPUnitTest::wiredesignzHmvcInstalled())
315
		{
316
			new CI();
317
		}
318
		$controller = new $class;
319
		$CI =& get_instance();
320
321
		// Set CodeIgniter instance to TestCase
322
		$this->testCase->setCI($CI);
323
324
		// Set default response code 200
325
		set_status_header(200);
326
		// Run callable
327
		if ($this->callables !== [])
328
		{
329
			foreach ($this->callables as $callable)
330
			{
331
				$callable($CI);
332
			}
333
		}
334
335
		$this->callHook('post_controller_constructor');
336
337
		// Call controller method
338
		call_user_func_array([$controller, $method], $params);
339
340
		$this->callHook('post_controller');
341
342
		if ($call_display && $this->callHook('display_override') === false)
343
		{
344
			$CI->output->_display();
345
		}
346
347
		$output = ob_get_clean();
348
349
		if ($output == '')
350
		{
351
			$output = $CI->output->get_output();
352
		}
353
354
		return $output;
355
	}
356
357
	/**
358
	 * Get HTTP Status Code Info
359
	 * 
360
	 * @return array ['code' => code, 'text' => text]
361
	 * @throws LogicException
362
	 */
363
	public function getStatus()
364
	{
365
		$CI =& get_instance();
366
		if (! isset($CI->output->_status))
367
		{
368
			throw new LogicException('Status code is not set. You must call $this->request() first');
369
		}
370
371
		return $CI->output->_status;
372
	}
373
}
374