2

I found myself starting to answer a question that I have answered many times , so I thought why not making a code snippet and just reference this.

This is a short code snippet only to demonstrate how this thing work (with apache and rewrite engine on) , in real life PHP code shouldn't be under the public_html / www folder in any case (with maybe only one exception a index.php that its main task is to include something (or instantiate a class) from the src folder)

Lets suppose that you have an example folder under the domain example.com , you have some urls
eg1: http://example.com/example?var=value (this in this approach is equivalent to http://example.com/example/?var=value)
eg2: http://example.com/example/threadName/topicName

and you want to get the ?var=value or threadName/topicName or what ever after the http://example.com/example/ part. This is a very common senario that I have seen in many variations.

So lets do it:
Under the example directory we have an .htaccess file

DirectoryIndex index.php
RewriteEngine On

RewriteCond %{ENV:URI} ^$
RewriteRule ^(.*)$ - [ENV=URI:$1]
RewriteCond %{ENV:BASE} ^$
RewriteCond %{ENV:URI}::%{REQUEST_URI} ^(.*)::(.*?)\1$
RewriteRule ^ - [ENV=BASE:%2]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule .* %{ENV:BASE}index.php [QSA,L]

And an index.php file :

<?php
error_reporting( E_ALL );
ini_set( 'display_errors' ,'1' );
// set UTF-8 encoding
mb_internal_encoding( "UTF-8" );

// eg1: lets suppose that your domain is http://example.com 
// and that we (the .htaccess file and this index.php) is under the 
// example directory 

$protocol = StringUtils::beforeFirst( $_SERVER["SERVER_PROTOCOL"] ,"/" ) 
    . ( empty( $_SERVER["HTTPS"] ) ? '' : ( $_SERVER["HTTPS"] == "on" ) 
        ? "s" : "" );
// The protocol part , eg1 > http

$httpHost = isset( $_SERVER["HTTP_HOST"] ) ? trim( $_SERVER["HTTP_HOST"] 
    ,"/" ) : null;
// the host , eg1 > example.com 

$phpSelf = $_SERVER["PHP_SELF"];
// the normal current url of the index.php 
// , eg1 > http://example.com/example/index.php

$rootUrl = StringUtils::toLowerCase( $protocol ) . "://" . $httpHost .
     StringUtils::beforeLast( $phpSelf ,"/" );
// The rootUrl (where the htaccess is) , eg1 > http://example.com/example

$fullRequestUri = StringUtils::toLowerCase( $protocol ) . "://" . $httpHost
    . $_SERVER["REQUEST_URI"];
//  The fullRequestedUrl (including the ? if is any or any other after 
//  slashes. eg1 > http://example.com/example/example/?var=value 
//  or eg1> http://example.com/example/action?var=value
//  or anything 

$afterRootUrl = StringUtils::afterFirst( 
    StringUtils::toLowerCase( $protocol ) . "://" . $httpHost 
    . $fullRequestUri , $rootUrl . "/" );
// The after root url , eg1 > action?var=value

// Now you have the after root Url and you can do what ever you like 
// to it 
echo $afterRootUrl;

/**
 * StringUtils class contains static methods for common string
 * operations
 *
 * @version THIS IS A MINIFIED VERSION having only the methods I need
 *          for this code snippet
 */
class StringUtils
{

    /**
     * Returns how many characters the string is
     * 
     * @param <b>string $string</b>
     *          The string
     * @return number
     */
    public static function length($string)
    {
        return mb_strlen( $string );
    }

    /**
     * Counts the occurrences of a string inside another string and returns
     * the result
     * 
     * @param <b>string $string</b>
     *          The string to be searched
     * @param <b>string $search</b>
     *          The string to search for
     * @param <b>boolean $caseSensitive
     *          :optional</b> Defines if will be
     *          case sensitive. By default true.
     * @return number
     */
    public static function count($string ,$search ,$caseSensitive = true)
    {
        return $caseSensitive ? mb_substr_count( $string ,$search ) 
            : mb_substr_count( 
                self::toLowerCase( $string ) ,self::toLowerCase( $search ) );
    }

    /**
     * Return the index of <b>the first occurance</b> of a part of a
     * string to the string
     * 
     * @param <b>string $string</b>
     *          The string to be searched
     * @param <b>string $search
     *          string</b> The string to search for
     * @param <b>boolean $caseSensitive
     *          :optional</b> Defines if will
     *          be case sensitive. By default true.
     * @return number
     */
    public static function firstIndexOf($string ,$search 
        ,$caseSensitive = true)
    {
        return $caseSensitive ? mb_strpos( $string ,$search ) 
            : mb_stripos( $string , $search );
    }

    /**
     * Return the index of <b>the last occurance</b> of a part of a string
     * to the string
     * 
     * @param
     *          <b>$string string</b> The string to be searched
     * @param
     *          <b>$search string</b> The string to search for
     * @param
     *          <b>$caseSensitive boolean :optional</b> Defines if the search
     *          will be case sensitive. By default true.
     * @return number
     */
    public static function lastIndexOf($string ,$search 
        ,$caseSensitive = true)
    {
        return $caseSensitive ? mb_strrpos( $string ,$search ) : mb_strripos( 
            $string ,$search );
    }

    /**
     * Converts a string to lower case
     * 
     * @param <b>string $string</b>
     *          The sting to be converted
     * @return string
     */
    public static function toLowerCase($string)
    {
        return mb_strtolower( $string );
    }

    /**
     * Returns a part of the string from a character and for as many
     * characters as provided
     * 
     * @param
     *          <b>$string string</b> The string to retrieve the part from
     * @param
     *          <b>$start string </b> The index of the first character
     *          (0 for the first one)
     * @param
     *          <b>$length string</b> The length of the part the will be
     *          extracted from the string
     * @return string
     */
    public static function substring($string ,$start ,$length = null)
    {
        return ( $length === null ) ? ( mb_substr( $string ,$start ) ) 
            : ( $length ==
             0 ? "" : mb_substr( $string ,$start ,$length ) );
    }

    /**
     * Returns the part of a string <b>before the first</b> occurrence of
     * the string to search for.
     * If the string doesn't contain the needle returns the string itself
     * 
     * @param <b>string $string</b>
     *          The string to be searched
     * @param <b>string $search</b>
     *          The string to search for
     * @param <b>boolean $caseSensitive
     *          :optional</b> Defines if the search
     *          will be case sensitive. By default true.
     * @return string
     */
    public static function beforeFirst($string ,$search 
        ,$caseSensitive = true)
    {
        return self::count( $string ,$search ,$caseSensitive ) === 0 
            ? $string : self::substring($string 
                ,0 ,self::firstIndexOf( $string ,$search ,$caseSensitive ) );
    }

    /**
     * Returns the part of a string <b>before the last</b> occurrence of
     * the string to search for.
     * If the string doesn't contain the needle returns the string itself
     * 
     * @param <b>string $string</b>
     *          The string to be searched
     * @param <b>string $search</b>
     *          The string to search for
     * @param
     *          <b>$caseSensitive boolean :optional</b> Defines if the
     *          search will be case sensitive. By default true.
     * @return string
     */
    public static function beforeLast($string ,$search ,$caseSensitive = true)
    {
        return self::count( $string ,$search ,$caseSensitive ) === 0 ? 
            $string : self::substring(  $string 
                ,0 ,self::lastIndexOf( $string ,$search ,$caseSensitive ) );
    }

    /**
     * Returns the part of a string <b>after the first</b> occurrence of the
     * string to search for.
     * If the string doesn't contain the needle returns an empty string
     * 
     * @param <b>string $string</b>
     *          The string to be searched
     * @param <b>string $search</b>
     *          The string to search for
     * @param <b>boolean $caseSensitive
     *          :optional</b> Defines if the search
     *          will be case sensitive. By default true.
     * @return string
     */
    public static function afterFirst($string ,$search ,$caseSensitive = true)
    {
        return self::count( $string ,$search ,$caseSensitive ) === 0 ? "" 
            : self::substring( 
                $string ,
                self::firstIndexOf( $string ,$search ,$caseSensitive ) +
                    self::length( $search ) );
    }
}
?>

And we are done , now you have the after root url in the $afterRootUrl variable and you can do what ever you like with it.

Votes + Comments
always a joy to read your snippets. Thanks jkon
thanks for sharing! :)
<?php
error_reporting( E_ALL );
ini_set( 'display_errors' ,'1' );
// set UTF-8 encoding
mb_internal_encoding( "UTF-8" );

// eg1: lets suppose that your domain is http://example.com 
// and that we (the .htaccess file and this index.php) is under the 
// example directory 

$protocol = StringUtils::beforeFirst( $_SERVER["SERVER_PROTOCOL"] ,"/" ) 
	. ( empty( $_SERVER["HTTPS"] ) ? '' : ( $_SERVER["HTTPS"] == "on" ) 
		? "s" : "" );
// The protocol part , eg1 > http

$httpHost = isset( $_SERVER["HTTP_HOST"] ) ? trim( $_SERVER["HTTP_HOST"] 
	,"/" ) : null;
// the host , eg1 > example.com 

$phpSelf = $_SERVER["PHP_SELF"];
// the normal current url of the index.php 
// , eg1 > http://example.com/example/index.php

$rootUrl = StringUtils::toLowerCase( $protocol ) . "://" . $httpHost .
	 StringUtils::beforeLast( $phpSelf ,"/" );
// The rootUrl (where the htaccess is) , eg1 > http://example.com/example

$fullRequestUri = StringUtils::toLowerCase( $protocol ) . "://" . $httpHost
	. $_SERVER["REQUEST_URI"];
//  The fullRequestedUrl (including the ? if is any or any other after 
//  slashes. eg1 > http://example.com/example/example/?var=value 
//  or eg1> http://example.com/example/action?var=value
//  or anything 

$afterRootUrl = StringUtils::afterFirst( 
	StringUtils::toLowerCase( $protocol ) . "://" . $httpHost 
	. $fullRequestUri , $rootUrl . "/" );
// The after root url , eg1 > action?var=value

// Now you have the after root Url and you can do what ever you like 
// to it 
echo $afterRootUrl;




/**
 * StringUtils class contains static methods for common string
 * operations
 *
 * @version THIS IS A MINIFIED VERSION having only the methods I need
 *          for this code snippet
 */
class StringUtils
{

	/**
	 * Returns how many characters the string is
	 * 
	 * @param <b>string $string</b>
	 *        	The string
	 * @return number
	 */
	public static function length($string)
	{
		return mb_strlen( $string );
	}

	/**
	 * Counts the occurrences of a string inside another string and returns
	 * the result
	 * 
	 * @param <b>string $string</b>
	 *        	The string to be searched
	 * @param <b>string $search</b>
	 *        	The string to search for
	 * @param <b>boolean $caseSensitive
	 *        	:optional</b> Defines if will be
	 *        	case sensitive. By default true.
	 * @return number
	 */
	public static function count($string ,$search ,$caseSensitive = true)
	{
		return $caseSensitive ? mb_substr_count( $string ,$search ) 
			: mb_substr_count( 
				self::toLowerCase( $string ) ,self::toLowerCase( $search ) );
	}

	/**
	 * Return the index of <b>the first occurance</b> of a part of a
	 * string to the string
	 * 
	 * @param <b>string $string</b>
	 *        	The string to be searched
	 * @param <b>string $search
	 *        	string</b> The string to search for
	 * @param <b>boolean $caseSensitive
	 *        	:optional</b> Defines if will
	 *        	be case sensitive. By default true.
	 * @return number
	 */
	public static function firstIndexOf($string ,$search 
		,$caseSensitive = true)
	{
		return $caseSensitive ? mb_strpos( $string ,$search ) 
			: mb_stripos( $string ,	$search );
	}

	/**
	 * Return the index of <b>the last occurance</b> of a part of a string
	 * to the string
	 * 
	 * @param
	 *        	<b>$string string</b> The string to be searched
	 * @param
	 *        	<b>$search string</b> The string to search for
	 * @param
	 *        	<b>$caseSensitive boolean :optional</b> Defines if the search
	 *        	will be case sensitive. By default true.
	 * @return number
	 */
	public static function lastIndexOf($string ,$search 
		,$caseSensitive = true)
	{
		return $caseSensitive ? mb_strrpos( $string ,$search ) : mb_strripos( 
			$string ,$search );
	}

	/**
	 * Converts a string to lower case
	 * 
	 * @param <b>string $string</b>
	 *        	The sting to be converted
	 * @return string
	 */
	public static function toLowerCase($string)
	{
		return mb_strtolower( $string );
	}

	/**
	 * Returns a part of the string from a character and for as many
	 * characters as provided
	 * 
	 * @param
	 *        	<b>$string string</b> The string to retrieve the part from
	 * @param
	 *        	<b>$start string </b> The index of the first character
	 *        	(0 for the first one)
	 * @param
	 *        	<b>$length string</b> The length of the part the will be
	 *        	extracted from the string
	 * @return string
	 */
	public static function substring($string ,$start ,$length = null)
	{
		return ( $length === null ) ? ( mb_substr( $string ,$start ) ) 
			: ( $length ==
			 0 ? "" : mb_substr( $string ,$start ,$length ) );
	}

	/**
	 * Returns the part of a string <b>before the first</b> occurrence of
	 * the string to search for.
	 * If the string doesn't contain the needle returns the string itself
	 * 
	 * @param <b>string $string</b>
	 *        	The string to be searched
	 * @param <b>string $search</b>
	 *        	The string to search for
	 * @param <b>boolean $caseSensitive
	 *        	:optional</b> Defines if the search
	 *        	will be case sensitive. By default true.
	 * @return string
	 */
	public static function beforeFirst($string ,$search 
		,$caseSensitive = true)
	{
		return self::count( $string ,$search ,$caseSensitive ) === 0 
			? $string : self::substring($string 
				,0 ,self::firstIndexOf( $string ,$search ,$caseSensitive ) );
	}

	/**
	 * Returns the part of a string <b>before the last</b> occurrence of
	 * the string to search for.
	 * If the string doesn't contain the needle returns the string itself
	 * 
	 * @param <b>string $string</b>
	 *        	The string to be searched
	 * @param <b>string $search</b>
	 *        	The string to search for
	 * @param
	 *        	<b>$caseSensitive boolean :optional</b> Defines if the
	 *        	search will be case sensitive. By default true.
	 * @return string
	 */
	public static function beforeLast($string ,$search ,$caseSensitive = true)
	{
		return self::count( $string ,$search ,$caseSensitive ) === 0 ? 
			$string : self::substring(	$string 
				,0 ,self::lastIndexOf( $string ,$search ,$caseSensitive ) );
	}

	/**
	 * Returns the part of a string <b>after the first</b> occurrence of the
	 * string to search for.
	 * If the string doesn't contain the needle returns an empty string
	 * 
	 * @param <b>string $string</b>
	 *        	The string to be searched
	 * @param <b>string $search</b>
	 *        	The string to search for
	 * @param <b>boolean $caseSensitive
	 *        	:optional</b> Defines if the search
	 *        	will be case sensitive. By default true.
	 * @return string
	 */
	public static function afterFirst($string ,$search ,$caseSensitive = true)
	{
		return self::count( $string ,$search ,$caseSensitive ) === 0 ? "" 
			: self::substring( 
				$string ,
				self::firstIndexOf( $string ,$search ,$caseSensitive ) +
				 	self::length( $search ) );
	}
}
?>
2
Contributors
3
Replies
30
Views
1 Year
Discussion Span
Last Post by jkon
0

Of course if there is a chance to have special UTF8 characters (e.g. spaces or non Latin letters) in URL you should url decode the $afterRootUrl

$afterRootUrl = urldecode($afterRootUrl);

Edited by jkon

1

Hello cereal ,
Well actually it is not like parse_url , lets see an example but many more can be made,

Lets say that you have this .htaccess and this index.php in http://example.com/article
and the url is http://example.com/article/Weather/the-most-hot-year there is no way parse_url can tell you that the after root (the current folder where .htaccess and index.php is) is http://example.com/article and the after Root url Weather/the-most-hot-year.

Votes + Comments
perfectly clear, thanks for the explanation
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.