This ought to be in a FAQ somewhere, I certainly get asked it enough: “How can I retrieve attributes from LDAP of users who log into my web application?” Well, I’ll break it down with an example that retrieves information about a user that submits their uid & password to a form. Note that this code could be simplified to handle authentication only by determining if the ldap_bind() succeeds or not — it’s usually enough to simply be able to bind to verify someones identity.

I’ll make my comments after each section of code.

<?php
# specify the location of our directory server and
# attempt to establish a connection (note we're not binding yet)

$ds = ldap_connect("ldaps://ldap.example.edu");

You’ve got to know the location of your directory (LDAP) server and the method that your administrator allows you to connect (ldaps or ldap). I’m using the PHP ldap_connect() function to make the connection. The ldap_connect() function can take a URI for your directory server. It returns an LDAP link identifier on success or FALSE on error.

if ( $ds ) {
   # form the LDAP distinguished name (DN) that we'll bind
   # with from the POST variables $uid and $password

   $uid = $_POST['uid'];
   $password = $_POST['password'];
   $userdn = "uid=$uid,cn=accounts,dc=example,dc=edu";

I’m assuming a couple of things in the preceding code: (1) the page that you’re coding is called from another that passes POST variables, and (2) that the POST variables are called ‘uid’ for the userid and ‘password’ for the password. We’ll form the RDN from the ‘uid’ and the base for our account tree in LDAP. (Note: It wouldn’t make any sense to call this page using GET variables — otherwise your password would have been encoded in the URL. You’ll also want to ensure that your POST variables are defined before you process any of this code.)

   # attept to bind to the directory server

   $result = ldap_bind( $ds, $userdn, $password );

Binding to the directory server is easy using the ldap_bind() function. You reference the link identifier from the ldap_connect() function and the userdn and password variables stored from the POST. ldap_bind() returns TRUE on success or FALSE on failure.

   # search the accounts branch for the entry (uid=$uid) and
   # save the results in an array called $accounts_searchResult

   $accounts_searchResult = ldap_search( $ds, "cn=accounts,dc=example,dc=edu", "uid=$uid" );

The ldap_search() function does the hard work of searching the directory and returning a data structure with your results. There are many options you can pass to ldap_search(), but only three are required — the ldap link identifier from your ldap_connect() function, a search base (where to look), and a filter (what to look for).

   # the elements of the searchResult array are ldap entries. Grab the
   # first entry off the array and examine it

   $entry = ldap_first_entry( $ds, $accounts_searchResult );

The ldap_first_entry() function will return the first entry (go figure) from the data structure returned by our previous call to ldap_search(). The first entry should be the only one we care about since we were searching for a specific user. If you were trying to search for all users, say you were developing a page that displayed a telephone directory, then you’d want to investigate into the ldap_next_entry() function, which would — like it says — fetch the next entry from the result of your search. Both functions return an entry identifier on success and FALSE on error.

   while ( $entry ) {
      # let's find the DN for this specific entry

      $dn = ldap_get_dn( $ds, $entry );

      echo "dn: $dn<br>n";

The very first thing this example prints out is the LDAP DN (distinguished name) of the entry. This is useful, as it is the key into your directory for the username that we searched for. The ldap_get_dn() function returns a string containing the DN of the entry or FALSE on error. Now let’s print out the results of the query. I’m going to use nested HTML unordered lists for the display — if you can’t follow along, just remove the <ul> and <li> tags from your code and it will display the same information (only not as fancy).

      # let's examine all of the attributes associated with this entry

      $attrs = ldap_get_attributes( $ds, $entry );

The ldap_get_attributes() function will return an array of all of our attribute names ( the descriptive name assigned to each piece of information in the entry — like uid, displayname, mail, etc). Next we loop through each of the attributes and display their values. The following bit of code is a bit convoluted and will probably only make sense if you think of the entry as a two-dimensional array.

      echo "<ul>";
      for ( $i = 0; $i < $attrs['count']; $i++ ) {
         # the array $attrs will use the attribute name (a string) as the
         # subscript notation. Ex: $attrs[displayname] = 'john doe'

         echo "<li>$attrs[$i]n";

         # some attributes can be multivalued
         # (ie: affliation can be either student, staff, or both student and staff)
         # the value of the attributes is stored in the second dimension of the
         # $attr array: $arrt['attribute_name'][0 .. N] = ( value0, value1, ... valueN )

         echo "<ul>n";

         for ( $j = 0; $j < $attrs[$attrs[$i]]['count']; $j++ ) {
            # print out all of the attributes
            echo "<li>" . $attrs[$attrs[$i]][$j] . "</li>";
         }

         echo "</ul></li>n";

      }

      echo "</ul>";

Phew, we’re done with that. You did follow it all, didn’t you? In a nutshell, PHP can reference arrays using keys instead of just plain old integers (like some languages do). So you can reference $attrs['displayname'] to return an array of the values associated with ‘displayname’. To actually see the values we index into the second dimension of the array — $attrs['displayname'][$index], where $index is a value between 0 and the number of ‘displayname’s.

      # if there are more entries in the search result, show the next one
      $entry = ldap_next_entry( $ds, $entry );
}

We’ve previously discussed the ldap_next_entry() function. What this line of code does is grab a new $entry before the while() loop repeats itself. As previously discussed, ldap_next_entry() will return FALSE on error meaning that we’ll drop out of the while() loop when we reach the last entry.

   ldap_free_result( $accounts_searchResult );
   ldap_unbind( $ds );
}
?>

We close with some housecleaning functions. The ldap_free_result() function will free up the memory we’d allocated to holding the search result. Note that all result memory will be automatically freed when the script terminates. If your script isn’t ending here, calling ldap_free_result() is a good idea as it will keep runtime memory usage low — and your sysadmin happy. The ldap_unbind() function unbinds from the directory server — and keeps your directory server administrator happy too.