2006-01-24 08:18:26 +00:00
< ? php
// $Id$
2006-01-23 07:54:08 +00:00
/**
* @ file
* Functions to handle paths in Drupal , including path aliasing .
*
* These functions are not loaded for cached pages , but modules that need
* to use them in hook_init () or hook exit () can make them available , by
* executing " drupal_bootstrap(DRUPAL_BOOTSTRAP_PATH); " .
*/
/**
* Initialize the $_GET [ 'q' ] variable to the proper normal path .
*/
2009-07-14 10:22:17 +00:00
function drupal_path_initialize () {
2006-01-23 07:54:08 +00:00
if ( ! empty ( $_GET [ 'q' ])) {
$_GET [ 'q' ] = drupal_get_normal_path ( trim ( $_GET [ 'q' ], '/' ));
}
else {
$_GET [ 'q' ] = drupal_get_normal_path ( variable_get ( 'site_frontpage' , 'node' ));
}
}
/**
* Given an alias , return its Drupal system URL if one exists . Given a Drupal
2006-11-26 01:55:37 +00:00
* system URL return one of its aliases if such a one exists . Otherwise ,
* return FALSE .
2006-01-23 07:54:08 +00:00
*
* @ param $action
* One of the following values :
* - wipe : delete the alias cache .
* - alias : return an alias for a given Drupal system path ( if one exists ) .
* - source : return the Drupal system URL for a path alias ( if one exists ) .
* @ param $path
* The path to investigate for corresponding aliases or system URLs .
2007-03-26 01:32:22 +00:00
* @ param $path_language
* Optional language code to search the path with . Defaults to the page language .
* If there ' s no path defined for that language it will search paths without
* language .
2006-01-23 07:54:08 +00:00
*
* @ return
* Either a Drupal system path , an aliased path , or FALSE if no path was
* found .
*/
2007-03-26 01:32:22 +00:00
function drupal_lookup_path ( $action , $path = '' , $path_language = '' ) {
global $language ;
2009-08-02 06:48:24 +00:00
$cache = & drupal_static ( __FUNCTION__ , array (
'map' => array (),
'no_src' => array (),
'whitelist' => NULL ,
'system_paths' => array (),
'no_aliases' => array (),
'first_call' => TRUE ,
));
2007-03-26 01:32:22 +00:00
2009-06-01 11:23:27 +00:00
// Retrieve the path alias whitelist.
2009-08-02 06:48:24 +00:00
if ( ! isset ( $cache [ 'whitelist' ])) {
$cache [ 'whitelist' ] = variable_get ( 'path_alias_whitelist' , NULL );
if ( ! isset ( $cache [ 'whitelist' ])) {
$cache [ 'whitelist' ] = drupal_path_alias_whitelist_rebuild ();
2009-06-01 11:23:27 +00:00
}
2008-06-24 22:12:15 +00:00
}
2009-06-01 11:23:27 +00:00
$path_language = $path_language ? $path_language : $language -> language ;
2006-01-23 07:54:08 +00:00
if ( $action == 'wipe' ) {
2009-08-02 06:48:24 +00:00
$cache = array ();
$cache [ 'whitelist' ] = drupal_path_alias_whitelist_rebuild ();
2006-01-23 07:54:08 +00:00
}
2009-08-02 06:48:24 +00:00
elseif ( $cache [ 'whitelist' ] && $path != '' ) {
2006-01-23 07:54:08 +00:00
if ( $action == 'alias' ) {
2009-05-16 19:07:02 +00:00
// During the first call to drupal_lookup_path() per language, load the
// expected system paths for the page from cache.
2009-08-02 06:48:24 +00:00
if ( ! empty ( $cache [ 'first_call' ])) {
$cache [ 'first_call' ] = FALSE ;
2009-06-01 11:23:27 +00:00
2009-08-02 06:48:24 +00:00
$cache [ 'map' ][ $path_language ] = array ();
2009-05-16 19:07:02 +00:00
// Load system paths from cache.
$cid = current_path ();
2009-08-02 06:48:24 +00:00
if ( $cached = cache_get ( $cid , 'cache_path' )) {
$cache [ 'system_paths' ] = $cached -> data ;
2009-05-16 19:07:02 +00:00
// Now fetch the aliases corresponding to these system paths.
// We order by ASC and overwrite array keys to ensure the correct
// alias is used when there are multiple aliases per path.
2009-08-02 06:48:24 +00:00
$cache [ 'map' ][ $path_language ] = db_query ( " SELECT src, dst FROM { url_alias} WHERE src IN (:system) AND language IN (:language, '') ORDER BY language ASC, pid ASC " , array (
':system' => $cache [ 'system_paths' ],
2009-05-16 19:07:02 +00:00
':language' => $path_language
)) -> fetchAllKeyed ();
// Keep a record of paths with no alias to avoid querying twice.
2009-08-02 06:48:24 +00:00
$cache [ 'no_aliases' ][ $path_language ] = array_flip ( array_diff_key ( $cache [ 'system_paths' ], array_keys ( $cache [ 'map' ][ $path_language ])));
2009-05-16 19:07:02 +00:00
}
}
// If the alias has already been loaded, return it.
2009-08-02 06:48:24 +00:00
if ( isset ( $cache [ 'map' ][ $path_language ][ $path ])) {
return $cache [ 'map' ][ $path_language ][ $path ];
2006-01-23 07:54:08 +00:00
}
2009-06-01 11:23:27 +00:00
// Check the path whitelist, if the top_level part before the first /
// is not in the list, then there is no need to do anything further,
// it is not in the database.
2009-08-02 06:48:24 +00:00
elseif ( ! isset ( $cache [ 'whitelist' ][ strtok ( $path , '/' )])) {
2009-06-01 11:23:27 +00:00
return FALSE ;
}
2009-05-16 19:07:02 +00:00
// For system paths which were not cached, query aliases individually.
2009-08-02 06:48:24 +00:00
else if ( ! isset ( $cache [ 'no_aliases' ][ $path_language ][ $path ])) {
2009-05-16 19:07:02 +00:00
// Get the most fitting result falling back with alias without language
2009-08-02 06:48:24 +00:00
$alias = db_query ( " SELECT dst FROM { url_alias} WHERE src = :src AND language IN (:language, '') ORDER BY language DESC, pid DESC " , array (
2009-05-16 19:07:02 +00:00
':src' => $path ,
':language' => $path_language
)) -> fetchField ();
2009-08-02 06:48:24 +00:00
$cache [ 'map' ][ $path_language ][ $path ] = $alias ;
2009-05-16 19:07:02 +00:00
return $alias ;
}
2006-01-23 07:54:08 +00:00
}
2006-11-26 01:55:37 +00:00
// Check $no_src for this $path in case we've already determined that there
// isn't a path that has this alias
2009-08-02 06:48:24 +00:00
elseif ( $action == 'source' && ! isset ( $cache [ 'no_src' ][ $path_language ][ $path ])) {
2006-11-26 01:55:37 +00:00
// Look for the value $path within the cached $map
2007-03-26 01:32:22 +00:00
$src = '' ;
2009-08-02 06:48:24 +00:00
if ( ! isset ( $cache [ 'map' ][ $path_language ]) || ! ( $src = array_search ( $path , $cache [ 'map' ][ $path_language ]))) {
2007-03-26 01:32:22 +00:00
// Get the most fitting result falling back with alias without language
2009-08-02 06:48:24 +00:00
if ( $src = db_query ( " SELECT src FROM { url_alias} WHERE dst = :dst AND language IN (:language, '') ORDER BY language DESC, pid DESC " , array (
2009-01-03 11:20:59 +00:00
':dst' => $path ,
':language' => $path_language ))
2009-01-03 13:17:25 +00:00
-> fetchField ()) {
2009-08-02 06:48:24 +00:00
$cache [ 'map' ][ $path_language ][ $src ] = $path ;
2006-11-26 01:55:37 +00:00
}
else {
// We can't record anything into $map because we do not have a valid
// index and there is no need because we have not learned anything
// about any Drupal path. Thus cache to $no_src.
2009-08-02 06:48:24 +00:00
$cache [ 'no_src' ][ $path_language ][ $path ] = TRUE ;
2006-01-23 07:54:08 +00:00
}
}
2006-11-26 01:55:37 +00:00
return $src ;
2006-01-23 07:54:08 +00:00
}
}
return FALSE ;
}
2009-05-16 19:07:02 +00:00
/**
* Cache system paths for a page .
*
* Cache an array of the system paths available on each page . We assume
* that aiases will be needed for the majority of these paths during
* subsequent requests , and load them in a single query during
* drupal_lookup_path () .
*/
function drupal_cache_system_paths () {
// Check if the system paths for this page were loaded from cache in this
// request to avoid writing to cache on every request.
2009-08-02 06:48:24 +00:00
$cache = & drupal_static ( 'drupal_lookup_path' , array ());
if ( ! $cache [ 'system_paths' ]) {
2009-05-16 19:07:02 +00:00
// Generate a cache ID (cid) specifically for this page.
$cid = current_path ();
2009-08-02 06:48:24 +00:00
// The static $map array used by drupal_lookup_path() includes all
// system paths for the page request.
if ( $paths = current ( $cache [ 'map' ])) {
2009-05-16 19:07:02 +00:00
$data = array_keys ( $paths );
$expire = REQUEST_TIME + ( 60 * 60 * 24 );
cache_set ( $cid , $data , 'cache_path' , $expire );
}
}
}
2006-01-23 07:54:08 +00:00
/**
* Given an internal Drupal path , return the alias set by the administrator .
*
2009-05-07 15:49:57 +00:00
* If no path is provided , the function will return the alias of the current
* page .
*
2006-01-23 07:54:08 +00:00
* @ param $path
* An internal Drupal path .
2007-03-26 01:32:22 +00:00
* @ param $path_language
* An optional language code to look up the path in .
2006-01-23 07:54:08 +00:00
*
* @ return
* An aliased path if one was found , or the original path if no alias was
* found .
*/
2009-05-07 15:49:57 +00:00
function drupal_get_path_alias ( $path = NULL , $path_language = '' ) {
// If no path is specified, use the current page's path.
if ( $path == NULL ) {
$path = $_GET [ 'q' ];
}
2006-01-23 07:54:08 +00:00
$result = $path ;
2007-03-26 01:32:22 +00:00
if ( $alias = drupal_lookup_path ( 'alias' , $path , $path_language )) {
2006-01-23 07:54:08 +00:00
$result = $alias ;
}
return $result ;
}
/**
* Given a path alias , return the internal path it represents .
*
* @ param $path
* A Drupal path alias .
2007-03-26 01:32:22 +00:00
* @ param $path_language
* An optional language code to look up the path in .
2006-01-23 07:54:08 +00:00
*
* @ return
* The internal path represented by the alias , or the original alias if no
* internal path was found .
*/
2007-03-26 01:32:22 +00:00
function drupal_get_normal_path ( $path , $path_language = '' ) {
2006-01-23 07:54:08 +00:00
$result = $path ;
2007-03-26 01:32:22 +00:00
if ( $src = drupal_lookup_path ( 'source' , $path , $path_language )) {
2006-01-23 07:54:08 +00:00
$result = $src ;
}
2007-06-18 06:59:11 +00:00
if ( function_exists ( 'custom_url_rewrite_inbound' )) {
// Modules may alter the inbound request path by reference.
custom_url_rewrite_inbound ( $result , $path , $path_language );
2006-01-23 07:54:08 +00:00
}
return $result ;
}
/**
* Return a component of the current Drupal path .
*
2009-07-20 18:51:36 +00:00
* When viewing a page at the path " admin/structure/types " , for example , arg ( 0 )
2009-01-04 20:04:32 +00:00
* returns " admin " , arg ( 1 ) returns " content " , and arg ( 2 ) returns " types " .
2006-01-23 07:54:08 +00:00
*
* Avoid use of this function where possible , as resulting code is hard to read .
2009-01-04 20:04:32 +00:00
* In menu callback functions , attempt to use named arguments . See the explanation
* in menu . inc for how to construct callbacks that take arguments . When attempting
* to use this function to load an element from the current path , e . g . loading the
* node on a node page , please use menu_get_object () instead .
2006-01-23 07:54:08 +00:00
*
* @ param $index
* The index of the component , where each component is separated by a '/'
* ( forward - slash ), and where the first component has an index of 0 ( zero ) .
2009-07-27 19:28:38 +00:00
* @ param $path
* A path to break into components . Defaults to the path of the current page .
2006-01-23 07:54:08 +00:00
*
* @ return
2007-11-04 16:42:45 +00:00
* The component specified by $index , or NULL if the specified component was
2006-01-23 07:54:08 +00:00
* not found .
*/
2007-01-24 14:48:36 +00:00
function arg ( $index = NULL , $path = NULL ) {
2009-04-03 17:41:32 +00:00
$arguments = & drupal_static ( __FUNCTION__ );
2006-01-23 07:54:08 +00:00
2007-01-24 14:48:36 +00:00
if ( ! isset ( $path )) {
$path = $_GET [ 'q' ];
2006-01-23 07:54:08 +00:00
}
2007-01-24 14:48:36 +00:00
if ( ! isset ( $arguments [ $path ])) {
$arguments [ $path ] = explode ( '/' , $path );
}
if ( ! isset ( $index )) {
return $arguments [ $path ];
}
if ( isset ( $arguments [ $path ][ $index ])) {
return $arguments [ $path ][ $index ];
2006-01-23 07:54:08 +00:00
}
}
/**
* Get the title of the current page , for display on the page and in the title bar .
*
* @ return
* The current page ' s title .
*/
function drupal_get_title () {
$title = drupal_set_title ();
2009-05-26 09:12:29 +00:00
// During a bootstrap, menu.inc is not included and thus we cannot provide a title.
2006-01-23 07:54:08 +00:00
if ( ! isset ( $title ) && function_exists ( 'menu_get_active_title' )) {
$title = check_plain ( menu_get_active_title ());
}
return $title ;
}
/**
* Set the title of the current page , for display on the page and in the title bar .
*
* @ param $title
* Optional string value to assign to the page title ; or if set to NULL
* ( default ), leaves the current title unchanged .
2008-10-14 11:01:08 +00:00
* @ param $output
* Optional flag - normally should be left as CHECK_PLAIN . Only set to
* PASS_THROUGH if you have already removed any possibly dangerous code
* from $title using a function like check_plain () or filter_xss () . With this
* flag the string will be passed through unchanged .
2006-01-23 07:54:08 +00:00
*
* @ return
* The updated title of the current page .
*/
2008-10-14 11:01:08 +00:00
function drupal_set_title ( $title = NULL , $output = CHECK_PLAIN ) {
2009-04-03 17:41:32 +00:00
$stored_title = & drupal_static ( __FUNCTION__ );
2006-01-23 07:54:08 +00:00
if ( isset ( $title )) {
2008-10-14 11:01:08 +00:00
$stored_title = ( $output == PASS_THROUGH ) ? $title : check_plain ( $title );
2006-01-23 07:54:08 +00:00
}
2008-12-09 07:16:10 +00:00
2006-01-23 07:54:08 +00:00
return $stored_title ;
}
2006-04-24 19:25:37 +00:00
/**
* Check if the current page is the front page .
*
* @ return
* Boolean value : TRUE if the current page is the front page ; FALSE if otherwise .
*/
function drupal_is_front_page () {
2009-04-03 17:41:32 +00:00
$is_front_page = & drupal_static ( __FUNCTION__ );
2008-12-09 07:16:10 +00:00
if ( ! isset ( $is_front_page )) {
2009-07-14 10:22:17 +00:00
// As drupal_path_initialize updates $_GET['q'] with the 'site_frontpage' path,
2008-12-09 07:16:10 +00:00
// we can check it against the 'site_frontpage' variable.
$is_front_page = ( $_GET [ 'q' ] == drupal_get_normal_path ( variable_get ( 'site_frontpage' , 'node' )));
}
return $is_front_page ;
2006-04-24 19:25:37 +00:00
}
2007-10-16 14:10:33 +00:00
/**
* Check if a path matches any pattern in a set of patterns .
*
* @ param $path
* The path to match .
* @ param $patterns
* String containing a set of patterns separated by \n , \r or \r\n .
*
* @ return
* Boolean value : TRUE if the path matches a pattern , FALSE otherwise .
*/
function drupal_match_path ( $path , $patterns ) {
2009-04-03 17:41:32 +00:00
$regexps = & drupal_static ( __FUNCTION__ );
2007-10-17 12:34:16 +00:00
2007-10-16 14:10:33 +00:00
if ( ! isset ( $regexps [ $patterns ])) {
2008-04-14 17:48:46 +00:00
$regexps [ $patterns ] = '/^(' . preg_replace ( array ( '/(\r\n?|\n)/' , '/\\\\\*/' , '/(^|\|)\\\\<front\\\\>($|\|)/' ), array ( '|' , '.*' , '\1' . preg_quote ( variable_get ( 'site_frontpage' , 'node' ), '/' ) . '\2' ), preg_quote ( $patterns , '/' )) . ')$/' ;
2007-10-16 14:10:33 +00:00
}
2008-06-18 03:36:24 +00:00
return ( bool ) preg_match ( $regexps [ $patterns ], $path );
2007-10-16 14:10:33 +00:00
}
2009-05-06 15:51:36 +00:00
/**
* Return the current URL path of the page being viewed .
*
* Examples :
* - http :// example . com / node / 306 returns " node/306 " .
* - http :// example . com / drupalfolder / node / 306 returns " node/306 " while
* base_path () returns " /drupalfolder/ " .
* - http :// example . com / path / alias ( which is a path alias for node / 306 ) returns
* " node/306 " as opposed to the path alias .
*
* This function is not available in hook_boot () so use $_GET [ 'q' ] instead .
* However , be careful when doing that because in the case of Example #3
* $_GET [ 'q' ] will contain " path/alias " . If " node/306 " is needed , calling
* drupal_bootstrap ( DRUPAL_BOOTSTRAP_PATH ) makes this function available .
*
* @ return
* The current Drupal URL path .
*/
function current_path () {
return $_GET [ 'q' ];
}
2009-06-01 11:23:27 +00:00
/**
* Rebuild the path alias white list .
*
* @ return
* An array containing a white list of path aliases .
*/
function drupal_path_alias_whitelist_rebuild () {
// For each alias in the database, get the top level component of the system
// path it corresponds to. This is the portion of the path before the first /
// if present, otherwise the whole path itself.
$whitelist = array ();
$result = db_query ( " SELECT SUBSTRING_INDEX(src, '/', 1) AS path FROM { url_alias} GROUP BY path " );
foreach ( $result as $row ) {
$whitelist [ $row -> path ] = TRUE ;
}
variable_set ( 'path_alias_whitelist' , $whitelist );
return $whitelist ;
}