eZ Publish  [trunk]
ezuri.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZURI class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package lib
00009  */
00010 
00011 /**
00012  * Provides access to the HTTP uri
00013 
00014  * The URI can be accessed one element at a time with element() and elements() and
00015  * can be iterated with increase() and the current index returned with index(). Movin
00016  * the beginning and end is done with toBeginning() and toEnd().
00017  * The base can be retrieved with base() and the elements with elements().
00018  *
00019  * This class also supports the attribute system.
00020  *
00021  *
00022  * The index starts at 0
00023  *
00024  * @package eZHTTP
00025  */
00026 
00027 class eZURI
00028 {
00029     /**
00030      * The original URI string
00031      *
00032      * @var string
00033      */
00034     public $URI;
00035 
00036     /**
00037      * The URI array
00038      *
00039      * @var array
00040      */
00041     public $URIArray;
00042 
00043     /**
00044      * The current index
00045      *
00046      * @var int
00047      */
00048     public $Index;
00049 
00050     /**
00051      * User defined template variables
00052      *
00053      * @var array
00054      */
00055     public $UserArray;
00056 
00057     /**
00058      * URI transformation mode used by transformURI().
00059      *
00060      * @var string
00061      *
00062      * @see transformURI()
00063      * @see getTransformURIMode()
00064      * @see setTransformURIMode()
00065      */
00066     private static $transformURIMode = "relative";
00067 
00068     /**
00069      * Initializes with the URI string $uri. The URI string is split on / into an array.
00070      *
00071      * @param string $uri the URI to use
00072      * @return void
00073     */
00074     public function __construct( $uri )
00075     {
00076         $this->setURIString( $uri );
00077     }
00078 
00079     /**
00080      * Decodes a path string which is in IRI format and returns the new path in the internal encoding.
00081      *
00082      * More info on IRI here: {@link http://www.w3.org/International/O-URL-and-ident.html}
00083      *
00084      * @param string $str the string to decode
00085      * @return string decoded version of $str
00086      */
00087     public static function decodeIRI( $str )
00088     {
00089         $str = urldecode( $str ); // Decode %xx entries, we now have a utf-8 string
00090         $codec = eZTextCodec::instance( 'utf-8' ); // Make sure string is converted from utf-8 to internal encoding
00091         return $codec->convertString( $str );
00092     }
00093 
00094     /**
00095      * Encodes path string in internal encoding to a new string which conforms to the IRI specification.
00096      *
00097      * More info on IRI here: {@link http://www.w3.org/International/O-URL-and-ident.html}
00098      *
00099      * @param string $str the IRI to encode
00100      * @return string $str encoded as IRU
00101      */
00102     public static function encodeIRI( $str )
00103     {
00104         $codec = eZTextCodec::instance( false, 'utf-8' );
00105         $str = $codec->convertString( $str ); // Make sure the string is in utf-8
00106         $out = explode( "/", $str ); // Don't encode the slashes
00107         foreach ( $out as $i => $o )
00108         {
00109             if ( preg_match( "#^[\(]([a-zA-Z0-9_]+)[\)]#", $o, $m ) )
00110             {
00111                 // Don't encode '(' and ')' in user parameters
00112                 $out[$i] = '(' . urlencode( $m[1] ) . ')';
00113             }
00114             else
00115             {
00116                 $out[$i] = urlencode( $o ); // Let PHP do the rest
00117             }
00118         }
00119         $tmp = join( "/", $out );
00120         // Don't encode '~' in URLs
00121         $tmp = str_replace( '%7E', '~', $tmp );
00122         return $tmp;
00123     }
00124 
00125     /**
00126      * Parse URL and encode/decode its path string
00127      *
00128      * @param string $url the URL to parse
00129      * @param boolean $encode tells to encode the URI
00130      * @return string the parsed url
00131      */
00132     public static function codecURL( $url, $encode )
00133     {
00134         $originalLocale = setlocale( LC_CTYPE, "0" );
00135         setlocale( LC_CTYPE, 'C' );
00136         // Parse URL and encode the path.
00137         $data = parse_url( $url );
00138         setlocale( LC_CTYPE, $originalLocale );
00139 
00140         if ( isset( $data['path'] ) )
00141         {
00142             if ( $encode )
00143                 $data['path'] = eZURI::encodeIRI( $data['path'] ); // Make sure it is encoded to IRI format
00144             else
00145                 $data['path'] = eZURI::decodeIRI( $data['path'] ); // Make sure it is dencoded to internal encoding
00146         }
00147 
00148         // Reconstruct the URL
00149         $host    = '';
00150         $preHost = '';
00151         if ( isset( $data['user'] ) )
00152         {
00153             if ( isset( $data['pass'] ) )
00154                 $preHost .= $data['user'] . ':' . $data['pass'] . '@';
00155             else
00156                 $preHost .= $data['user'] . '@';
00157         }
00158         if ( isset( $data['host'] ) )
00159         {
00160             if ( isset( $data['port'] ) )
00161                 $host = $preHost . $data['host'] . ':' . $data['port'];
00162             else
00163                 $host = $preHost . $data['host'];
00164         }
00165         $url = '';
00166         if ( isset( $data['scheme'] ) )
00167             $url = $data['scheme'] . '://' . $host;
00168         else if ( strlen( $host ) > 0 )
00169             $url = '//' . $host;
00170         if ( isset( $data['path'] ) )
00171         {
00172             $url .= $data['path'];
00173         }
00174         if ( isset( $data['query'] ) )
00175         {
00176             $url .= '?' . $data['query'];
00177         }
00178         if ( isset( $data['fragment'] ) )
00179         {
00180             $url .= '#' . $data['fragment'];
00181         }
00182 
00183         return $url;
00184     }
00185 
00186     /**
00187      * Encodes path string of URL in internal encoding to a new string which conforms to the IRI specification.
00188      *
00189      * @param type $url
00190      * @return string the encoded url
00191      */
00192     public static function encodeURL( $url )
00193     {
00194         return eZURI::codecURL( $url, true );
00195     }
00196 
00197     /**
00198      * Decodes URL which has path string is in IRI format and returns the new URL with path in the internal encoding.
00199      *
00200      * @param string $url url to decode
00201      * @return string the decoded url
00202      */
00203     public static function decodeURL( $url )
00204     {
00205         return eZURI::codecURL( $url, false );
00206     }
00207 
00208     /**
00209      * Sets uri string for instance
00210      *
00211      * Sets the current URI string to $uri, the URI is then split into array elements
00212      * and index reset to 1.
00213      *
00214      * @param string $uri
00215      * @param boolean $fullInitialize
00216      * @return void
00217      */
00218     public function setURIString( $uri, $fullInitialize = true )
00219     {
00220         if ( strlen( $uri ) > 0 and
00221              $uri[0] == '/' )
00222             $uri = substr( $uri, 1 );
00223 
00224         $uri = eZURI::decodeIRI( $uri );
00225 
00226         $this->URI = $uri;
00227         $this->URIArray = explode( '/', $uri );
00228         $this->Index = 0;
00229 
00230         if ( $fullInitialize )
00231         {
00232             $this->OriginalURI = $uri;
00233             $this->UserArray = array();
00234 
00235             $ini = eZINI::instance( 'template.ini' );
00236 
00237             if ( $ini->variable( 'ControlSettings', 'OldStyleUserVariables' ) == 'enabled' )
00238             {
00239                 foreach( array_keys( $this->URIArray ) as $key )
00240                 {
00241                     if ( isset( $this->URIArray[$key] ) && preg_match( "(^[\(][a-zA-Z0-9_]+[\)])", $this->URIArray[$key] ) )
00242                     {
00243                         $this->UserArray[substr( $this->URIArray[$key], 1, strlen( $this->URIArray[$key] ) - 2 )] = $this->URIArray[$key+1];
00244                         unset( $this->URIArray[$key] );
00245                         unset( $this->URIArray[$key+1] );
00246                     }
00247                 }
00248             }
00249             else
00250             {
00251                 unset( $paramName );
00252                 unset( $paramValue );
00253                 foreach( array_keys( $this->URIArray ) as $key )
00254                 {
00255                     if ( isset( $this->URIArray[$key] ) )
00256                     {
00257                         if ( preg_match( "/^[\(][a-zA-Z0-9_]+[\)]/", $this->URIArray[$key] ) )
00258                         {
00259                             if ( isset( $paramName ) and isset( $paramValue ) )
00260                             {
00261                                 $this->UserArray[ $paramName ] = $paramValue;
00262                                 unset( $paramName );
00263                                 unset( $paramValue );
00264                             }
00265                             $paramName = substr( $this->URIArray[$key], 1, strlen( $this->URIArray[$key] ) - 2 );
00266                             if ( isset( $this->URIArray[$key+1] ) )
00267                             {
00268                                 $this->UserArray[ $paramName ] = $this->URIArray[$key+1];
00269                                 unset( $this->URIArray[$key+1] );
00270                             }
00271                             else
00272                                 $this->UserArray[ $paramName ] = "";
00273                             unset( $this->URIArray[$key] );
00274                         }
00275                         else
00276                         {
00277                             if ( isset( $paramName ) )
00278                             {
00279                                 if ( !empty( $this->URIArray[$key] ) )
00280                                     $this->UserArray[ $paramName ] .= '/' . $this->URIArray[$key];
00281                                 unset( $this->URIArray[$key] );
00282                             }
00283                         }
00284                     }
00285                 }
00286             }
00287 
00288             // Remake the URI without any user parameters
00289             $this->URI = implode( '/', $this->URIArray );
00290 
00291             $ini = eZINI::instance( 'template.ini' );
00292             if ( $ini->variable( 'ControlSettings', 'AllowUserVariables' ) == 'false' )
00293             {
00294                 $this->UserArray = array();
00295             }
00296             // Convert filter string to current locale
00297             $this->convertFilterString();
00298         }
00299     }
00300 
00301     /**
00302      * Get the uri string
00303      *
00304      * The URI will not include the leading / if $withLeadingSlash is true.
00305      *
00306      * @param boolean $withLeadingSlash prefix the uri with /
00307      * @return string the URI passed as to the object
00308      */
00309     public function uriString( $withLeadingSlash = false )
00310     {
00311         $uri = $this->URI;
00312         if ( $withLeadingSlash )
00313             $uri = "/$uri";
00314         return $uri;
00315     }
00316 
00317     /**
00318      * Return the original URI
00319      *
00320      * the URI will not include the leading / if $withLeadingSlash is true.
00321      *
00322      * @param boolean $withLeadingSlash prefix the uri with /
00323      * @return string the URI passed to the object with user parameters (if any).
00324      */
00325     public function originalURIString( $withLeadingSlash = false )
00326     {
00327         $uri = $this->OriginalURI;
00328         if ( $withLeadingSlash )
00329             $uri = "/$uri";
00330         return $uri;
00331     }
00332 
00333     /**
00334      * Check if there URI is set
00335      *
00336      * @return boolean true if the URI is empty, ie it's equal to / or empty string.
00337      */
00338     public function isEmpty()
00339     {
00340         return $this->URI == '' or $this->URI == '/';
00341     }
00342 
00343     /**
00344      * Get element index from uri
00345      *
00346      * @param integer $index the index of URI to return
00347      * @param boolean $relative if index is relative to the internal pointer
00348      * @return string|null the element at $index
00349      */
00350     public function element( $index = 0, $relative = true )
00351     {
00352         $pos = $index;
00353         if ( $relative )
00354             $pos += $this->Index;
00355         if ( isset( $this->URIArray[$pos] ) )
00356             return $this->URIArray[$pos];
00357         $ret = null;
00358         return $ret;
00359     }
00360 
00361     /**
00362      * Return all URI elements
00363      *
00364      * @param boolean $as_text return the elements as string
00365      * @return array|string all elements as string/array depending on $as_text
00366      */
00367     public function elements( $as_text = true )
00368     {
00369         $elements = array_slice( $this->URIArray, $this->Index );
00370         if ( $as_text )
00371         {
00372             $retValue = implode( '/', $elements );
00373             return $retValue;
00374         }
00375         else
00376             return $elements;
00377     }
00378 
00379     /**
00380      * Converts filter string to current locale. When an user types in browser
00381      * url like: "/content/view/full/2/(namefilter)/a" 'a' letter should be
00382      * urldecoded and converted from utf-8 to current locale.
00383      *
00384      * @return string converted string
00385      */
00386     public function convertFilterString()
00387     {
00388         foreach ( array_keys( $this->UserArray ) as $paramKey )
00389         {
00390             if ( $paramKey == 'namefilter' )
00391             {
00392                 $char = $this->UserArray[$paramKey];
00393                 $char = urldecode( $char );
00394 
00395                 $codec = eZTextCodec::instance( 'utf-8', false );
00396                 if ( $codec )
00397                     $char = $codec->convertString( $char );
00398             }
00399         }
00400     }
00401 
00402     /**
00403      * Get user variables
00404      *
00405      * @return array all the user defined variables
00406      */
00407     public function userParameters()
00408     {
00409         return $this->UserArray;
00410     }
00411 
00412     /**
00413      * Reset the internal pointer
00414      *
00415      * @return void
00416      */
00417     public function toBeginning()
00418     {
00419         $this->Index = 0;
00420     }
00421 
00422     /**
00423      * Moves the index to the end.
00424      *
00425      * @return void
00426      */
00427     public function toEnd()
00428     {
00429         $this->Index = count( $this->URIArray );
00430     }
00431 
00432     /**
00433      * Moves the index 1 step up or $num if specified.
00434      *
00435      * @param int $num number of steps to move pointer
00436      * @return void
00437      */
00438     public function increase( $num = 1 )
00439     {
00440         $this->Index += $num;
00441         if ( $this->Index < 0 )
00442             $this->Index = 0;
00443     }
00444 
00445     /**
00446      * Removes all elements below the current index, recreates the URI string
00447      * and sets index to 0.
00448      *
00449      * @return void
00450      */
00451     public function dropBase()
00452     {
00453         $elements = array_slice( $this->URIArray, $this->Index );
00454         $this->URIArray = $elements;
00455         $this->URI = implode( '/', $this->URIArray );
00456         $uri = $this->URI;
00457         foreach ( $this->UserArray as $name => $value )
00458         {
00459             $uri .= '/(' . $name . ')/' . $value;
00460         }
00461         $this->OriginalURI = $uri;
00462         $this->Index = 0;
00463     }
00464 
00465     /**
00466      * Return the current position of pointer
00467      *
00468      * @return int the current pointer position.
00469      */
00470     public function index()
00471     {
00472         return $this->Index;
00473     }
00474 
00475     /**
00476      * Return the elements before pointer
00477      *
00478      * @param type $as_text return as text or array
00479      * @return string|array the base string or the base elements as an array if $as_text is true.
00480      */
00481     public function base( $as_text = true )
00482     {
00483         $elements = array_slice( $this->URIArray, 0, $this->Index );
00484         if ( $as_text )
00485         {
00486             $baseAsText = '/' . implode( '/', $elements );
00487             return $baseAsText;
00488         }
00489         else
00490             return $elements;
00491     }
00492 
00493     /**
00494      * Tries to match the base of $uri against this base and returns the result.
00495      * A match is made if all elements of this object match the base elements of
00496      * the $uri object, this means that $uri's base may be longer than this base but
00497      * not shorter.
00498      *
00499      * @param eZURI $uri the uri to match against
00500      * @return boolean
00501      */
00502     public function matchBase( eZURI $uri )
00503     {
00504         if ( !( $uri instanceof eZURI ) )
00505         {
00506             return false;
00507         }
00508         if ( count( $this->URIArray ) == 0 or
00509              count( $uri->URIArray ) == 0 )
00510         {
00511             return false;
00512         }
00513         for ( $i = 0; $i < count( $this->URIArray ); ++$i )
00514         {
00515             if ( $this->URIArray[$i] != $uri->URIArray[$i] )
00516             {
00517                 return false;
00518             }
00519         }
00520         return true;
00521     }
00522 
00523     /**
00524      * Export all attributes of the object
00525      *
00526      * @return array the attributes for the object
00527      */
00528     public function attributes()
00529     {
00530         return array( 'element',
00531                       'base',
00532                       'tail',
00533                       'index',
00534                       'uri',
00535                       'original_uri',
00536                       'query_string' );
00537     }
00538 
00539     /**
00540      * Check if attribute exsits
00541      *
00542      * @param string $attr the attrbiute to check if exists
00543      * @return boolean
00544      */
00545     public function hasAttribute( $attr )
00546     {
00547         return in_array( $attr, $this->attributes() );
00548     }
00549 
00550     /**
00551      * Get value for an attribute
00552      *
00553      * @param string $attr
00554      * @return boolean the value for attribute $attr or null if it does not exist.
00555      */
00556     public function attribute( $attr )
00557     {
00558         switch ( $attr )
00559         {
00560             case 'element':
00561                 return $this->element();
00562                 break;
00563             case 'tail':
00564                 return $this->elements();
00565                 break;
00566             case 'base':
00567                 return $this->base();
00568                 break;
00569             case 'index':
00570                 return $this->index();
00571                 break;
00572             case 'uri':
00573                 return $this->uriString();
00574                 break;
00575             case 'original_uri':
00576                 return $this->originalURIString();
00577                 break;
00578             case 'query_string':
00579                 return eZSys::queryString();
00580                 break;
00581             default:
00582             {
00583                 eZDebug::writeError( "Attribute '$attr' does not exist", __METHOD__ );
00584                 return null;
00585             } break;
00586         }
00587     }
00588 
00589     /**
00590      * Returns a shared instance of the eZURI class IF $uri is false or the same as current
00591      * request uri, if not then a new non shared instance is created.
00592      *
00593      * @param false|string $uri Shared uri instance if false
00594      * @return eZURI
00595      */
00596     public static function instance( $uri = false )
00597     {
00598         // If $uri is false we assume the caller wants eZSys::requestURI()
00599         if ( $uri === false or $uri == eZSys::requestURI() )
00600         {
00601             if ( !isset( $GLOBALS['eZURIRequestInstance'] ) )
00602             {
00603                 $GLOBALS['eZURIRequestInstance'] = new eZURI( eZSys::requestURI() );
00604             }
00605             return $GLOBALS['eZURIRequestInstance'];
00606         }
00607 
00608         return new eZURI( $uri );
00609     }
00610 
00611     /**
00612      * Implementation of an 'ezurl' template operator
00613      * Makes valid eZ Publish urls to use in links
00614      *
00615      * @param string &$href
00616      * @param boolean $ignoreIndexDir
00617      * @param string $serverURL full|relative
00618      * @return string the link to use
00619      */
00620     public static function transformURI( &$href, $ignoreIndexDir = false, $serverURL = null )
00621     {
00622         if ( $serverURL === null )
00623         {
00624             $serverURL = self::$transformURIMode;
00625         }
00626 
00627         if ( preg_match( "#^[a-zA-Z0-9]+:#", $href ) || substr( $href, 0, 2 ) == '//' )
00628             return false;
00629 
00630         if ( strlen( $href ) == 0 )
00631             $href = '/';
00632         else if ( $href[0] == '#' )
00633         {
00634             $href = htmlspecialchars( $href );
00635             return true;
00636         }
00637         else if ( $href[0] != '/' )
00638         {
00639             $href = '/' . $href;
00640         }
00641 
00642         $sys = eZSys::instance();
00643         $dir = !$ignoreIndexDir ? $sys->indexDir() : $sys->wwwDir();
00644         $serverURL = $serverURL === 'full' ? $sys->serverURL() : '' ;
00645         $href = $serverURL . $dir . $href;
00646         if ( !$ignoreIndexDir )
00647         {
00648             $href = preg_replace( "#^(//)#", "/", $href );
00649             $href = preg_replace( "#(^.*)(/+)$#", "\$1", $href );
00650         }
00651         $href = str_replace( '&amp;amp;', '&amp;', htmlspecialchars( $href ) );
00652 
00653         if ( $href == "" )
00654             $href = "/";
00655 
00656         return true;
00657     }
00658 
00659     /**
00660      * Returns the current mode used for transformURI().
00661      *
00662      * @see transformURI()
00663      * @see setTransformURIMode()
00664      *
00665      * @return string
00666      */
00667     public static function getTransformURIMode()
00668     {
00669         return self::$transformURIMode;
00670     }
00671 
00672     /**
00673      * Sets the current mode used for transformURI() to $mode.
00674      *
00675      * @see transformURI()
00676      * @see getTransformURIMode()
00677      *
00678      * @param string $mode
00679      */
00680     public static function setTransformURIMode( $mode )
00681     {
00682         self::$transformURIMode = $mode;
00683     }
00684 
00685 }
00686 
00687 ?>