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