first reimport from platal
[platal.git] / htdocs / TESTS / simpletest / url.php
1 <?php
2 /**
3 * base include file for SimpleTest
4 * @package SimpleTest
5 * @subpackage WebTester
6 * @version $Id: url.php,v 1.13 2004/08/15 21:53:08 lastcraft Exp $
7 */
8
9 /**
10 * Bundle of GET/POST parameters. Can include
11 * repeated parameters.
12 * @package SimpleTest
13 * @subpackage WebTester
14 */
15 class SimpleQueryString {
16 var $_request;
17
18 /**
19 * Starts empty.
20 * @param array $query/SimpleQueryString Hash of parameters.
21 * Multiple values are
22 * as lists on a single key.
23 * @access public
24 */
25 function SimpleQueryString($query = false) {
26 if (! $query) {
27 $query = array();
28 }
29 $this->_request = array();
30 $this->merge($query);
31 }
32
33 /**
34 * Adds a parameter to the query.
35 * @param string $key Key to add value to.
36 * @param string/array $value New data.
37 * @access public
38 */
39 function add($key, $value) {
40 if (! isset($this->_request[$key])) {
41 $this->_request[$key] = array();
42 }
43 if (is_array($value)) {
44 foreach ($value as $item) {
45 $this->_request[$key][] = $item;
46 }
47 } else {
48 $this->_request[$key][] = $value;
49 }
50 }
51
52 /**
53 * Adds a set of parameters to this query.
54 * @param array $query/SimpleQueryString Hash of parameters.
55 * Multiple values are
56 * as lists on a single key.
57 * @access public
58 */
59 function merge($query) {
60 if (is_object($query)) {
61 foreach ($query->getKeys() as $key) {
62 $this->add($key, $query->getValue($key));
63 }
64 } else {
65 foreach ($query as $key => $value) {
66 $this->add($key, $value);
67 }
68 }
69 }
70
71 /**
72 * Accessor for single value.
73 * @return string/array False if missing, string
74 * if present and array if
75 * multiple entries.
76 * @access public
77 */
78 function getValue($key) {
79 if (! isset($this->_request[$key])) {
80 return false;
81 } elseif (count($this->_request[$key]) == 1) {
82 return $this->_request[$key][0];
83 } else {
84 return $this->_request[$key];
85 }
86 }
87
88 /**
89 * Accessor for key list.
90 * @return array List of keys present.
91 * @access public
92 */
93 function getKeys() {
94 return array_keys($this->_request);
95 }
96
97 /**
98 * Gets all parameters as structured hash. Repeated
99 * values are list values.
100 * @return array Hash of keys and value sets.
101 * @access public
102 */
103 function getAll() {
104 $values = array();
105 foreach ($this->_request as $key => $value) {
106 $values[$key] = (count($value) == 1 ? $value[0] : $value);
107 }
108 return $values;
109 }
110
111 /**
112 * Renders the query string as a URL encoded
113 * request part.
114 * @return string Part of URL.
115 * @access public
116 */
117 function asString() {
118 $statements = array();
119 foreach ($this->_request as $key => $values) {
120 foreach ($values as $value) {
121 $statements[] = "$key=" . urlencode($value);
122 }
123 }
124 return implode('&', $statements);
125 }
126 }
127
128 /**
129 * URL parser to replace parse_url() PHP function which
130 * got broken in PHP 4.3.0. Adds some browser specific
131 * functionality such as expandomatic expansion.
132 * Guesses a bit trying to separate the host from
133 * the path.
134 * @package SimpleTest
135 * @subpackage WebTester
136 */
137 class SimpleUrl {
138 var $_scheme;
139 var $_username;
140 var $_password;
141 var $_host;
142 var $_port;
143 var $_path;
144 var $_request;
145 var $_fragment;
146 var $_x;
147 var $_y;
148 var $_target;
149
150 /**
151 * Constructor. Parses URL into sections.
152 * @param string $url Incoming URL.
153 * @access public
154 */
155 function SimpleUrl($url) {
156 list($this->_x, $this->_y) = $this->_chompCoordinates($url);
157 $this->_scheme = $this->_chompScheme($url);
158 list($this->_username, $this->_password) = $this->_chompLogin($url);
159 $this->_host = $this->_chompHost($url);
160 $this->_port = false;
161 if (preg_match('/(.*?):(.*)/', $this->_host, $host_parts)) {
162 $this->_host = $host_parts[1];
163 $this->_port = (integer)$host_parts[2];
164 }
165 $this->_path = $this->_chompPath($url);
166 $this->_request = $this->_parseRequest($this->_chompRequest($url));
167 $this->_fragment = (strncmp($url, "#", 1) == 0 ? substr($url, 1) : false);
168 $this->_target = false;
169 }
170
171 /**
172 * Extracts the X, Y coordinate pair from an image map.
173 * @param string $url URL so far. The coordinates will be
174 * removed.
175 * @return array X, Y as a pair of integers.
176 * @access private
177 */
178 function _chompCoordinates(&$url) {
179 if (preg_match('/(.*)\?(\d+),(\d+)$/', $url, $matches)) {
180 $url = $matches[1];
181 return array((integer)$matches[2], (integer)$matches[3]);
182 }
183 return array(false, false);
184 }
185
186 /**
187 * Extracts the scheme part of an incoming URL.
188 * @param string $url URL so far. The scheme will be
189 * removed.
190 * @return string Scheme part or false.
191 * @access private
192 */
193 function _chompScheme(&$url) {
194 if (preg_match('/(.*?):(\/\/)(.*)/', $url, $matches)) {
195 $url = $matches[2] . $matches[3];
196 return $matches[1];
197 }
198 return false;
199 }
200
201 /**
202 * Extracts the username and password from the
203 * incoming URL. The // prefix will be reattached
204 * to the URL after the doublet is extracted.
205 * @param string $url URL so far. The username and
206 * password are removed.
207 * @return array Two item list of username and
208 * password. Will urldecode() them.
209 * @access private
210 */
211 function _chompLogin(&$url) {
212 $prefix = '';
213 if (preg_match('/(\/\/)(.*)/', $url, $matches)) {
214 $prefix = $matches[1];
215 $url = $matches[2];
216 }
217 if (preg_match('/(.*?)@(.*)/', $url, $matches)) {
218 $url = $prefix . $matches[2];
219 $parts = split(":", $matches[1]);
220 return array(
221 urldecode($parts[0]),
222 isset($parts[1]) ? urldecode($parts[1]) : false);
223 }
224 $url = $prefix . $url;
225 return array(false, false);
226 }
227
228 /**
229 * Extracts the host part of an incoming URL.
230 * Includes the port number part. Will extract
231 * the host if it starts with // or it has
232 * a top level domain or it has at least two
233 * dots.
234 * @param string $url URL so far. The host will be
235 * removed.
236 * @return string Host part guess or false.
237 * @access private
238 */
239 function _chompHost(&$url) {
240 if (preg_match('/(\/\/)(.*?)(\/.*|\?.*|#.*|$)/', $url, $matches)) {
241 $url = $matches[3];
242 return $matches[2];
243 }
244 if (preg_match('/(.*?)(\.\.\/|\.\/|\/|\?|#|$)(.*)/', $url, $matches)) {
245 if (preg_match('/[a-z0-9\-]+\.(com|edu|net|org|gov|mil|int)/i', $matches[1])) {
246 $url = $matches[2] . $matches[3];
247 return $matches[1];
248 } elseif (preg_match('/[a-z0-9\-]+\.[a-z0-9\-]+\.[a-z0-9\-]+/i', $matches[1])) {
249 $url = $matches[2] . $matches[3];
250 return $matches[1];
251 }
252 }
253 return false;
254 }
255
256 /**
257 * Extracts the path information from the incoming
258 * URL. Strips this path from the URL.
259 * @param string $url URL so far. The host will be
260 * removed.
261 * @return string Path part or '/'.
262 * @access private
263 */
264 function _chompPath(&$url) {
265 if (preg_match('/(.*?)(\?|#|$)(.*)/', $url, $matches)) {
266 $url = $matches[2] . $matches[3];
267 return ($matches[1] ? $matches[1] : '');
268 }
269 return '';
270 }
271
272 /**
273 * Strips off the request data.
274 * @param string $url URL so far. The request will be
275 * removed.
276 * @return string Raw request part.
277 * @access private
278 */
279 function _chompRequest(&$url) {
280 if (preg_match('/\?(.*?)(#|$)(.*)/', $url, $matches)) {
281 $url = $matches[2] . $matches[3];
282 return $matches[1];
283 }
284 return '';
285 }
286
287 /**
288 * Breaks the request down into an object.
289 * @param string $raw Raw request.
290 * @return SimpleQueryString Parsed data.
291 * @access private
292 */
293 function _parseRequest($raw) {
294 $request = new SimpleQueryString();
295 foreach (split("&", $raw) as $pair) {
296 if (preg_match('/(.*?)=(.*)/', $pair, $matches)) {
297 $request->add($matches[1], urldecode($matches[2]));
298 } elseif ($pair) {
299 $request->add($pair, '');
300 }
301 }
302 return $request;
303 }
304
305 /**
306 * Accessor for protocol part.
307 * @param string $default Value to use if not present.
308 * @return string Scheme name, e.g "http".
309 * @access public
310 */
311 function getScheme($default = false) {
312 return $this->_scheme ? $this->_scheme : $default;
313 }
314
315 /**
316 * Accessor for user name.
317 * @return string Username preceding host.
318 * @access public
319 */
320 function getUsername() {
321 return $this->_username;
322 }
323
324 /**
325 * Accessor for password.
326 * @return string Password preceding host.
327 * @access public
328 */
329 function getPassword() {
330 return $this->_password;
331 }
332
333 /**
334 * Accessor for hostname and port.
335 * @param string $default Value to use if not present.
336 * @return string Hostname only.
337 * @access public
338 */
339 function getHost($default = false) {
340 return $this->_host ? $this->_host : $default;
341 }
342
343 /**
344 * Accessor for top level domain.
345 * @return string Last part of host.
346 * @access public
347 */
348 function getTld() {
349 $path_parts = pathinfo($this->getHost());
350 return (isset($path_parts['extension']) ? $path_parts['extension'] : false);
351 }
352
353 /**
354 * Accessor for port number.
355 * @return integer TCP/IP port number.
356 * @access public
357 */
358 function getPort() {
359 return $this->_port;
360 }
361
362 /**
363 * Accessor for path.
364 * @return string Full path including leading slash if implied.
365 * @access public
366 */
367 function getPath() {
368 if (! $this->_path && $this->_host) {
369 return '/';
370 }
371 return $this->_path;
372 }
373
374 /**
375 * Accessor for page if any. This may be a
376 * directory name if ambiguious.
377 * @return Page name.
378 * @access public
379 */
380 function getPage() {
381 if (! preg_match('/([^\/]*?)$/', $this->getPath(), $matches)) {
382 return false;
383 }
384 return $matches[1];
385 }
386
387 /**
388 * Gets the path to the page.
389 * @return string Path less the page.
390 * @access public
391 */
392 function getBasePath() {
393 if (! preg_match('/(.*\/)[^\/]*?$/', $this->getPath(), $matches)) {
394 return false;
395 }
396 return $matches[1];
397 }
398
399 /**
400 * Accessor for fragment at end of URL after the "#".
401 * @return string Part after "#".
402 * @access public
403 */
404 function getFragment() {
405 return $this->_fragment;
406 }
407
408 /**
409 * Accessor for horizontal image coordinate.
410 * @return integer X value.
411 * @access public
412 */
413 function getX() {
414 return $this->_x;
415 }
416
417 /**
418 * Accessor for vertical image coordinate.
419 * @return integer Y value.
420 * @access public
421 */
422 function getY() {
423 return $this->_y;
424 }
425
426 /**
427 * Accessor for current request parameters
428 * in URL string form
429 * @return string Form is string "?a=1&b=2", etc.
430 * @access public
431 */
432 function getEncodedRequest() {
433 $query = $this->_request;
434 $encoded = $query->asString();
435 if ($encoded) {
436 return "?$encoded";
437 }
438 return '';
439 }
440
441 /**
442 * Encodes parameters as HTTP request parameters.
443 * @param hash $parameters Request as hash.
444 * @return string Encoded request.
445 * @access public
446 * @static
447 */
448 function encodeRequest($parameters) {
449 if (! $parameters) {
450 return '';
451 }
452 $query = &new SimpleQueryString();
453 foreach ($parameters as $key => $value) {
454 $query->add($key, $value);
455 }
456 return $query->asString();
457 }
458
459 /**
460 * Accessor for current request parameters
461 * as an object.
462 * @return array Hash of name and value pairs. The
463 * values will be lists for repeated items.
464 * @access public
465 */
466 function getRequest() {
467 return $this->_request->getAll();
468 }
469
470 /**
471 * Adds an additional parameter to the request.
472 * @param string $key Name of parameter.
473 * @param string $value Value as string.
474 * @access public
475 */
476 function addRequestParameter($key, $value) {
477 $this->_request->add($key, $value);
478 }
479
480 /**
481 * Adds additional parameters to the request.
482 * @param hash $parameters Hash of additional parameters.
483 * @access public
484 */
485 function addRequestParameters($parameters) {
486 if ($parameters) {
487 $this->_request->merge($parameters);
488 }
489 }
490
491 /**
492 * Clears down all parameters.
493 * @access public
494 */
495 function clearRequest() {
496 $this->_request = &new SimpleQueryString();
497 }
498
499 /**
500 * Sets image coordinates. Set to flase to clear
501 * them.
502 * @param integer $x Horizontal position.
503 * @param integer $y Vertical position.
504 * @access public
505 */
506 function setCoordinates($x = false, $y = false) {
507 if (($x === false) || ($y === false)) {
508 $this->_x = $this->_y = false;
509 return;
510 }
511 $this->_x = (integer)$x;
512 $this->_y = (integer)$y;
513 }
514
515 /**
516 * Gets the frame target if present. Although
517 * not strictly part of the URL specification it
518 * acts as similarily to the browser.
519 * @return boolean/string Frame name or false if none.
520 * @access public
521 */
522 function getTarget() {
523 return $this->_target;
524 }
525
526 /**
527 * Attaches a frame target.
528 * @param string $frame Name of frame.
529 * @access public
530 */
531 function setTarget($frame) {
532 $this->_target = $frame;
533 }
534
535 /**
536 * Renders the URL back into a string.
537 * @return string URL in canonical form.
538 * @access public
539 */
540 function asString() {
541 $scheme = $identity = $host = $path = $encoded = $fragment = '';
542 if ($this->_username && $this->_password) {
543 $identity = $this->_username . ':' . $this->_password . '@';
544 }
545 if ($this->getHost()) {
546 $scheme = $this->getScheme() ? $this->getScheme() : 'http';
547 $host = $this->getHost();
548 }
549 if (substr($this->_path, 0, 1) == '/') {
550 $path = $this->normalisePath($this->_path);
551 }
552 $encoded = $this->getEncodedRequest();
553 $fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
554 $coords = ($this->_x !== false) ? '?' . $this->_x . ',' . $this->_y : '';
555 return "$scheme://$identity$host$path$encoded$fragment$coords";
556 }
557
558 /**
559 * Replaces unknown sections to turn a relative
560 * URL into an absolute one. The base URL can
561 * be either a string or a SimpleUrl object.
562 * @param string/SimpleUrl $base Base URL.
563 * @access public
564 */
565 function makeAbsolute($base) {
566 if (! is_object($base)) {
567 $base = new SimpleUrl($base);
568 }
569 $scheme = $this->getScheme() ? $this->getScheme() : $base->getScheme();
570 $host = $this->getHost();
571 $port = $this->getPort() ? ':' . $this->getPort() : '';
572 $path = $this->normalisePath($this->_path);
573 if (! $host) {
574 $host = $base->getHost();
575 $port = $base->getPort() ? ':' . $base->getPort() : '';
576 if ($this->_isRelativePath($this->_path)) {
577 $path = $this->normalisePath($base->getBasePath() . $this->_path);
578 }
579 }
580 $identity = $this->_getIdentity() ? $this->_getIdentity() . '@' : '';
581 $encoded = $this->getEncodedRequest();
582 $fragment = $this->getFragment() ? '#'. $this->getFragment() : '';
583 $coords = ($this->_x !== false) ? '?' . $this->_x . ',' . $this->_y : '';
584 return new SimpleUrl("$scheme://$identity$host$port$path$encoded$fragment$coords");
585 }
586
587 /**
588 * Simple test to see if a path part is relative.
589 * @param string $path Path to test.
590 * @return boolean True if starts with a "/".
591 * @access private
592 */
593 function _isRelativePath($path) {
594 return (substr($path, 0, 1) != '/');
595 }
596
597 /**
598 * Extracts the username and password for use in rendering
599 * a URL.
600 * @return string/boolean Form of username:password@ or false.
601 * @access private
602 */
603 function _getIdentity() {
604 if ($this->_username && $this->_password) {
605 return $this->_username . ':' . $this->_password;
606 }
607 return false;
608 }
609
610 /**
611 * Replaces . and .. sections of the path.
612 * @param string $path Unoptimised path.
613 * @return string Path with dots removed if possible.
614 * @access public
615 */
616 function normalisePath($path) {
617 $path = preg_replace('|/[^/]+/\.\./|', '/', $path);
618 return preg_replace('|/\./|', '/', $path);
619 }
620 }
621 ?>