Creating Links (aka Anchor tags) - The "l" Function

Note: This article covers the l function for Drupal 5. While most of the information is still applicable to Drupal 6, the function call has changed. See http://api.drupal.org/api/function/l/6 for the Drupal 6 version of the call.

Why would anchor tags warrant attention? It's just a text string after all. Concatenate a variable or two with some text to create the link and you're done, right? Maybe.

Do you want to move your code between different Drupal installations? If the answer is yes you need to worry about handling the differences between servers, things like whether clean URLs (1) are enabled or the instance is installed in a base directory (2).

Having to test and determine output based on specific conditions sounds like a good candidate for a function. The Drupal programmers who came before you though so as well, thus we have the "l" function.

The l function brings with it other advantages. If the link is Drupal content with a URL path alias the l function will automatically use the path alias, even if it's passed the Drupal "system" URL. If the URL you're specifying happens to be the current page, it automatically adds class="active", which is very handy when using CSS to theme navigation links.

A Simple Example

Still not convinced it's worth the trouble? Let's compare methods by creating an anchor tag with text provided in a variable named $linktext, and a URL path provided in $path.

Using string concatenation we'd need to do something like this:

<?php

  
if ((bool)variable_get('clean_url', '0')) {
    
$link = '<a href="' . $path . '">' . $linktext . '</a>';
   } else {
    
$link = '<a href="?q=' . $path. '">' . $linktext . '</a>';
   }

?>

This doesn't take into account creating absolute links where we'd need to grab server name and base directory info. It also wouldn't automatically look up an alias path.

Using the l function, which takes everything into account:

<?php

   $link
= l($linktext, $path);

?>

The Full "l" Function

Documentation on the "l" function for each of the Drupal versions is available at api.drupal.org. For Drupal 5, http://api.drupal.org/api/5/function/l:

   l($text, $path, $attributes = array(), $query = NULL, $fragment = NULL,
     $absolute = FALSE, $html = FALSE)

A URL can get quite involved. For a full explanation of the URL components, see this article: http://en.wikipedia.org/wiki/Url The l function lets you specify most of these pieces individually.

Specifying Drupal "System" Paths

When specifying a system path don't start the string with '/'. For example, use node/1 not /node/1. The starting '/' signifies an external URL. If passed /node/1 the l function will use the exact path you specify to create the URL, resulting in: href="//node/1"

Using node/1 tells the l function this is a Drupal content path. Drupal not only constructs the link taking the server configuration into account it checks for a path alias. If node/1 has a alias of my_first_post, the link is built using the alias.

For example, l('Permalink', 'node/1') would produce:

   <a href="/my_first_post">Permalink</a>

Specifying Anchor Tag Attributes

Two common anchor tag attributes are class and target. Here's how we add them to the previous example:

<?php

   $attributes
= array( 'class' => 'foo', 'target' => '_blank' );
  
$link = l('Permalink', 'node/1', $attributes);

?>

This would produce:

   <a target="_blank" class="foo" href="/my_first_post">Permalink</a>

Linking to Images

The function's default action assumes that $text is pure text, not HTML. If < and > are present they're automatically converted into &lt; and &gt;. If you're linking an image you need the HTML so set the $html parameter TRUE. As you'll see in the l function's full description the $html parameter is the last of seven parameters, so you need to specify all of the preceding parameters. This call would therefore look something like:

<?php

   $image
= '<img src="…" alt="Alternative Text" />';
  
$link = l($image, $url, array(), NULL, NULL, FALSE, TRUE);

?>

Conclusion

The l function provides important functionality for writing portable code. If you're writing a module or theme and want to insure it will work in any configuration you'd probably write this function yourself if it wasn't available. Even those not concerned about portability will typically find the "l" function convenient to use.

1: Clean URLs

URLs can have two forms in Drupal, one that uses the a query string, and one that doesn't:
1: http://www.example.com?q=node/4
2: http://www.example.com/node/4

Example number two is referred to as "clean".

2: Base Directory

Drupal can be run from directories in the server root. For example:

http://www.example.com/~dale

Since content appears under this directory, it's referred to as the base directory.

In the example below, ~dale is the base directory, while node/4 is the Drupal system path:

http://www.example.com/~dale/node/4

Comments

Nice focus on two functions that most devs probably use all the time without thinking ;). But, you forgot the most important reason of all: security.

url() and l() will apply the correct escaping to each URL component as needed, ensuring safe output (with things like urlencode and check_url) and working around annoying mod_rewrite bugs as needed.

Your 'simple example' could be a vector for XSS, depending on where the variables originate from.

The Drupal.org security docs have more good and bad examples, as well as background info.

Excellent points!

During the thought process for the article I had remembered security, but it was (obviously) forgotten and didn't make it to page. Thanks for the catch. It makes the point of the example even stronger. And it's a beautiful thing when the simplest route is also the most secure.

mod_rewrite bugs? I was blissfully unaware of those!

Thanks for this tutorial. It has helped me understand the l function better than anything else I could find.

However, I'm not sure if there has been a change in Drupal 6 or whether there might be an error in the "Specifying Anchor Tag Attributes" section regarding $attributes.

The only way I could get it to work for me was by using the following:

$options['attributes'] = array( 'class' => 'foo', 'target' => '_blank' );
print l('Permalink', 'node/1', $options);

Thanks again!

Herb, I'm glad you found this article useful.

You're exactly right. There was a change between Drupal 5 and Drupal 6. The function signature in Drupal 6 is: l($text, $path, $options = array())

I believe this was to simplify the call. In D5 you had to specify a bunch of empty parameters for something towards the end, like HTML. In D6 you specify only what you need to specify.

Details at http://api.drupal.org/api/function/l/6

Sir, Thanks for a very good article on an important topic like linking and paths which are at times very confusing. I am facing a little problem myself when I am trying to put image directly through adding blocks. When I am giving full URL in img src, it is working while the relative path (as the image path is already set as site/default/file) like images/xyz.jpg is not working. It is creating a lot of problem. For instance I was trying to put a simple jquery slideshow. I kept trying but was not getting any output. Much later when I changed the image path to full URL in block configuration of full html input, it worked fine. My question is: why only full URL is working and not relative path? Plz help if possible. I will be obliged.