| 
<?php
 /**
 * Parses a URI, provides details and a full correctly composed URI, processes a relative URI redirect
 *
 * @package gCurl
 * @author Grigori Kochanov
 * @version 1
 */
 class gURI{
 /**
 * URI schema part
 *
 * @var string
 */
 public $scheme;
 /**
 * Host name
 *
 * @var string
 */
 public $host;
 /**
 * User (for basic HTTP authentication)
 *
 * @var string
 */
 public $user;
 /**
 * Password (for basic HTTP authentication)
 *
 * @var string
 */
 public $pass;
 /**
 * Path part
 *
 * @var string
 */
 public $path;
 /**
 * Directory part of the path (not part of RFC)
 *
 * @var string
 */
 public $dir;
 /**
 * Query string
 *
 * @var string
 */
 public $query;
 /**
 * Port to connect to (if not 80)
 *
 * @var string
 */
 public $port;
 /**
 * Port part including preceeding semicolon
 *
 * @var string
 */
 public $port_string;
 /**
 * The anchor (after #)
 *
 * @var string
 */
 public $fragment;
 /**
 * Full correct URI in the RFC-compliant format
 *
 * @var string
 */
 public $full;
 
 /**
 * Flag defines whether to process "/../", "/./", "//" and convert backslash to slashes
 *
 * @var bool
 */
 public $normalize_path = false;
 
 /**
 * Symbol used to mark a start of a query
 *
 */
 public $QUERY_DELIMITER = '?';
 
 /**
 * Class constructor
 *
 * @param string $new_uri
 */
 function __construct($new_uri=null){
 $new_uri and $this->process($new_uri);
 }
 
 /**
 * Parse the URI, return false on error
 *
 * @param string $new_uri
 * @return bool
 */
 function process($new_uri){
 //init variables, results of parse_url() may redefine them
 $this->scheme =$this->host=$this->user =$this->pass =$this->path =$this->dir=$this->query=$this->fragment=$this->full='';
 $this->port = 80;
 $this->port_string = ':80';
 if (strpos($new_uri, '://') === false){
 $new_uri = 'http://'.$new_uri;
 }
 
 //parse current url - get parts
 $uri_array = @parse_url($new_uri);
 if (!$uri_array){
 return false;
 }
 
 //set varables with parts of URI
 $uri_array['scheme'] = empty($uri_array['scheme'])?'http://': $uri_array['scheme'].'://';
 //user:password@
 if (!empty($uri_array['user'])){
 if (!empty($uri_array['pass']))
 $uri_array['pass'] = ':'.$uri_array['pass'].'@';
 else {
 $uri_array['user'] .= '@';
 $uri_array['pass'] = '';
 }
 }else {
 $uri_array['user'] = $uri_array['pass'] = '';
 }
 
 if (!empty($uri_array['port'])){
 $uri_array['port_string'] = ':'.$uri_array['port'];
 }else {
 $uri_array['port'] = 80;
 $uri_array['port_string'] = '';
 }
 
 if (empty($uri_array['path']) || !trim($uri_array['path'])){
 $uri_array['path'] = '/';
 }
 
 $uri_array['dir']=$this->dirname($uri_array['path']);
 $uri_array['query'] =empty($uri_array['query'])? '':'?'.$uri_array['query'];
 $uri_array['fragment'] = empty($uri_array['fragment'])?'': '#'.$uri_array['fragment'];
 
 //assign class fields
 foreach($uri_array as $key=>$value){
 $this->$key = $value;
 }
 $this->get_full_uri();
 return true;
 }
 
 /**
 * Processes a new URI using details of a previous one
 *
 * @param string $new_url
 * @return bool
 */
 function parse_http_redirect ($new_url){
 if (!$new_url || !is_string($new_url)){
 return false;
 }
 
 //detect if URL is absolute
 if ($method_pos = strpos($new_url, '://')){
 $method = substr($new_url, 0, $method_pos);
 if (!strcasecmp($method, 'http') || !strcasecmp($method,'https')){
 // absolute URL passed
 return $this->process($new_url);
 }else{//invalid protocol
 return false;
 }
 }
 
 // URL is relative
 //do we have GET data in the URL?
 $param_pos = strpos($new_url, $this->QuERY_DELIMITER);
 if($param_pos !== false){
 $new_query = substr($new_url, $param_pos);
 $new_path = $param_pos ? substr($new_url, 0, $param_pos) : '' ;
 }else{
 $new_path = $new_url;
 $new_query = '';
 }
 
 //is URL relative to the previous URL path?
 if ($new_url[0] != '/'){
 //attach relative link to the current URI's directory
 $new_path = $this->dirname($this->path).'/'. $new_path;
 }
 
 if ($this->normalize_path){
 //replace back and repetitive slashes with a single forward one
 $new_path = preg_replace ('~((\\\\+)|/){2,}~', '/', $new_path);
 //parse links to the upper directories
 if (strpos($new_path, '/../') !== false){
 $path_array = explode ('/', $new_path);
 foreach ($path_array as $key=>$value){
 if ($value == '..'){
 if ($key > 2){
 unset ($path_array[$key-1]);
 }
 unset ($path_array[$key]);
 }
 }
 $new_path = implode ('/', $path_array);
 }
 //parse links to the 'current' directories
 $new_path = str_replace('/./', '/', $new_path);
 }
 
 $this->path = $new_path;
 $this->query = $new_query;
 $this->get_full_uri();
 
 return true;
 }
 
 /**
 * Returns the directory part of the path (path parameter may include query string)
 *
 * @param string $path
 * @return string
 */
 function dirname($path){
 if(!$path){
 return false;
 }
 $i=strpos($path,'?');
 $dir=$i?substr($path,0,$i):$path;
 
 $i=strrpos($dir,'/');
 $dir=$i?substr($dir,0,$i):'/';
 $dir[0]=='/' || $dir='/'.$dir;
 return $dir;
 }
 
 /**
 * (re)compile the full uri and return the string
 *
 * @return string
 */
 function get_full_uri(){
 $this->full = $this->scheme.$this->user.$this->pass.
 $this->host.$this->port_string.$this->path.$this->query;
 return $this->full;
 }
 
 /**
 *Checks if the requested host exists
 *
 *@return bool
 */
 function checkHost(){
 
 if (!$this->host){
 throw new URLException(120);
 }
 //host name may be given as IP address or domain name
 $regexp='/^\d{2,3}(\.\d{1,3}){3}$/';
 if(!checkdnsrr($this->host,'A') && !preg_match($regexp,$this->host)){
 throw new URLException(120);
 }
 
 }
 
 //end of the class
 }
 
 |