1<?php 2/** 3 * Simulates a full DokuWiki HTTP Request and allows 4 * runtime inspection. 5 */ 6 7use dokuwiki\Extension\EventHandler; 8use dokuwiki\Input\Input; 9 10/** 11 * Helper class to execute a fake request 12 */ 13class TestRequest { 14 15 protected $valid_scripts = array('/doku.php', '/lib/exe/fetch.php', '/lib/exe/detail.php', '/lib/exe/ajax.php'); 16 protected $script; 17 18 protected $server = array(); 19 protected $session = array(); 20 protected $get = array(); 21 protected $post = array(); 22 protected $data = array(); 23 24 /** @var string stores the output buffer, even when it's flushed */ 25 protected $output_buffer = ''; 26 27 /** @var null|TestRequest the currently running request */ 28 static protected $running = null; 29 30 /** 31 * Get a $_SERVER var 32 * 33 * @param string $key 34 * @return mixed 35 */ 36 public function getServer($key) { 37 return $this->server[$key]; 38 } 39 40 /** 41 * Get a $_SESSION var 42 * 43 * @param string $key 44 * @return mixed 45 */ 46 public function getSession($key) { 47 return $this->session[$key]; 48 } 49 50 /** 51 * Get a $_GET var 52 * 53 * @param string $key 54 * @return mixed 55 */ 56 public function getGet($key) { 57 return $this->get[$key]; 58 } 59 60 /** 61 * Get a $_POST var 62 * 63 * @param string $key 64 * @return mixed 65 */ 66 public function getPost($key) { 67 return $this->post[$key]; 68 } 69 70 /** 71 * Get the script that will execute the request 72 * 73 * @return string 74 */ 75 public function getScript() { 76 return $this->script; 77 } 78 79 /** 80 * Set a $_SERVER var 81 * 82 * @param string $key 83 * @param mixed $value 84 */ 85 public function setServer($key, $value) { 86 $this->server[$key] = $value; 87 } 88 89 /** 90 * Set a $_SESSION var 91 * 92 * @param string $key 93 * @param mixed $value 94 */ 95 public function setSession($key, $value) { 96 $this->session[$key] = $value; 97 } 98 99 /** 100 * Set a $_GET var 101 * 102 * @param string $key 103 * @param mixed $value 104 */ 105 public function setGet($key, $value) { 106 $this->get[$key] = $value; 107 } 108 109 /** 110 * Set a $_POST var 111 * 112 * @param string $key 113 * @param mixed $value 114 */ 115 public function setPost($key, $value) { 116 $this->post[$key] = $value; 117 } 118 119 /** 120 * Executes the request 121 * 122 * @param string $uri end URL to simulate, needs to be one of the testable scripts 123 * @return TestResponse the resulting output of the request 124 */ 125 public function execute($uri = '/doku.php') { 126 global $INPUT; 127 128 // save old environment 129 $server = $_SERVER; 130 $session = $_SESSION; 131 $get = $_GET; 132 $post = $_POST; 133 $request = $_REQUEST; 134 $input = $INPUT; 135 136 // prepare the right URI 137 $this->setUri($uri); 138 139 // import all defined globals into the function scope 140 foreach(array_keys($GLOBALS) as $glb) { 141 global $$glb; 142 } 143 144 // fake environment 145 global $default_server_vars; 146 $_SERVER = array_merge($default_server_vars, $this->server); 147 $_SESSION = $this->session; 148 $_GET = $this->get; 149 $_POST = $this->post; 150 $_REQUEST = array_merge($_GET, $_POST); 151 152 // reset event handler 153 global $EVENT_HANDLER; 154 $EVENT_HANDLER = new EventHandler(); 155 156 // reset output buffer 157 $this->output_buffer = ''; 158 159 // now execute dokuwiki and grep the output 160 self::$running = $this; 161 header_remove(); 162 ob_start(array($this, 'ob_start_callback')); 163 $INPUT = new Input(); 164 include(DOKU_INC . $this->script); 165 ob_end_flush(); 166 self::$running = null; 167 168 // create the response object 169 $response = new TestResponse( 170 $this->output_buffer, 171 // cli sapi doesn't do headers, prefer xdebug_get_headers() which works under cli 172 (function_exists('xdebug_get_headers') ? xdebug_get_headers() : headers_list()), 173 $this->data 174 ); 175 176 // reset environment 177 $_SERVER = $server; 178 $_SESSION = $session; 179 $_GET = $get; 180 $_POST = $post; 181 $_REQUEST = $request; 182 $INPUT = $input; 183 184 return $response; 185 } 186 187 /** 188 * Set the virtual URI the request works against 189 * 190 * This parses the given URI and sets any contained GET variables 191 * but will not overwrite any previously set ones (eg. set via setGet()). 192 * 193 * It initializes the $_SERVER['REQUEST_URI'] and $_SERVER['QUERY_STRING'] 194 * with all set GET variables. 195 * 196 * @param string $uri end URL to simulate 197 * @throws Exception when an invalid script is passed 198 */ 199 protected function setUri($uri) { 200 if(!preg_match('#^(' . join('|', $this->valid_scripts) . ')#', $uri)) { 201 throw new Exception("$uri \n--- only " . join(', ', $this->valid_scripts) . " are supported currently"); 202 } 203 204 $params = array(); 205 list($uri, $query) = sexplode('?', $uri, 2); 206 if($query) parse_str($query, $params); 207 208 $this->script = substr($uri, 1); 209 $this->get = array_merge($params, $this->get); 210 if(count($this->get)) { 211 $query = '?' . http_build_query($this->get, '', '&'); 212 $query = str_replace( 213 array('%3A', '%5B', '%5D'), 214 array(':', '[', ']'), 215 $query 216 ); 217 $uri = $uri . $query; 218 } 219 220 $this->setServer('QUERY_STRING', $query); 221 $this->setServer('REQUEST_URI', $uri); 222 } 223 224 /** 225 * Simulate a POST request with the given variables 226 * 227 * @param array $post all the POST parameters to use 228 * @param string $uri end URL to simulate 229 * @return TestResponse 230 */ 231 public function post($post = array(), $uri = '/doku.php') { 232 $this->post = array_merge($this->post, $post); 233 $this->setServer('REQUEST_METHOD', 'POST'); 234 return $this->execute($uri); 235 } 236 237 /** 238 * Simulate a GET request with the given variables 239 * 240 * @param array $get all the GET parameters to use 241 * @param string $uri end URL to simulate 242 * @return TestResponse 243 */ 244 public function get($get = array(), $uri = '/doku.php') { 245 $this->get = array_merge($this->get, $get); 246 $this->setServer('REQUEST_METHOD', 'GET'); 247 return $this->execute($uri); 248 } 249 250 /** 251 * Callback for ob_start 252 * 253 * This continues to fill our own buffer, even when some part 254 * of the code askes for flushing the buffers 255 * 256 * @param string $buffer 257 */ 258 public function ob_start_callback($buffer) { 259 $this->output_buffer .= $buffer; 260 } 261 262 /** 263 * Access the TestRequest from the executed code 264 * 265 * This allows certain functions to access the TestRequest that is accessing them 266 * to add additional info. 267 * 268 * @return null|TestRequest the currently executed request if any 269 */ 270 public static function getRunning() { 271 return self::$running; 272 } 273 274 /** 275 * Store data to be read in the response later 276 * 277 * When called multiple times with the same key, the data is appended to this 278 * key's array 279 * 280 * @param string $key the identifier for this information 281 * @param mixed $value arbitrary data to store 282 */ 283 public function addData($key, $value) { 284 if(!isset($this->data[$key])) $this->data[$key] = array(); 285 $this->data[$key][] = $value; 286 } 287} 288