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 |
|
|
|
|
12
|
|
|
{ |
13
|
|
|
/** |
14
|
|
|
* @var TestCase |
15
|
|
|
*/ |
16
|
|
|
protected $testCase; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var CIPHPUnitTestSuperGlobal |
20
|
|
|
*/ |
21
|
|
|
protected $superGlobal; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var CIPHPUnitTestRouter |
25
|
|
|
*/ |
26
|
|
|
protected $router; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var callable[] callable called post controller constructor |
30
|
|
|
*/ |
31
|
|
|
protected $callables = []; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var callable[] callable called pre controller constructor |
35
|
|
|
*/ |
36
|
|
|
protected $callablePreConstructors = []; |
37
|
|
|
|
38
|
|
|
protected $enableHooks = false; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var CI_Hooks |
42
|
|
|
*/ |
43
|
|
|
protected $hooks; |
44
|
|
|
|
45
|
|
|
public function __construct(TestCase $testCase) |
46
|
|
|
{ |
47
|
|
|
$this->testCase = $testCase; |
48
|
|
|
$this->superGlobal = new CIPHPUnitTestSuperGlobal(); |
49
|
|
|
$this->router = new CIPHPUnitTestRouter(); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Set HTTP request header |
54
|
|
|
* |
55
|
|
|
* @param string $name header name |
56
|
|
|
* @param string $value value |
57
|
|
|
*/ |
58
|
|
|
public function setHeader($name, $value) |
59
|
|
|
{ |
60
|
|
|
$this->superGlobal->set_SERVER_HttpHeader($name, $value); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Set $_FILES |
65
|
|
|
* |
66
|
|
|
* @param array $files |
67
|
|
|
*/ |
68
|
|
|
public function setFiles(array $files) |
69
|
|
|
{ |
70
|
|
|
$this->superGlobal->set_FILES($files); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Set (and Reset) callable |
75
|
|
|
* |
76
|
|
|
* @param callable $callable function to run after controller instantiation |
77
|
|
|
*/ |
78
|
|
|
public function setCallable(callable $callable) |
79
|
|
|
{ |
80
|
|
|
$this->callables = []; |
81
|
|
|
$this->callables[] = $callable; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Add callable |
86
|
|
|
* |
87
|
|
|
* @param callable $callable function to run after controller instantiation |
88
|
|
|
*/ |
89
|
|
|
public function addCallable(callable $callable) |
90
|
|
|
{ |
91
|
|
|
$this->callables[] = $callable; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Set (and Reset) callable pre constructor |
96
|
|
|
* |
97
|
|
|
* @param callable $callable function to run before controller instantiation |
98
|
|
|
*/ |
99
|
|
|
public function setCallablePreConstructor(callable $callable) |
100
|
|
|
{ |
101
|
|
|
$this->callablePreConstructors = []; |
102
|
|
|
$this->callablePreConstructors[] = $callable; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Add callable pre constructor |
107
|
|
|
* |
108
|
|
|
* @param callable $callable function to run before controller instantiation |
109
|
|
|
*/ |
110
|
|
|
public function addCallablePreConstructor(callable $callable) |
111
|
|
|
{ |
112
|
|
|
$this->callablePreConstructors[] = $callable; |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
/** |
116
|
|
|
* Enable Hooks for Controllres |
117
|
|
|
* This enables only pre_controller, post_controller_constructor, post_controller |
118
|
|
|
*/ |
119
|
|
|
public function enableHooks() |
120
|
|
|
{ |
121
|
|
|
$this->enableHooks = true; |
122
|
|
|
$this->hooks =& load_class('Hooks', 'core'); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Request to Controller |
127
|
|
|
* |
128
|
|
|
* @param string $http_method HTTP method |
129
|
|
|
* @param array|string $argv array of controller,method,arg|uri |
130
|
|
|
* @param array|string $params POST params/GET params|raw_input_stream |
131
|
|
|
*/ |
132
|
|
|
public function request($http_method, $argv, $params = []) |
|
|
|
|
133
|
|
|
{ |
134
|
|
|
if ($this->testCase->getStrictRequestErrorCheck()) { |
135
|
|
|
$this->testCase->enableStrictErrorCheck(); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
if (is_string($argv)) |
139
|
|
|
{ |
140
|
|
|
$argv = ltrim($argv, '/'); |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
// Set super globals |
144
|
|
|
$_SERVER['REQUEST_METHOD'] = $http_method; |
145
|
|
|
$this->superGlobal->set_GET($argv, $params); |
146
|
|
|
$this->superGlobal->set_POST($params); |
147
|
|
|
$this->superGlobal->set_SERVER_REQUEST_URI($argv); |
148
|
|
|
|
149
|
|
|
try { |
150
|
|
|
if (is_array($argv)) |
151
|
|
|
{ |
152
|
|
|
$ret = $this->callControllerMethod( |
153
|
|
|
$http_method, $argv, $params |
154
|
|
|
); |
155
|
|
|
$this->testCase->disableStrictErrorCheck(); |
156
|
|
|
return $ret; |
157
|
|
|
} |
158
|
|
|
else |
159
|
|
|
{ |
160
|
|
|
$ret = $this->requestUri($http_method, $argv, $params); |
161
|
|
|
$this->testCase->disableStrictErrorCheck(); |
162
|
|
|
return $ret; |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
// redirect() |
166
|
|
|
catch (CIPHPUnitTestRedirectException $e) |
167
|
|
|
{ |
168
|
|
|
$this->testCase->disableStrictErrorCheck(); |
169
|
|
|
if ($e->getCode() === 0) |
170
|
|
|
{ |
171
|
|
|
set_status_header(200); |
172
|
|
|
} |
173
|
|
|
else |
174
|
|
|
{ |
175
|
|
|
set_status_header($e->getCode()); |
176
|
|
|
} |
177
|
|
|
$CI =& get_instance(); |
178
|
|
|
$CI->output->_status['redirect'] = $e->getMessage(); |
179
|
|
|
} |
180
|
|
|
// show_404() |
181
|
|
|
catch (CIPHPUnitTestShow404Exception $e) |
182
|
|
|
{ |
183
|
|
|
$this->testCase->disableStrictErrorCheck(); |
184
|
|
|
$this->processError($e); |
185
|
|
|
return $e->getMessage(); |
186
|
|
|
} |
187
|
|
|
// show_error() |
188
|
|
|
catch (CIPHPUnitTestShowErrorException $e) |
189
|
|
|
{ |
190
|
|
|
$this->testCase->disableStrictErrorCheck(); |
191
|
|
|
$this->processError($e); |
192
|
|
|
return $e->getMessage(); |
193
|
|
|
} |
194
|
|
|
// exit() |
195
|
|
|
catch (CIPHPUnitTestExitException $e) |
196
|
|
|
{ |
197
|
|
|
$this->testCase->disableStrictErrorCheck(); |
198
|
|
|
$output = ob_get_clean(); |
199
|
|
|
return $output; |
200
|
|
|
} |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
protected function processError(Exception $e) |
204
|
|
|
{ |
205
|
|
|
set_status_header($e->getCode()); |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* Call Controller Method |
210
|
|
|
* |
211
|
|
|
* @param string $http_method HTTP method |
212
|
|
|
* @param array $argv controller, method [, arg1, ...] |
213
|
|
|
* @param array|string $request_params POST params/GET params|raw_input_stream |
214
|
|
|
*/ |
215
|
|
|
protected function callControllerMethod($http_method, $argv, $request_params) |
|
|
|
|
216
|
|
|
{ |
217
|
|
|
$_SERVER['argv'] = array_merge(['index.php'], $argv); |
218
|
|
|
|
219
|
|
|
$class = ucfirst($argv[0]); |
220
|
|
|
$method = $argv[1]; |
221
|
|
|
|
222
|
|
|
// Remove controller and method |
223
|
|
|
array_shift($argv); |
224
|
|
|
array_shift($argv); |
225
|
|
|
|
226
|
|
|
// $request = [ |
|
|
|
|
227
|
|
|
// 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'], |
228
|
|
|
// 'class' => $class, |
229
|
|
|
// 'method' => $method, |
230
|
|
|
// 'params' => $argv, |
231
|
|
|
// '$_GET' => $_GET, |
232
|
|
|
// '$_POST' => $_POST, |
233
|
|
|
// ]; |
234
|
|
|
// var_dump($request, $_SERVER['argv']); |
235
|
|
|
|
236
|
|
|
// Reset CodeIgniter instance state |
237
|
|
|
reset_instance(); |
238
|
|
|
|
239
|
|
|
$this->setRawInputStream($request_params); |
240
|
|
|
|
241
|
|
|
// 404 checking |
242
|
|
|
if (! class_exists($class) || ! method_exists($class, $method)) |
243
|
|
|
{ |
244
|
|
|
// If 404, CodeIgniter instance is not created yet. So create it here |
245
|
|
|
// Because we need CI->output->_status to store info |
246
|
|
|
$CI =& get_instance(); |
247
|
|
|
if ($CI instanceof CIPHPUnitTestNullCodeIgniter) |
248
|
|
|
{ |
249
|
|
|
CIPHPUnitTest::createCodeIgniterInstance(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
show_404($class.'::'.$method . '() is not found'); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
$params = $argv; |
256
|
|
|
|
257
|
|
|
return $this->createAndCallController($class, $method, $params, false); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* Request to URI |
262
|
|
|
* |
263
|
|
|
* @param string $http_method HTTP method |
264
|
|
|
* @param string $uri URI string |
265
|
|
|
* @param array|string $request_params POST params/GET params|raw_input_stream |
266
|
|
|
*/ |
267
|
|
|
protected function requestUri($http_method, $uri, $request_params) |
|
|
|
|
268
|
|
|
{ |
269
|
|
|
$_SERVER['argv'] = ['index.php', $uri]; |
270
|
|
|
$_SERVER['PATH_INFO'] = '/'.$uri; |
271
|
|
|
|
272
|
|
|
// Force cli mode because if not, it changes URI (and RTR) behavior |
273
|
|
|
$cli = is_cli(); |
274
|
|
|
set_is_cli(TRUE); |
|
|
|
|
275
|
|
|
|
276
|
|
|
// Reset CodeIgniter instance state |
277
|
|
|
reset_instance(); |
278
|
|
|
|
279
|
|
|
$this->setRawInputStream($request_params); |
280
|
|
|
|
281
|
|
|
// Get route |
282
|
|
|
list($class, $method, $params) = $this->router->getRoute(); |
283
|
|
|
|
284
|
|
|
// Restore cli mode |
285
|
|
|
set_is_cli($cli); |
|
|
|
|
286
|
|
|
|
287
|
|
|
// $request = [ |
|
|
|
|
288
|
|
|
// 'REQUEST_METHOD' => $_SERVER['REQUEST_METHOD'], |
289
|
|
|
// 'class' => $class, |
290
|
|
|
// 'method' => $method, |
291
|
|
|
// 'params' => $params, |
292
|
|
|
// '$_GET' => $_GET, |
293
|
|
|
// '$_POST' => $_POST, |
294
|
|
|
// ]; |
295
|
|
|
// var_dump($request, $_SERVER['argv']); |
296
|
|
|
|
297
|
|
|
return $this->createAndCallController($class, $method, $params); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
protected function callHook($hook) |
301
|
|
|
{ |
302
|
|
|
if ($this->enableHooks) |
303
|
|
|
{ |
304
|
|
|
return $this->hooks->call_hook($hook); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
return false; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
protected function setRawInputStream($string) |
311
|
|
|
{ |
312
|
|
|
if (is_string($string)) |
313
|
|
|
{ |
314
|
|
|
$INPUT =& load_class('Input', 'core'); |
315
|
|
|
CIPHPUnitTestReflection::setPrivateProperty( |
316
|
|
|
$INPUT, |
317
|
|
|
'_raw_input_stream', |
318
|
|
|
$string |
319
|
|
|
); |
320
|
|
|
} |
321
|
|
|
} |
322
|
|
|
|
323
|
|
|
protected function createAndCallController($class, $method, $params, $call_display = true) |
324
|
|
|
{ |
325
|
|
|
ob_start(); |
326
|
|
|
|
327
|
|
|
$this->callHook('pre_controller'); |
328
|
|
|
|
329
|
|
|
// Run callablePreConstructor |
330
|
|
|
if ($this->callablePreConstructors !== []) |
331
|
|
|
{ |
332
|
|
|
foreach ($this->callablePreConstructors as $callable) |
333
|
|
|
{ |
334
|
|
|
$callable(); |
335
|
|
|
} |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
// Create controller |
339
|
|
|
if (CIPHPUnitTest::wiredesignzHmvcInstalled()) |
340
|
|
|
{ |
341
|
|
|
new CI(); |
342
|
|
|
} |
343
|
|
|
$controller = new $class; |
344
|
|
|
$CI =& get_instance(); |
345
|
|
|
|
346
|
|
|
// Set CodeIgniter instance to TestCase |
347
|
|
|
$this->testCase->setCI($CI); |
348
|
|
|
|
349
|
|
|
if (! isset($CI->output->_status)) |
350
|
|
|
{ |
351
|
|
|
// Prevent overwriting, if already set in the $class::__construct() |
352
|
|
|
// Set default response code 200 |
353
|
|
|
set_status_header(200); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
// Run callable |
357
|
|
|
if ($this->callables !== []) |
358
|
|
|
{ |
359
|
|
|
foreach ($this->callables as $callable) |
360
|
|
|
{ |
361
|
|
|
$callable($CI); |
362
|
|
|
} |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
$this->callHook('post_controller_constructor'); |
366
|
|
|
|
367
|
|
|
// Call controller method |
368
|
|
|
call_user_func_array([$controller, $method], $params); |
369
|
|
|
|
370
|
|
|
$this->callHook('post_controller'); |
371
|
|
|
|
372
|
|
|
if ($call_display && $this->callHook('display_override') === false) |
373
|
|
|
{ |
374
|
|
|
$CI->output->_display(); |
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
$output = ob_get_clean(); |
378
|
|
|
|
379
|
|
|
if ($output == '') |
380
|
|
|
{ |
381
|
|
|
$output = $CI->output->get_output(); |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
return $output; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Get HTTP Status Code Info |
389
|
|
|
* |
390
|
|
|
* @return array ['code' => code, 'text' => text] |
391
|
|
|
* @throws LogicException |
392
|
|
|
*/ |
393
|
|
|
public function getStatus() |
394
|
|
|
{ |
395
|
|
|
$CI =& get_instance(); |
396
|
|
|
if (! isset($CI->output->_status)) |
397
|
|
|
{ |
398
|
|
|
throw new LogicException('Status code is not set. You must call $this->request() first'); |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
return $CI->output->_status; |
402
|
|
|
} |
403
|
|
|
} |
404
|
|
|
|
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.