annotate vendor/symfony/browser-kit/Client.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 7a779792577d
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 /*
Chris@0 4 * This file is part of the Symfony package.
Chris@0 5 *
Chris@0 6 * (c) Fabien Potencier <fabien@symfony.com>
Chris@0 7 *
Chris@0 8 * For the full copyright and license information, please view the LICENSE
Chris@0 9 * file that was distributed with this source code.
Chris@0 10 */
Chris@0 11
Chris@0 12 namespace Symfony\Component\BrowserKit;
Chris@0 13
Chris@0 14 use Symfony\Component\DomCrawler\Crawler;
Chris@0 15 use Symfony\Component\DomCrawler\Link;
Chris@0 16 use Symfony\Component\DomCrawler\Form;
Chris@0 17 use Symfony\Component\Process\PhpProcess;
Chris@0 18
Chris@0 19 /**
Chris@0 20 * Client simulates a browser.
Chris@0 21 *
Chris@0 22 * To make the actual request, you need to implement the doRequest() method.
Chris@0 23 *
Chris@0 24 * If you want to be able to run requests in their own process (insulated flag),
Chris@0 25 * you need to also implement the getScript() method.
Chris@0 26 *
Chris@0 27 * @author Fabien Potencier <fabien@symfony.com>
Chris@0 28 */
Chris@0 29 abstract class Client
Chris@0 30 {
Chris@0 31 protected $history;
Chris@0 32 protected $cookieJar;
Chris@0 33 protected $server = array();
Chris@0 34 protected $internalRequest;
Chris@0 35 protected $request;
Chris@0 36 protected $internalResponse;
Chris@0 37 protected $response;
Chris@0 38 protected $crawler;
Chris@0 39 protected $insulated = false;
Chris@0 40 protected $redirect;
Chris@0 41 protected $followRedirects = true;
Chris@0 42
Chris@0 43 private $maxRedirects = -1;
Chris@0 44 private $redirectCount = 0;
Chris@0 45 private $isMainRequest = true;
Chris@0 46
Chris@0 47 /**
Chris@0 48 * Constructor.
Chris@0 49 *
Chris@0 50 * @param array $server The server parameters (equivalent of $_SERVER)
Chris@0 51 * @param History $history A History instance to store the browser history
Chris@0 52 * @param CookieJar $cookieJar A CookieJar instance to store the cookies
Chris@0 53 */
Chris@0 54 public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null)
Chris@0 55 {
Chris@0 56 $this->setServerParameters($server);
Chris@0 57 $this->history = $history ?: new History();
Chris@0 58 $this->cookieJar = $cookieJar ?: new CookieJar();
Chris@0 59 }
Chris@0 60
Chris@0 61 /**
Chris@0 62 * Sets whether to automatically follow redirects or not.
Chris@0 63 *
Chris@0 64 * @param bool $followRedirect Whether to follow redirects
Chris@0 65 */
Chris@0 66 public function followRedirects($followRedirect = true)
Chris@0 67 {
Chris@0 68 $this->followRedirects = (bool) $followRedirect;
Chris@0 69 }
Chris@0 70
Chris@0 71 /**
Chris@0 72 * Returns whether client automatically follows redirects or not.
Chris@0 73 *
Chris@0 74 * @return bool
Chris@0 75 */
Chris@0 76 public function isFollowingRedirects()
Chris@0 77 {
Chris@0 78 return $this->followRedirects;
Chris@0 79 }
Chris@0 80
Chris@0 81 /**
Chris@0 82 * Sets the maximum number of requests that crawler can follow.
Chris@0 83 *
Chris@0 84 * @param int $maxRedirects
Chris@0 85 */
Chris@0 86 public function setMaxRedirects($maxRedirects)
Chris@0 87 {
Chris@0 88 $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects;
Chris@0 89 $this->followRedirects = -1 != $this->maxRedirects;
Chris@0 90 }
Chris@0 91
Chris@0 92 /**
Chris@0 93 * Returns the maximum number of requests that crawler can follow.
Chris@0 94 *
Chris@0 95 * @return int
Chris@0 96 */
Chris@0 97 public function getMaxRedirects()
Chris@0 98 {
Chris@0 99 return $this->maxRedirects;
Chris@0 100 }
Chris@0 101
Chris@0 102 /**
Chris@0 103 * Sets the insulated flag.
Chris@0 104 *
Chris@0 105 * @param bool $insulated Whether to insulate the requests or not
Chris@0 106 *
Chris@0 107 * @throws \RuntimeException When Symfony Process Component is not installed
Chris@0 108 */
Chris@0 109 public function insulate($insulated = true)
Chris@0 110 {
Chris@0 111 if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
Chris@0 112 throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
Chris@0 113 }
Chris@0 114
Chris@0 115 $this->insulated = (bool) $insulated;
Chris@0 116 }
Chris@0 117
Chris@0 118 /**
Chris@0 119 * Sets server parameters.
Chris@0 120 *
Chris@0 121 * @param array $server An array of server parameters
Chris@0 122 */
Chris@0 123 public function setServerParameters(array $server)
Chris@0 124 {
Chris@0 125 $this->server = array_merge(array(
Chris@0 126 'HTTP_USER_AGENT' => 'Symfony BrowserKit',
Chris@0 127 ), $server);
Chris@0 128 }
Chris@0 129
Chris@0 130 /**
Chris@0 131 * Sets single server parameter.
Chris@0 132 *
Chris@0 133 * @param string $key A key of the parameter
Chris@0 134 * @param string $value A value of the parameter
Chris@0 135 */
Chris@0 136 public function setServerParameter($key, $value)
Chris@0 137 {
Chris@0 138 $this->server[$key] = $value;
Chris@0 139 }
Chris@0 140
Chris@0 141 /**
Chris@0 142 * Gets single server parameter for specified key.
Chris@0 143 *
Chris@0 144 * @param string $key A key of the parameter to get
Chris@0 145 * @param string $default A default value when key is undefined
Chris@0 146 *
Chris@0 147 * @return string A value of the parameter
Chris@0 148 */
Chris@0 149 public function getServerParameter($key, $default = '')
Chris@0 150 {
Chris@0 151 return isset($this->server[$key]) ? $this->server[$key] : $default;
Chris@0 152 }
Chris@0 153
Chris@0 154 /**
Chris@0 155 * Returns the History instance.
Chris@0 156 *
Chris@0 157 * @return History A History instance
Chris@0 158 */
Chris@0 159 public function getHistory()
Chris@0 160 {
Chris@0 161 return $this->history;
Chris@0 162 }
Chris@0 163
Chris@0 164 /**
Chris@0 165 * Returns the CookieJar instance.
Chris@0 166 *
Chris@0 167 * @return CookieJar A CookieJar instance
Chris@0 168 */
Chris@0 169 public function getCookieJar()
Chris@0 170 {
Chris@0 171 return $this->cookieJar;
Chris@0 172 }
Chris@0 173
Chris@0 174 /**
Chris@0 175 * Returns the current Crawler instance.
Chris@0 176 *
Chris@0 177 * @return Crawler|null A Crawler instance
Chris@0 178 */
Chris@0 179 public function getCrawler()
Chris@0 180 {
Chris@0 181 return $this->crawler;
Chris@0 182 }
Chris@0 183
Chris@0 184 /**
Chris@0 185 * Returns the current BrowserKit Response instance.
Chris@0 186 *
Chris@0 187 * @return Response|null A BrowserKit Response instance
Chris@0 188 */
Chris@0 189 public function getInternalResponse()
Chris@0 190 {
Chris@0 191 return $this->internalResponse;
Chris@0 192 }
Chris@0 193
Chris@0 194 /**
Chris@0 195 * Returns the current origin response instance.
Chris@0 196 *
Chris@0 197 * The origin response is the response instance that is returned
Chris@0 198 * by the code that handles requests.
Chris@0 199 *
Chris@0 200 * @return object|null A response instance
Chris@0 201 *
Chris@0 202 * @see doRequest()
Chris@0 203 */
Chris@0 204 public function getResponse()
Chris@0 205 {
Chris@0 206 return $this->response;
Chris@0 207 }
Chris@0 208
Chris@0 209 /**
Chris@0 210 * Returns the current BrowserKit Request instance.
Chris@0 211 *
Chris@0 212 * @return Request|null A BrowserKit Request instance
Chris@0 213 */
Chris@0 214 public function getInternalRequest()
Chris@0 215 {
Chris@0 216 return $this->internalRequest;
Chris@0 217 }
Chris@0 218
Chris@0 219 /**
Chris@0 220 * Returns the current origin Request instance.
Chris@0 221 *
Chris@0 222 * The origin request is the request instance that is sent
Chris@0 223 * to the code that handles requests.
Chris@0 224 *
Chris@0 225 * @return object|null A Request instance
Chris@0 226 *
Chris@0 227 * @see doRequest()
Chris@0 228 */
Chris@0 229 public function getRequest()
Chris@0 230 {
Chris@0 231 return $this->request;
Chris@0 232 }
Chris@0 233
Chris@0 234 /**
Chris@0 235 * Clicks on a given link.
Chris@0 236 *
Chris@0 237 * @param Link $link A Link instance
Chris@0 238 *
Chris@0 239 * @return Crawler
Chris@0 240 */
Chris@0 241 public function click(Link $link)
Chris@0 242 {
Chris@0 243 if ($link instanceof Form) {
Chris@0 244 return $this->submit($link);
Chris@0 245 }
Chris@0 246
Chris@0 247 return $this->request($link->getMethod(), $link->getUri());
Chris@0 248 }
Chris@0 249
Chris@0 250 /**
Chris@0 251 * Submits a form.
Chris@0 252 *
Chris@0 253 * @param Form $form A Form instance
Chris@0 254 * @param array $values An array of form field values
Chris@0 255 *
Chris@0 256 * @return Crawler
Chris@0 257 */
Chris@0 258 public function submit(Form $form, array $values = array())
Chris@0 259 {
Chris@0 260 $form->setValues($values);
Chris@0 261
Chris@0 262 return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
Chris@0 263 }
Chris@0 264
Chris@0 265 /**
Chris@0 266 * Calls a URI.
Chris@0 267 *
Chris@0 268 * @param string $method The request method
Chris@0 269 * @param string $uri The URI to fetch
Chris@0 270 * @param array $parameters The Request parameters
Chris@0 271 * @param array $files The files
Chris@0 272 * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does)
Chris@0 273 * @param string $content The raw body data
Chris@0 274 * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
Chris@0 275 *
Chris@0 276 * @return Crawler
Chris@0 277 */
Chris@0 278 public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
Chris@0 279 {
Chris@0 280 if ($this->isMainRequest) {
Chris@0 281 $this->redirectCount = 0;
Chris@0 282 } else {
Chris@0 283 ++$this->redirectCount;
Chris@0 284 }
Chris@0 285
Chris@0 286 $uri = $this->getAbsoluteUri($uri);
Chris@0 287
Chris@0 288 $server = array_merge($this->server, $server);
Chris@0 289
Chris@0 290 if (isset($server['HTTPS'])) {
Chris@0 291 $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri);
Chris@0 292 }
Chris@0 293
Chris@0 294 if (!$this->history->isEmpty()) {
Chris@0 295 $server['HTTP_REFERER'] = $this->history->current()->getUri();
Chris@0 296 }
Chris@0 297
Chris@0 298 if (empty($server['HTTP_HOST'])) {
Chris@0 299 $server['HTTP_HOST'] = $this->extractHost($uri);
Chris@0 300 }
Chris@0 301
Chris@0 302 $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
Chris@0 303
Chris@0 304 $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
Chris@0 305
Chris@0 306 $this->request = $this->filterRequest($this->internalRequest);
Chris@0 307
Chris@0 308 if (true === $changeHistory) {
Chris@0 309 $this->history->add($this->internalRequest);
Chris@0 310 }
Chris@0 311
Chris@0 312 if ($this->insulated) {
Chris@0 313 $this->response = $this->doRequestInProcess($this->request);
Chris@0 314 } else {
Chris@0 315 $this->response = $this->doRequest($this->request);
Chris@0 316 }
Chris@0 317
Chris@0 318 $this->internalResponse = $this->filterResponse($this->response);
Chris@0 319
Chris@0 320 $this->cookieJar->updateFromResponse($this->internalResponse, $uri);
Chris@0 321
Chris@0 322 $status = $this->internalResponse->getStatus();
Chris@0 323
Chris@0 324 if ($status >= 300 && $status < 400) {
Chris@0 325 $this->redirect = $this->internalResponse->getHeader('Location');
Chris@0 326 } else {
Chris@0 327 $this->redirect = null;
Chris@0 328 }
Chris@0 329
Chris@0 330 if ($this->followRedirects && $this->redirect) {
Chris@0 331 return $this->crawler = $this->followRedirect();
Chris@0 332 }
Chris@0 333
Chris@0 334 return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type'));
Chris@0 335 }
Chris@0 336
Chris@0 337 /**
Chris@0 338 * Makes a request in another process.
Chris@0 339 *
Chris@0 340 * @param object $request An origin request instance
Chris@0 341 *
Chris@0 342 * @return object An origin response instance
Chris@0 343 *
Chris@0 344 * @throws \RuntimeException When processing returns exit code
Chris@0 345 */
Chris@0 346 protected function doRequestInProcess($request)
Chris@0 347 {
Chris@0 348 $process = new PhpProcess($this->getScript($request), null, null);
Chris@0 349 $process->run();
Chris@0 350
Chris@0 351 if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
Chris@0 352 throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput()));
Chris@0 353 }
Chris@0 354
Chris@0 355 return unserialize($process->getOutput());
Chris@0 356 }
Chris@0 357
Chris@0 358 /**
Chris@0 359 * Makes a request.
Chris@0 360 *
Chris@0 361 * @param object $request An origin request instance
Chris@0 362 *
Chris@0 363 * @return object An origin response instance
Chris@0 364 */
Chris@0 365 abstract protected function doRequest($request);
Chris@0 366
Chris@0 367 /**
Chris@0 368 * Returns the script to execute when the request must be insulated.
Chris@0 369 *
Chris@0 370 * @param object $request An origin request instance
Chris@0 371 *
Chris@0 372 * @throws \LogicException When this abstract class is not implemented
Chris@0 373 */
Chris@0 374 protected function getScript($request)
Chris@0 375 {
Chris@0 376 throw new \LogicException('To insulate requests, you need to override the getScript() method.');
Chris@0 377 }
Chris@0 378
Chris@0 379 /**
Chris@0 380 * Filters the BrowserKit request to the origin one.
Chris@0 381 *
Chris@0 382 * @param Request $request The BrowserKit Request to filter
Chris@0 383 *
Chris@0 384 * @return object An origin request instance
Chris@0 385 */
Chris@0 386 protected function filterRequest(Request $request)
Chris@0 387 {
Chris@0 388 return $request;
Chris@0 389 }
Chris@0 390
Chris@0 391 /**
Chris@0 392 * Filters the origin response to the BrowserKit one.
Chris@0 393 *
Chris@0 394 * @param object $response The origin response to filter
Chris@0 395 *
Chris@0 396 * @return Response An BrowserKit Response instance
Chris@0 397 */
Chris@0 398 protected function filterResponse($response)
Chris@0 399 {
Chris@0 400 return $response;
Chris@0 401 }
Chris@0 402
Chris@0 403 /**
Chris@0 404 * Creates a crawler.
Chris@0 405 *
Chris@0 406 * This method returns null if the DomCrawler component is not available.
Chris@0 407 *
Chris@0 408 * @param string $uri A URI
Chris@0 409 * @param string $content Content for the crawler to use
Chris@0 410 * @param string $type Content type
Chris@0 411 *
Chris@0 412 * @return Crawler|null
Chris@0 413 */
Chris@0 414 protected function createCrawlerFromContent($uri, $content, $type)
Chris@0 415 {
Chris@0 416 if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
Chris@0 417 return;
Chris@0 418 }
Chris@0 419
Chris@0 420 $crawler = new Crawler(null, $uri);
Chris@0 421 $crawler->addContent($content, $type);
Chris@0 422
Chris@0 423 return $crawler;
Chris@0 424 }
Chris@0 425
Chris@0 426 /**
Chris@0 427 * Goes back in the browser history.
Chris@0 428 *
Chris@0 429 * @return Crawler
Chris@0 430 */
Chris@0 431 public function back()
Chris@0 432 {
Chris@0 433 return $this->requestFromRequest($this->history->back(), false);
Chris@0 434 }
Chris@0 435
Chris@0 436 /**
Chris@0 437 * Goes forward in the browser history.
Chris@0 438 *
Chris@0 439 * @return Crawler
Chris@0 440 */
Chris@0 441 public function forward()
Chris@0 442 {
Chris@0 443 return $this->requestFromRequest($this->history->forward(), false);
Chris@0 444 }
Chris@0 445
Chris@0 446 /**
Chris@0 447 * Reloads the current browser.
Chris@0 448 *
Chris@0 449 * @return Crawler
Chris@0 450 */
Chris@0 451 public function reload()
Chris@0 452 {
Chris@0 453 return $this->requestFromRequest($this->history->current(), false);
Chris@0 454 }
Chris@0 455
Chris@0 456 /**
Chris@0 457 * Follow redirects?
Chris@0 458 *
Chris@0 459 * @return Crawler
Chris@0 460 *
Chris@0 461 * @throws \LogicException If request was not a redirect
Chris@0 462 */
Chris@0 463 public function followRedirect()
Chris@0 464 {
Chris@0 465 if (empty($this->redirect)) {
Chris@0 466 throw new \LogicException('The request was not redirected.');
Chris@0 467 }
Chris@0 468
Chris@0 469 if (-1 !== $this->maxRedirects) {
Chris@0 470 if ($this->redirectCount > $this->maxRedirects) {
Chris@0 471 throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects));
Chris@0 472 }
Chris@0 473 }
Chris@0 474
Chris@0 475 $request = $this->internalRequest;
Chris@0 476
Chris@0 477 if (in_array($this->internalResponse->getStatus(), array(302, 303))) {
Chris@0 478 $method = 'GET';
Chris@0 479 $files = array();
Chris@0 480 $content = null;
Chris@0 481 } else {
Chris@0 482 $method = $request->getMethod();
Chris@0 483 $files = $request->getFiles();
Chris@0 484 $content = $request->getContent();
Chris@0 485 }
Chris@0 486
Chris@0 487 if ('GET' === strtoupper($method)) {
Chris@0 488 // Don't forward parameters for GET request as it should reach the redirection URI
Chris@0 489 $parameters = array();
Chris@0 490 } else {
Chris@0 491 $parameters = $request->getParameters();
Chris@0 492 }
Chris@0 493
Chris@0 494 $server = $request->getServer();
Chris@0 495 $server = $this->updateServerFromUri($server, $this->redirect);
Chris@0 496
Chris@0 497 $this->isMainRequest = false;
Chris@0 498
Chris@0 499 $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content);
Chris@0 500
Chris@0 501 $this->isMainRequest = true;
Chris@0 502
Chris@0 503 return $response;
Chris@0 504 }
Chris@0 505
Chris@0 506 /**
Chris@0 507 * Restarts the client.
Chris@0 508 *
Chris@0 509 * It flushes history and all cookies.
Chris@0 510 */
Chris@0 511 public function restart()
Chris@0 512 {
Chris@0 513 $this->cookieJar->clear();
Chris@0 514 $this->history->clear();
Chris@0 515 }
Chris@0 516
Chris@0 517 /**
Chris@0 518 * Takes a URI and converts it to absolute if it is not already absolute.
Chris@0 519 *
Chris@0 520 * @param string $uri A URI
Chris@0 521 *
Chris@0 522 * @return string An absolute URI
Chris@0 523 */
Chris@0 524 protected function getAbsoluteUri($uri)
Chris@0 525 {
Chris@0 526 // already absolute?
Chris@0 527 if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) {
Chris@0 528 return $uri;
Chris@0 529 }
Chris@0 530
Chris@0 531 if (!$this->history->isEmpty()) {
Chris@0 532 $currentUri = $this->history->current()->getUri();
Chris@0 533 } else {
Chris@0 534 $currentUri = sprintf('http%s://%s/',
Chris@0 535 isset($this->server['HTTPS']) ? 's' : '',
Chris@0 536 isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
Chris@0 537 );
Chris@0 538 }
Chris@0 539
Chris@0 540 // protocol relative URL
Chris@0 541 if (0 === strpos($uri, '//')) {
Chris@0 542 return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri;
Chris@0 543 }
Chris@0 544
Chris@0 545 // anchor or query string parameters?
Chris@0 546 if (!$uri || '#' == $uri[0] || '?' == $uri[0]) {
Chris@0 547 return preg_replace('/[#?].*?$/', '', $currentUri).$uri;
Chris@0 548 }
Chris@0 549
Chris@0 550 if ('/' !== $uri[0]) {
Chris@0 551 $path = parse_url($currentUri, PHP_URL_PATH);
Chris@0 552
Chris@0 553 if ('/' !== substr($path, -1)) {
Chris@0 554 $path = substr($path, 0, strrpos($path, '/') + 1);
Chris@0 555 }
Chris@0 556
Chris@0 557 $uri = $path.$uri;
Chris@0 558 }
Chris@0 559
Chris@0 560 return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
Chris@0 561 }
Chris@0 562
Chris@0 563 /**
Chris@0 564 * Makes a request from a Request object directly.
Chris@0 565 *
Chris@0 566 * @param Request $request A Request instance
Chris@0 567 * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
Chris@0 568 *
Chris@0 569 * @return Crawler
Chris@0 570 */
Chris@0 571 protected function requestFromRequest(Request $request, $changeHistory = true)
Chris@0 572 {
Chris@0 573 return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
Chris@0 574 }
Chris@0 575
Chris@0 576 private function updateServerFromUri($server, $uri)
Chris@0 577 {
Chris@0 578 $server['HTTP_HOST'] = $this->extractHost($uri);
Chris@0 579 $scheme = parse_url($uri, PHP_URL_SCHEME);
Chris@0 580 $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme;
Chris@0 581 unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']);
Chris@0 582
Chris@0 583 return $server;
Chris@0 584 }
Chris@0 585
Chris@0 586 private function extractHost($uri)
Chris@0 587 {
Chris@0 588 $host = parse_url($uri, PHP_URL_HOST);
Chris@0 589
Chris@0 590 if ($port = parse_url($uri, PHP_URL_PORT)) {
Chris@0 591 return $host.':'.$port;
Chris@0 592 }
Chris@0 593
Chris@0 594 return $host;
Chris@0 595 }
Chris@0 596 }