[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/joomla/environment/ -> uri.php (source)

   1  <?php
   2  /**
   3   * @version        $Id: uri.php 21079 2011-04-04 20:54:40Z dextercowley $
   4   * @package        Joomla.Framework
   5   * @subpackage    Environment
   6   * @copyright    Copyright (C) 2005 - 2010 Open Source Matters. All rights reserved.
   7   * @license        GNU/GPL, see LICENSE.php
   8   * Joomla! is free software. This version may have been modified pursuant
   9   * to the GNU General Public License, and as distributed it includes or
  10   * is derivative of works licensed under the GNU General Public License or
  11   * other free or open source software licenses.
  12   * See COPYRIGHT.php for copyright notices and details.
  13   */
  14  
  15  // Check to ensure this file is within the rest of the framework
  16  defined('JPATH_BASE') or die();
  17  
  18  /**
  19   * JURI Class
  20   *
  21   * This class serves two purposes.  First to parse a URI and provide a common interface
  22   * for the Joomla Framework to access and manipulate a URI.  Second to attain the URI of
  23   * the current executing script from the server regardless of server.
  24   *
  25   * @package        Joomla.Framework
  26   * @subpackage    Environment
  27   * @since        1.5
  28   */
  29  class JURI extends JObject
  30  {
  31      /**
  32       * Original URI
  33       *
  34       * @var        string
  35       */
  36      var $_uri = null;
  37  
  38      /**
  39       * Protocol
  40       *
  41       * @var        string
  42       */
  43      var $_scheme = null;
  44  
  45      /**
  46       * Host
  47       *
  48       * @var        string
  49       */
  50      var $_host = null;
  51  
  52      /**
  53       * Port
  54       *
  55       * @var        integer
  56       */
  57      var $_port = null;
  58  
  59      /**
  60       * Username
  61       *
  62       * @var        string
  63       */
  64      var $_user = null;
  65  
  66      /**
  67       * Password
  68       *
  69       * @var        string
  70       */
  71      var $_pass = null;
  72  
  73      /**
  74       * Path
  75       *
  76       * @var        string
  77       */
  78      var $_path = null;
  79  
  80      /**
  81       * Query
  82       *
  83       * @var        string
  84       */
  85      var $_query = null;
  86  
  87      /**
  88       * Anchor
  89       *
  90       * @var        string
  91       */
  92      var $_fragment = null;
  93  
  94      /**
  95       * Query variable hash
  96       *
  97       * @var        array
  98       */
  99      var $_vars = array ();
 100  
 101      /**
 102       * Constructor.
 103       * You can pass a URI string to the constructor to initialize a specific URI.
 104       *
 105       * @param    string $uri The optional URI string
 106       */
 107  	function __construct($uri = null)
 108      {
 109          if ($uri !== null) {
 110              $this->parse($uri);
 111          }
 112      }
 113  
 114      /**
 115       * Returns a reference to a global JURI object, only creating it
 116       * if it doesn't already exist.
 117       *
 118       * This method must be invoked as:
 119       *         <pre>  $uri =& JURI::getInstance([$uri]);</pre>
 120       *
 121       * @static
 122       * @param    string $uri The URI to parse.  [optional: if null uses script URI]
 123       * @return    JURI  The URI object.
 124       * @since    1.5
 125       */
 126      function &getInstance($uri = 'SERVER')
 127      {
 128          static $instances = array();
 129  
 130          if (!isset ($instances[$uri]))
 131          {
 132              // Are we obtaining the URI from the server?
 133              if ($uri == 'SERVER')
 134              {
 135                  // Determine if the request was over SSL (HTTPS)
 136                  if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {
 137                      $https = 's://';
 138                  } else {
 139                      $https = '://';
 140                  }
 141  
 142                  /*
 143                   * Since we are assigning the URI from the server variables, we first need
 144                   * to determine if we are running on apache or IIS.  If PHP_SELF and REQUEST_URI
 145                   * are present, we will assume we are running on apache.
 146                   */
 147                  if (!empty ($_SERVER['PHP_SELF']) && !empty ($_SERVER['REQUEST_URI'])) {
 148  
 149                      /*
 150                       * To build the entire URI we need to prepend the protocol, and the http host
 151                       * to the URI string.
 152                       */
 153                      $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
 154  
 155                  /*
 156                   * Since we do not have REQUEST_URI to work with, we will assume we are
 157                   * running on IIS and will therefore need to work some magic with the SCRIPT_NAME and
 158                   * QUERY_STRING environment variables.
 159                   */
 160                  
 161                       if (strlen($_SERVER['QUERY_STRING']) && strpos($_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING']) === false) {
 162                          $theURI .= '?'.$_SERVER['QUERY_STRING'];
 163                      }
 164  
 165                  }
 166                   else
 167                   {
 168                      // IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable... thanks, MS
 169                      $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'];
 170          
 171                      // If the query string exists append it to the URI string
 172                      if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
 173                          $theURI .= '?' . $_SERVER['QUERY_STRING'];
 174                      }
 175                  }
 176  
 177                  // Now we need to clean what we got since we can't trust the server var
 178                  // Need to check that the URI is fully decoded in case of multiple-encoded attack vectors.
 179                  $halt    = 0;
 180                  while (true)
 181                  {
 182                      $last    = $theURI;
 183                      $theURI = urldecode($theURI);
 184  
 185                      // Check whether the last decode is equal to the first.
 186                      if ($theURI == $last) {
 187                          // Break out of the while if the URI is stable.
 188                          break;
 189                      }
 190                      else if (++$halt > 10) {
 191                          // Runaway check. URI has been seriously compromised.
 192                          jexit();
 193                      }
 194                  }
 195  
 196                  $theURI = str_replace('"', '&quot;',$theURI);
 197                  $theURI = str_replace('<', '&lt;',$theURI);
 198                  $theURI = str_replace('>', '&gt;',$theURI);
 199                  $theURI = preg_replace('/eval\((.*)\)/', '', $theURI);
 200                  $theURI = preg_replace('/[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']/', '""', $theURI);
 201              }
 202              else
 203              {
 204                  // We were given a URI
 205                  $theURI = $uri;
 206              }
 207  
 208              // Create the new JURI instance
 209              $instances[$uri] = new JURI($theURI);
 210          }
 211          return $instances[$uri];
 212      }
 213  
 214      /**
 215       * Returns the base URI for the request.
 216       *
 217       * @access    public
 218       * @static
 219       * @param    boolean $pathonly If false, prepend the scheme, host and port information. Default is false.
 220       * @return    string    The base URI string
 221       * @since    1.5
 222       */
 223  	function base($pathonly = false)
 224      {
 225          static $base;
 226  
 227          // Get the base request path
 228          if (!isset($base))
 229          {
 230              $config =& JFactory::getConfig();
 231              $live_site = $config->getValue('config.live_site');
 232              if(trim($live_site) != '') {
 233                  $uri =& JURI::getInstance($live_site);
 234                  $base['prefix'] = $uri->toString( array('scheme', 'host', 'port'));
 235                  $base['path'] = rtrim($uri->toString( array('path')), '/\\');
 236                  if(JPATH_BASE == JPATH_ADMINISTRATOR) {
 237                      $base['path'] .= '/administrator';
 238                  }
 239              } else {
 240                  $uri             =& JURI::getInstance();
 241                  $base['prefix'] = $uri->toString( array('scheme', 'host', 'port'));
 242  
 243                  if (strpos(php_sapi_name(), 'cgi') !== false && !empty($_SERVER['REQUEST_URI']) &&
 244                      (!ini_get('cgi.fix_pathinfo') || version_compare(PHP_VERSION, '5.2.4', '<'))) {
 245                      // CGI on PHP pre-5.2.4 with cgi.fix_pathinfo = 0.
 246  
 247                      // In pre-rev. 240885 of main_cgi.c, SCRIPT_NAME doesn't conform the PHP spec.,
 248                      // therefore we use PHP_SELF instead.
 249                      $base['path'] =  rtrim(dirname(str_replace(array('"', '<', '>', "'"), '', $_SERVER["PHP_SELF"])), '/\\');
 250                  } else {
 251                      // Since PHP 5.2.4 we can trust SCRIPT_NAME;  it conforms the spec.
 252                      $base['path'] =  rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
 253                  }
 254              }
 255          }
 256  
 257          return $pathonly === false ? $base['prefix'].$base['path'].'/' : $base['path'];
 258      }
 259  
 260      /**
 261       * Returns the root URI for the request.
 262       *
 263       * @access    public
 264       * @static
 265       * @param    boolean $pathonly If false, prepend the scheme, host and port information. Default is false.
 266       * @return    string    The root URI string
 267       * @since    1.5
 268       */
 269  	function root($pathonly = false, $path = null)
 270      {
 271          static $root;
 272  
 273          // Get the scheme
 274          if(!isset($root))
 275          {
 276              $uri            =& JURI::getInstance(JURI::base());
 277              $root['prefix'] = $uri->toString( array('scheme', 'host', 'port') );
 278              $root['path']   = rtrim($uri->toString( array('path') ), '/\\');
 279          }
 280  
 281          // Get the scheme
 282          if(isset($path)) {
 283              $root['path']    = $path;
 284          }
 285  
 286          return $pathonly === false ? $root['prefix'].$root['path'].'/' : $root['path'];
 287      }
 288  
 289      /**
 290       * Returns the URL for the request, minus the query
 291       *
 292       * @access    public
 293       * @return    string
 294       * @since    1.5
 295       */
 296  	function current()
 297      {
 298          static $current;
 299  
 300          // Get the current URL
 301          if (!isset($current))
 302          {
 303              $uri     = & JURI::getInstance();
 304              $current = $uri->toString( array('scheme', 'host', 'port', 'path'));
 305          }
 306  
 307          return $current;
 308      }
 309  
 310      /**
 311       * Parse a given URI and populate the class fields
 312       *
 313       * @access    public
 314       * @param    string $uri The URI string to parse
 315       * @return    boolean True on success
 316       * @since    1.5
 317       */
 318  	function parse($uri)
 319      {
 320          //Initialize variables
 321          $retval = false;
 322  
 323          // Set the original URI to fall back on
 324          $this->_uri = $uri;
 325  
 326          /*
 327           * Parse the URI and populate the object fields.  If URI is parsed properly,
 328           * set method return value to true.
 329           */
 330          if ($_parts = $this->_parseURL($uri)) {
 331              $retval = true;
 332          }
 333  
 334          //We need to replace &amp; with & for parse_str to work right...
 335          if(isset ($_parts['query']) && strpos($_parts['query'], '&amp;')) {
 336              $_parts['query'] = str_replace('&amp;', '&', $_parts['query']);
 337          }
 338  
 339          $this->_scheme = isset ($_parts['scheme']) ? $_parts['scheme'] : null;
 340          $this->_user = isset ($_parts['user']) ? $_parts['user'] : null;
 341          $this->_pass = isset ($_parts['pass']) ? $_parts['pass'] : null;
 342          $this->_host = isset ($_parts['host']) ? $_parts['host'] : null;
 343          $this->_port = isset ($_parts['port']) ? $_parts['port'] : null;
 344          $this->_path = isset ($_parts['path']) ? $_parts['path'] : null;
 345          $this->_query = isset ($_parts['query'])? $_parts['query'] : null;
 346          $this->_fragment = isset ($_parts['fragment']) ? $_parts['fragment'] : null;
 347  
 348          //parse the query
 349  
 350          if(isset ($_parts['query'])) parse_str($_parts['query'], $this->_vars);
 351          return $retval;
 352      }
 353  
 354      /**
 355       * Returns full uri string
 356       *
 357       * @access    public
 358       * @param    array $parts An array specifying the parts to render
 359       * @return    string The rendered URI string
 360       * @since    1.5
 361       */
 362  	function toString($parts = array('scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment'))
 363      {
 364          $query = $this->getQuery(); //make sure the query is created
 365  
 366          $uri = '';
 367          $uri .= in_array('scheme', $parts)  ? (!empty($this->_scheme) ? $this->_scheme.'://' : '') : '';
 368          $uri .= in_array('user', $parts)    ? $this->_user : '';
 369          $uri .= in_array('pass', $parts)    ? (!empty ($this->_pass) ? ':' : '') .$this->_pass. (!empty ($this->_user) ? '@' : '') : '';
 370          $uri .= in_array('host', $parts)    ? $this->_host : '';
 371          $uri .= in_array('port', $parts)    ? (!empty ($this->_port) ? ':' : '').$this->_port : '';
 372          $uri .= in_array('path', $parts)    ? $this->_path : '';
 373          $uri .= in_array('query', $parts)    ? (!empty ($query) ? '?'.$query : '') : '';
 374          $uri .= in_array('fragment', $parts)? (!empty ($this->_fragment) ? '#'.$this->_fragment : '') : '';
 375  
 376          return $uri;
 377      }
 378  
 379      /**
 380       * Adds a query variable and value, replacing the value if it
 381       * already exists and returning the old value.
 382       *
 383       * @access    public
 384       * @param    string $name Name of the query variable to set
 385       * @param    string $value Value of the query variable
 386       * @return    string Previous value for the query variable
 387       * @since    1.5
 388       */
 389  	function setVar($name, $value)
 390      {
 391          $tmp = @$this->_vars[$name];
 392          $this->_vars[$name] = $value;
 393  
 394          //empty the query
 395          $this->_query = null;
 396  
 397          return $tmp;
 398      }
 399  
 400      /**
 401       * Returns a query variable by name
 402       *
 403       * @access    public
 404       * @param    string $name Name of the query variable to get
 405       * @return    array Query variables
 406       * @since    1.5
 407       */
 408  	function getVar($name = null, $default=null)
 409      {
 410          if(isset($this->_vars[$name])) {
 411              return $this->_vars[$name];
 412          }
 413          return $default;
 414      }
 415  
 416      /**
 417       * Removes an item from the query string variables if it exists
 418       *
 419       * @access    public
 420       * @param    string $name Name of variable to remove
 421       * @since    1.5
 422       */
 423  	function delVar($name)
 424      {
 425          if (in_array($name, array_keys($this->_vars)))
 426          {
 427              unset ($this->_vars[$name]);
 428  
 429              //empty the query
 430              $this->_query = null;
 431          }
 432      }
 433  
 434      /**
 435       * Sets the query to a supplied string in format:
 436       *         foo=bar&x=y
 437       *
 438       * @access    public
 439       * @param    mixed (array|string) $query The query string
 440       * @since    1.5
 441       */
 442  	function setQuery($query)
 443      {
 444          if(!is_array($query)) {
 445              if(strpos($query, '&amp;') !== false)
 446              {
 447                 $query = str_replace('&amp;','&',$query);
 448              }
 449              parse_str($query, $this->_vars);
 450          }
 451  
 452          if(is_array($query)) {
 453              $this->_vars = $query;
 454          }
 455  
 456          //empty the query
 457          $this->_query = null;
 458      }
 459  
 460      /**
 461       * Returns flat query string
 462       *
 463       * @access    public
 464       * @return    string Query string
 465       * @since    1.5
 466       */
 467  	function getQuery($toArray = false)
 468      {
 469          if($toArray) {
 470              return $this->_vars;
 471          }
 472  
 473          //If the query is empty build it first
 474          if(is_null($this->_query)) {
 475              $this->_query = $this->buildQuery($this->_vars);
 476          }
 477  
 478          return $this->_query;
 479      }
 480  
 481      /**
 482       * Build a query from a array (reverse of the PHP parse_str())
 483       *
 484       * @access    public
 485       * @return    string The resulting query string
 486       * @since    1.5
 487       * @see    parse_str()
 488       */
 489  	function buildQuery ($params, $akey = null)
 490      {
 491          if ( !is_array($params) || count($params) == 0 ) {
 492              return false;
 493          }
 494  
 495          $out = array();
 496  
 497          //reset in case we are looping
 498          if( !isset($akey) && !count($out) )  {
 499              unset($out);
 500              $out = array();
 501          }
 502  
 503          foreach ( $params as $key => $val )
 504          {
 505              if ( is_array($val) ) {
 506                  $out[] = JURI::buildQuery($val,$key);
 507                  continue;
 508              }
 509  
 510              $thekey = ( !$akey ) ? $key : $akey.'['.$key.']';
 511              $out[] = $thekey."=".urlencode($val);
 512          }
 513  
 514          return implode("&",$out);
 515      }
 516  
 517      /**
 518       * Get URI scheme (protocol)
 519       *         ie. http, https, ftp, etc...
 520       *
 521       * @access    public
 522       * @return    string The URI scheme
 523       * @since    1.5
 524       */
 525  	function getScheme() {
 526          return $this->_scheme;
 527      }
 528  
 529      /**
 530       * Set URI scheme (protocol)
 531       *         ie. http, https, ftp, etc...
 532       *
 533       * @access    public
 534       * @param    string $scheme The URI scheme
 535       * @since    1.5
 536       */
 537  	function setScheme($scheme) {
 538          $this->_scheme = $scheme;
 539      }
 540  
 541      /**
 542       * Get URI username
 543       *         returns the username, or null if no username was specified
 544       *
 545       * @access    public
 546       * @return    string The URI username
 547       * @since    1.5
 548       */
 549  	function getUser() {
 550          return $this->_user;
 551      }
 552  
 553      /**
 554       * Set URI username
 555       *
 556       * @access    public
 557       * @param    string $user The URI username
 558       * @since    1.5
 559       */
 560  	function setUser($user) {
 561          $this->_user = $user;
 562      }
 563  
 564      /**
 565       * Get URI password
 566       *         returns the password, or null if no password was specified
 567       *
 568       * @access    public
 569       * @return    string The URI password
 570       * @since    1.5
 571       */
 572  	function getPass() {
 573          return $this->_pass;
 574      }
 575  
 576      /**
 577       * Set URI password
 578       *
 579       * @access    public
 580       * @param    string $pass The URI password
 581       * @since    1.5
 582       */
 583  	function setPass($pass) {
 584          $this->_pass = $pass;
 585      }
 586  
 587      /**
 588       * Get URI host
 589       *         returns the hostname/ip, or null if no hostname/ip was specified
 590       *
 591       * @access    public
 592       * @return    string The URI host
 593       * @since    1.5
 594       */
 595  	function getHost() {
 596          return $this->_host;
 597      }
 598  
 599      /**
 600       * Set URI host
 601       *
 602       * @access    public
 603       * @param    string $host The URI host
 604       * @since    1.5
 605       */
 606  	function setHost($host) {
 607          $this->_host = $host;
 608      }
 609  
 610      /**
 611       * Get URI port
 612       *         returns the port number, or null if no port was specified
 613       *
 614       * @access    public
 615       * @return    int The URI port number
 616       */
 617  	function getPort() {
 618          return (isset ($this->_port)) ? $this->_port : null;
 619      }
 620  
 621      /**
 622       * Set URI port
 623       *
 624       * @access    public
 625       * @param    int $port The URI port number
 626       * @since    1.5
 627       */
 628  	function setPort($port) {
 629          $this->_port = $port;
 630      }
 631  
 632      /**
 633       * Gets the URI path string
 634       *
 635       * @access    public
 636       * @return    string The URI path string
 637       * @since    1.5
 638       */
 639  	function getPath() {
 640          return $this->_path;
 641      }
 642  
 643      /**
 644       * Set the URI path string
 645       *
 646       * @access    public
 647       * @param    string $path The URI path string
 648       * @since    1.5
 649       */
 650  	function setPath($path) {
 651          $this->_path = $this->_cleanPath($path);
 652      }
 653  
 654      /**
 655       * Get the URI archor string
 656       *         everything after the "#"
 657       *
 658       * @access    public
 659       * @return    string The URI anchor string
 660       * @since    1.5
 661       */
 662  	function getFragment() {
 663          return $this->_fragment;
 664      }
 665  
 666      /**
 667       * Set the URI anchor string
 668       *         everything after the "#"
 669       *
 670       * @access    public
 671       * @param    string $anchor The URI anchor string
 672       * @since    1.5
 673       */
 674  	function setFragment($anchor) {
 675          $this->_fragment = $anchor;
 676      }
 677  
 678      /**
 679       * Checks whether the current URI is using HTTPS
 680       *
 681       * @access    public
 682       * @return    boolean True if using SSL via HTTPS
 683       * @since    1.5
 684       */
 685  	function isSSL() {
 686          return $this->getScheme() == 'https' ? true : false;
 687      }
 688  
 689      /** 
 690       * Checks if the supplied URL is internal
 691       *
 692       * @access    public
 693       * @param     string $url The URL to check
 694       * @return    boolean True if Internal
 695       * @since    1.5
 696       */
 697  	function isInternal($url) {
 698          $uri =& JURI::getInstance($url);
 699          $base = $uri->toString(array('scheme', 'host', 'port', 'path'));
 700          $host = $uri->toString(array('scheme', 'host', 'port'));
 701          if(stripos($base, JURI::base()) !== 0 && !empty($host)) {
 702              return false;
 703          }
 704          return true;
 705      }
 706  
 707      /**
 708       * Resolves //, ../ and ./ from a path and returns
 709       * the result. Eg:
 710       *
 711       * /foo/bar/../boo.php    => /foo/boo.php
 712       * /foo/bar/../../boo.php => /boo.php
 713       * /foo/bar/.././/boo.php => /foo/boo.php
 714       *
 715       * @access    private
 716       * @param    string $uri The URI path to clean
 717       * @return    string Cleaned and resolved URI path
 718       * @since    1.5
 719       */
 720  	function _cleanPath($path)
 721      {
 722          $path = explode('/', preg_replace('#(/+)#', '/', $path));
 723  
 724          for ($i = 0; $i < count($path); $i ++) {
 725              if ($path[$i] == '.') {
 726                  unset ($path[$i]);
 727                  $path = array_values($path);
 728                  $i --;
 729  
 730              }
 731              elseif ($path[$i] == '..' AND ($i > 1 OR ($i == 1 AND $path[0] != ''))) {
 732                  unset ($path[$i]);
 733                  unset ($path[$i -1]);
 734                  $path = array_values($path);
 735                  $i -= 2;
 736  
 737              }
 738              elseif ($path[$i] == '..' AND $i == 1 AND $path[0] == '') {
 739                  unset ($path[$i]);
 740                  $path = array_values($path);
 741                  $i --;
 742  
 743              } else {
 744                  continue;
 745              }
 746          }
 747  
 748          return implode('/', $path);
 749      }
 750  
 751      /**
 752       * Backwards compatibility function for parse_url function
 753       *
 754       * This function solves different bugs in PHP versions lower then
 755       * 4.4, will be deprecated in future versions.
 756       *
 757       * @access    private
 758       * @return    array Associative array containing the URL parts
 759       * @since    1.5
 760       * @see parse_url()
 761       */
 762  	function _parseURL($uri)
 763      {
 764          $parts = array();
 765          if (version_compare( phpversion(), '4.4' ) < 0)
 766          {
 767              $regex = "<^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?>";
 768              $matches = array();
 769              preg_match($regex, $uri, $matches, PREG_OFFSET_CAPTURE);
 770  
 771              $authority = @$matches[4][0];
 772              if (strpos($authority, '@') !== false) {
 773                  $authority = explode('@', $authority);
 774                  @list($parts['user'], $parts['pass']) = explode(':', $authority[0]);
 775                  $authority = $authority[1];
 776              }
 777  
 778              if (strpos($authority, ':') !== false) {
 779                  $authority = explode(':', $authority);
 780                  $parts['host'] = $authority[0];
 781                  $parts['port'] = $authority[1];
 782              } else {
 783                  $parts['host'] = $authority;
 784              }
 785  
 786              $parts['scheme'] = @$matches[2][0];
 787              $parts['path'] = @$matches[5][0];
 788              $parts['query'] = @$matches[7][0];
 789              $parts['fragment'] = @$matches[9][0];
 790          }
 791          else
 792          {
 793              $parts = @parse_url($uri);
 794          }
 795          return $parts;
 796      }
 797  
 798  
 799  }


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1