xref: /dokuwiki/_test/core/TestRequest.php (revision 68d9ab902296b21ef6bb4220780c9ea5b573c888)
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