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