<?php
class PathValidator
{
	public function __construct($path)
	{
		$parsed_path = $this->ParsePath($path);
		
		if($parsed_path != null)
		{
			$this->path = $parsed_path;
		}
		else
		{
			throw new Exception("Invalid path specified");
		}
	}
	
	public function ValidatePath($root)
	{
		$root_path = $this->ParsePath($root);
		
		if($root_path != null)
		{
			if($this->RelativeDepth($root) >= 0)
			{
				$root_stack = explode("/", $root_path);
				$path_stack = explode("/", $this->path);
				
				for($i = 0; $i < count($root_stack); $i++)
				{
					if($root_stack[$i] != $path_stack[$i])
					{
						return false;
					}
				}
				
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			throw new Exception("Specified root path is invalid.");
		}
	}
	
	public function RelativeDepth($root)
	{
		$root_length = substr_count($this->ParsePath($root), "/");
		$path_length = substr_count($this->path, "/");
		
		return $path_length - $root_length;
	}
	
	private function RemoveTrailingSlash($path)
	{
		if(substr($path, strlen($path) - 1) == "/")
		{
			return substr($path, 0, strlen($path) - 1);
		}
		else
		{
			return $path;
		}
	}
	
	private function ParsePath($path)
	{
		/* We use a custom function for this since we just want to resolve the path no matter what,
		 * and the realpath() function will return false if the path either doesn't exist or is not
		 * accessible. */
		 
		$path = $this->RemoveTrailingSlash($path);
		 
		if(substr($path, 0, 1) == "/")
		{
			/* Absolute path */
			return $path;
		}
		else
		{
			$path_elements = explode("/", $path);
			
			if(substr($path, 0, 1) == "~")
			{
				/* Home directory path */
				if(!empty($_SERVER['home']))
				{
					$homedir = $_SERVER['home'];
				}
				elseif(getenv("HOME") != null)
				{
					$homedir = getenv("HOME");
				}
				elseif(function_exists("posix_getuid") && function_exists("posix_getpwuid"))
				{
					$userinfo = posix_getpwuid(posix_getuid());
					$homedir = $userinfo['dir'];
				}
				else
				{
					throw new Exception("Could not find a way to get the home directory of the current user.");
				}
				
				$homedir = $this->RemoveTrailingSlash($homedir);
				
				$stack = explode("/", $homedir);
				array_shift($path_elements);
			}
			else
			{
				/* Relative path */
				$basepath = $this->RemoveTrailingSlash(getcwd());
				$stack = explode("/", $basepath);
			}
			
			foreach($path_elements as $element)
			{
				if($element == ".")
				{
					/* Ignore */
				}
				elseif($element == "..")
				{
					/* Go up one directory */
					if(count($stack) > 1)
					{
						array_pop($stack);
					}
					else
					{
						/* There are no elements left to pop, this is an invalid path. */
						return null;
					}
				}
				else
				{
					/* Append to path */
					$stack[] = $element;
				}
			}
			
			return implode("/", $stack);
		}
	}
}
