3 * base include file for SimpleTest
5 * @subpackage WebTester
6 * @version $Id: url.php,v 1.13 2004/08/15 21:53:08 lastcraft Exp $
10 * Bundle of GET/POST parameters. Can include
11 * repeated parameters.
13 * @subpackage WebTester
15 class SimpleQueryString
{
20 * @param array $query/SimpleQueryString Hash of parameters.
22 * as lists on a single key.
25 function SimpleQueryString($query = false
) {
29 $this->_request
= array();
34 * Adds a parameter to the query.
35 * @param string $key Key to add value to.
36 * @param string/array $value New data.
39 function add($key, $value) {
40 if (! isset($this->_request
[$key])) {
41 $this->_request
[$key] = array();
43 if (is_array($value)) {
44 foreach ($value as $item) {
45 $this->_request
[$key][] = $item;
48 $this->_request
[$key][] = $value;
53 * Adds a set of parameters to this query.
54 * @param array $query/SimpleQueryString Hash of parameters.
56 * as lists on a single key.
59 function merge($query) {
60 if (is_object($query)) {
61 foreach ($query->getKeys() as $key) {
62 $this->add($key, $query->getValue($key));
65 foreach ($query as $key => $value) {
66 $this->add($key, $value);
72 * Accessor for single value.
73 * @return string/array False if missing, string
74 * if present and array if
78 function getValue($key) {
79 if (! isset($this->_request
[$key])) {
81 } elseif (count($this->_request
[$key]) == 1) {
82 return $this->_request
[$key][0];
84 return $this->_request
[$key];
89 * Accessor for key list.
90 * @return array List of keys present.
94 return array_keys($this->_request
);
98 * Gets all parameters as structured hash. Repeated
99 * values are list values.
100 * @return array Hash of keys and value sets.
105 foreach ($this->_request
as $key => $value) {
106 $values[$key] = (count($value) == 1 ?
$value[0] : $value);
112 * Renders the query string as a URL encoded
114 * @return string Part of URL.
117 function asString() {
118 $statements = array();
119 foreach ($this->_request
as $key => $values) {
120 foreach ($values as $value) {
121 $statements[] = "$key=" . urlencode($value);
124 return implode('&', $statements);
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
134 * @package SimpleTest
135 * @subpackage WebTester
151 * Constructor. Parses URL into sections.
152 * @param string $url Incoming URL.
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];
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
;
172 * Extracts the X, Y coordinate pair from an image map.
173 * @param string $url URL so far. The coordinates will be
175 * @return array X, Y as a pair of integers.
178 function _chompCoordinates(&$url) {
179 if (preg_match('/(.*)\?(\d+),(\d+)$/', $url, $matches)) {
181 return array((integer)$matches[2], (integer)$matches[3]);
183 return array(false
, false
);
187 * Extracts the scheme part of an incoming URL.
188 * @param string $url URL so far. The scheme will be
190 * @return string Scheme part or false.
193 function _chompScheme(&$url) {
194 if (preg_match('/(.*?):(\/\/)(.*)/', $url, $matches)) {
195 $url = $matches[2] . $matches[3];
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.
211 function _chompLogin(&$url) {
213 if (preg_match('/(\/\/)(.*)/', $url, $matches)) {
214 $prefix = $matches[1];
217 if (preg_match('/(.*?)@(.*)/', $url, $matches)) {
218 $url = $prefix . $matches[2];
219 $parts = explode(":", $matches[1]);
221 urldecode($parts[0]),
222 isset($parts[1]) ?
urldecode($parts[1]) : false
);
224 $url = $prefix . $url;
225 return array(false
, false
);
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
234 * @param string $url URL so far. The host will be
236 * @return string Host part guess or false.
239 function _chompHost(&$url) {
240 if (preg_match('/(\/\/)(.*?)(\/.*|\?.*|#.*|$)/', $url, $matches)) {
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];
248 } elseif (preg_match('/[a-z0-9\-]+\.[a-z0-9\-]+\.[a-z0-9\-]+/i', $matches[1])) {
249 $url = $matches[2] . $matches[3];
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
261 * @return string Path part or '/'.
264 function _chompPath(&$url) {
265 if (preg_match('/(.*?)(\?|#|$)(.*)/', $url, $matches)) {
266 $url = $matches[2] . $matches[3];
267 return ($matches[1] ?
$matches[1] : '');
273 * Strips off the request data.
274 * @param string $url URL so far. The request will be
276 * @return string Raw request part.
279 function _chompRequest(&$url) {
280 if (preg_match('/\?(.*?)(#|$)(.*)/', $url, $matches)) {
281 $url = $matches[2] . $matches[3];
288 * Breaks the request down into an object.
289 * @param string $raw Raw request.
290 * @return SimpleQueryString Parsed data.
293 function _parseRequest($raw) {
294 $request = new SimpleQueryString();
295 foreach (explode("&", $raw) as $pair) {
296 if (preg_match('/(.*?)=(.*)/', $pair, $matches)) {
297 $request->add($matches[1], urldecode($matches[2]));
299 $request->add($pair, '');
306 * Accessor for protocol part.
307 * @param string $default Value to use if not present.
308 * @return string Scheme name, e.g "http".
311 function getScheme($default = false
) {
312 return $this->_scheme ?
$this->_scheme
: $default;
316 * Accessor for user name.
317 * @return string Username preceding host.
320 function getUsername() {
321 return $this->_username
;
325 * Accessor for password.
326 * @return string Password preceding host.
329 function getPassword() {
330 return $this->_password
;
334 * Accessor for hostname and port.
335 * @param string $default Value to use if not present.
336 * @return string Hostname only.
339 function getHost($default = false
) {
340 return $this->_host ?
$this->_host
: $default;
344 * Accessor for top level domain.
345 * @return string Last part of host.
349 $path_parts = pathinfo($this->getHost());
350 return (isset($path_parts['extension']) ?
$path_parts['extension'] : false
);
354 * Accessor for port number.
355 * @return integer TCP/IP port number.
364 * @return string Full path including leading slash if implied.
368 if (! $this->_path
&& $this->_host
) {
375 * Accessor for page if any. This may be a
376 * directory name if ambiguious.
381 if (! preg_match('/([^\/]*?)$/', $this->getPath(), $matches)) {
388 * Gets the path to the page.
389 * @return string Path less the page.
392 function getBasePath() {
393 if (! preg_match('/(.*\/)[^\/]*?$/', $this->getPath(), $matches)) {
400 * Accessor for fragment at end of URL after the "#".
401 * @return string Part after "#".
404 function getFragment() {
405 return $this->_fragment
;
409 * Accessor for horizontal image coordinate.
410 * @return integer X value.
418 * Accessor for vertical image coordinate.
419 * @return integer Y value.
427 * Accessor for current request parameters
429 * @return string Form is string "?a=1&b=2", etc.
432 function getEncodedRequest() {
433 $query = $this->_request
;
434 $encoded = $query->asString();
442 * Encodes parameters as HTTP request parameters.
443 * @param hash $parameters Request as hash.
444 * @return string Encoded request.
448 function encodeRequest($parameters) {
452 $query = &new SimpleQueryString();
453 foreach ($parameters as $key => $value) {
454 $query->add($key, $value);
456 return $query->asString();
460 * Accessor for current request parameters
462 * @return array Hash of name and value pairs. The
463 * values will be lists for repeated items.
466 function getRequest() {
467 return $this->_request
->getAll();
471 * Adds an additional parameter to the request.
472 * @param string $key Name of parameter.
473 * @param string $value Value as string.
476 function addRequestParameter($key, $value) {
477 $this->_request
->add($key, $value);
481 * Adds additional parameters to the request.
482 * @param hash $parameters Hash of additional parameters.
485 function addRequestParameters($parameters) {
487 $this->_request
->merge($parameters);
492 * Clears down all parameters.
495 function clearRequest() {
496 $this->_request
= &new SimpleQueryString();
500 * Sets image coordinates. Set to flase to clear
502 * @param integer $x Horizontal position.
503 * @param integer $y Vertical position.
506 function setCoordinates($x = false
, $y = false
) {
507 if (($x === false
) ||
($y === false
)) {
508 $this->_x
= $this->_y
= false
;
511 $this->_x
= (integer)$x;
512 $this->_y
= (integer)$y;
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.
522 function getTarget() {
523 return $this->_target
;
527 * Attaches a frame target.
528 * @param string $frame Name of frame.
531 function setTarget($frame) {
532 $this->_target
= $frame;
536 * Renders the URL back into a string.
537 * @return string URL in canonical form.
540 function asString() {
541 $scheme = $identity = $host = $path = $encoded = $fragment = '';
542 if ($this->_username
&& $this->_password
) {
543 $identity = $this->_username
. ':' . $this->_password
. '@';
545 if ($this->getHost()) {
546 $scheme = $this->getScheme() ?
$this->getScheme() : 'http';
547 $host = $this->getHost();
549 if (substr($this->_path
, 0, 1) == '/') {
550 $path = $this->normalisePath($this->_path
);
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";
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.
565 function makeAbsolute($base) {
566 if (! is_object($base)) {
567 $base = new SimpleUrl($base);
569 $scheme = $this->getScheme() ?
$this->getScheme() : $base->getScheme();
570 $host = $this->getHost();
571 $port = $this->getPort() ?
':' . $this->getPort() : '';
572 $path = $this->normalisePath($this->_path
);
574 $host = $base->getHost();
575 $port = $base->getPort() ?
':' . $base->getPort() : '';
576 if ($this->_isRelativePath($this->_path
)) {
577 $path = $this->normalisePath($base->getBasePath() . $this->_path
);
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");
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 "/".
593 function _isRelativePath($path) {
594 return (substr($path, 0, 1) != '/');
598 * Extracts the username and password for use in rendering
600 * @return string/boolean Form of username:password@ or false.
603 function _getIdentity() {
604 if ($this->_username
&& $this->_password
) {
605 return $this->_username
. ':' . $this->_password
;
611 * Replaces . and .. sections of the path.
612 * @param string $path Unoptimised path.
613 * @return string Path with dots removed if possible.
616 function normalisePath($path) {
617 $path = preg_replace('|/[^/]+/\.\./|', '/', $path);
618 return preg_replace('|/\./|', '/', $path);