Essentials of LDAP with PHP

Share this article

Ever wanted a simple way to store address book style information and network information actually next to any kind of ordered information?

If so, then there’s a technology which has been around since 1993, one which despite not having the cool factor of such technologies as Node.js and Go, allows you to do exactly this. It’s called LDAP!

What is LDAP?

LDAP is short for Lightweight Directory Access Protocol and was developed at the University of Michigan around 1993, by Tim Howes, Steve Kille, Colin Robbins, and Wengyik Yeong.

In short, LDAP is an internet-ready version of an earlier protocol called X.500, which was developed back in the 80’s by the International Telecommunications Union (ITU) for managing telephone directories and directory services.

Whilst LDAP technically refers to the protocol, the name is often applied to the client and server as well. If it helps, you can think of it like SQL is for database servers; it’s the language used to interact with LDAP-enabled servers.

There are a number of LDAP-enabled servers around, the most common of which is Microsoft’s ActiveDirectory; which has been pervasive throughout their product lineup since the release of Windows 2000.

There’s an open source choice as well, one which we’ll be using throughout this article series, called OpenLDAP. It makes no assumptions as to the schema you’re using or the information you’re storing.

Here in part one of the series, I’m going to:

  1. Take you through the basics of setting up an OpenLDAP
  2. Show you how to load up a set of records
  3. Show you how to connect to it and perform some basic operations

Terminology

Before we do that, we need to look at a bit of the terminology. Continuing with the SQL analogy, there are a couple of terms which you’ll need to know, and which I’ll be using throughout the series, which you can find in the table below.

LDAP Term Description
dn A dn, or Distinguished Name, is a record’s unique identifier. This is much like a primary key in a relational database.
Directory Schema (or just Schema) In an LDAP directory, the entry values are governed by a directory schema. A directory schema is a set of definitions and constraints concerning the structure of the directory information.
entry An entry is much like a record in a database, and contains attributes which store the data for the entry.
attribute An attribute is much like an element in an associative array or column in a database. It specifies the type of information which can be stored for that attribute, along with other criteria, such as sorting and searching rules, case-sensitivity and so on.
cn cn is short for common name. An example would be “John Smith”
sn sn is short for surname.

The terminology is quite sophisticated, so I’m unable to cover it all here. But hopefully these basics are enough to get you started.

For more detailed information, check out this guide from O’Reilly, or the LDAP entry on Wikipedia.

Installing An LDAP Server

I’ve never found installing and configuring OpenLDAP particularly straight-forward and a lot of the information available on the net can be misleading or be for a mixture of versions of the server. Here is my best, most concise set of steps, based on using a Debian-based server.

Firstly, install the core server and utils by running the following commands:

sudo apt-get install slapd ldap-utils

These commands will ask you a set of questions, install the server, along with setting it to start at boot time. After it’s done, run the following command, which will help us better configure the installation:

dpkg-reconfigure slapd

This will ask a series of questions, and here’s a guide to answering them:

  • Omit OpenLDAP server configuration? No
  • DNS domain name: homestead.localdomain
  • Name of your organization: … Whatever & Co
  • Admin Password:
  • Confirm Password:
  • OK
  • BDB
  • Do you want your database to be removed when slapd is purged? No
  • Move old database? Yes
  • Allow LDAPv2 Protocol? No

Verify the Installation

With that done, let’s quickly verify that everything’s working, by running the following command:

ldapsearch -x -b dc=homestead,dc=localdomain

You shouldn’t receive an error, but if so, make sure that OpenLDAP’s running; you can do this by running the following command:

sudo netstat -tlnp | grep slapd

You should see output such as the following (formatted for readability):

tcp    0   0 0.0.0.0:389  0.0.0.0:*    LISTEN    6556/slapd      
tcp6   0   0 :::389       :::*         LISTEN    6556/slapd

Populating The Database

Now that the server is set up, we need to load it up with data. Create a new file called users.ldif and in it, add the following records:

dn: cn=Sheldon Cooper,ou=People,dc=homestead,dc=localdomain
cn: Sheldon Cooper
objectClass: person
objectClass: inetOrgPerson
sn: Cooper

dn: cn=Leonard Hofstadter,ou=People,dc=homestead,dc=localdomain
cn: Leonard Hofstadter
objectClass: person
objectClass: inetOrgPerson
sn: Hofstadter

dn: cn=Howard Wolowitz,ou=People,dc=homestead,dc=localdomain
cn: Howard Wolowitz
objectClass: person
objectClass: inetOrgPerson
sn: Wolowitz

dn: cn=Rajesh Koothrappali,ou=People,dc=homestead,dc=localdomain
cn: Rasjesh Koothrappali
objectClass: person
objectClass: inetOrgPerson
sn: Koothrappali

With the file saved, run the following command to load the information into the database:

ldapadd -x -W -D "cn=admin,dc=homestead,dc=localdomain" -f users.ldif

This will prompt you for the password you set earlier. Enter it and you should see output like the following:

adding new entry "cn=Sheldon Cooper,ou=People,dc=homestead,dc=localdomain"

adding new entry "cn=Leonard Hofstadter,ou=People,dc=homestead,dc=localdomain"

adding new entry "cn=Howard Wolowitz,ou=People,dc=homestead,dc=localdomain"

adding new entry "cn=Rajesh Koothrappali,ou=People,dc=homestead,dc=localdomain"

Verify the Records are Present

Now let’s do a quick check that the records are available. From the command line, run:

ldapsearch -x -b "dc=homestead,dc=localdomain" -s sub "objectclass=*"

This should display output similar to the following, which I’ve truncated for the purposes of readability:

# extended LDIF
#
# LDAPv3
# base <dc=homestead,dc=localdomain> with scope subtree
# filter: objectclass=*
# requesting: ALL
#

# homestead.localdomain
dn: dc=homestead,dc=localdomain
objectClass: top
objectClass: dcObject
objectClass: organization
o: homestead
dc: homestead

Interacting with PHP

With all these steps completed, we’re now ready to use PHP to query the server. To keep things simple, we’re using the Zend-Ldap component from Zend Framework 2.

There are a number of PHP LDAP libraries available, but this was the one that I found most effective and straight-forward to use. Granted, I am a bit of a Zend Framework evangelist, but this honestly is the simplest library I’ve found.

To handle the dependency, as in almost all PHP projects these days, we’ll use Composer to make it available. In your project directory, create a composer.json file, as below.

{
    "require": {
        "php": ">=5.3.0",
        "zendframework/zend-ldap": "2.3.*@dev"
    }
}

After that, run composer install to create the vendor directory and bring in the dependency.

Connecting to the LDAP server

With that done, create a new file called index.php in the root of your project directory; in there add the code below:

<?php
require 'vendor/autoload.php';

$baseDn = 'dc=homestead,dc=localdomain';
$options = array(
    'host' => '192.168.10.10',
    'password' => 'homestead',
    'bindRequiresDn' => true,
    'baseDn' => 'dc=homestead,dc=localdomain',
    'username' => "cn=admin,$baseDn"
);
$ldap = new Zend\Ldap\Ldap($options);
$ldap->bind();

This will first make the Zend\Ldap available via the composer generated autoload file. I’ve next defined a variable, $baseDn, to keep the code that much more readable here in the article.

Next I’ve created an array called $options, which stores the configuration settings we’ll use for initializing the Zend\Ldap\Ldap object.

In it I’ve specified the hostname, password, basedn and username. We could have skipped the username and password, but as we’ll be performing authenticated operations, it’s simplest to add it all now.

With the new Zend\Ldap\Ldap object initialized, I then called the bind method to make the connection to the server.

Searching The Database

Now let’s perform the first and simplest operation on the LDAP server: searching. The code below will search for every record in ou=People,dc=homestead,dc=localdomain, which will return the four we loaded earlier.

The first argument, (objectclass=*) specifies an SQL-like filter. This equates to running a SELECT *. The final argument performs a search which will look for all records.

There are a number of different search types, which we’ll cover in the next article. For now, this type is sufficient for our needs.

$result = $ldap->search(
   '(objectclass=*)',
   "ou=People,$baseDn",
   Zend\Ldap\Ldap::SEARCH_SCOPE_SUB
);

Next, we’ll iterate over the records in two ways. Firstly, we’ll call the toArray() method on the $result object, which we pass to json_encode. I’ve done this as a simple way of displaying all the information available.

print json_encode($result->toArray());

Alternatively, we could iterate over the dataset using a foreach as below. In this example, we’ve displayed the dn and cn elements of each record.

foreach ($result as $item) {
    echo $item["dn"] . ': ' . $item['cn'][0] . '<br />';
}

Updating an Entry

Now that we’ve looked at basic record searching and iteration, let’s look at updating a record. This requires three steps:

  1. Fetching the record
  2. Updating an existing property or setting a new one
  3. Persisting the record back to the LDAP server
$hm = $ldap->getEntry(
    "cn=Rajesh Koothrappali,ou=People,$baseDn"
);
Zend\Ldap\Attribute::setAttribute(
    $hm, 'mail', 'koothrappalir@homestead.localdomain'
);
$ldap->update(
    "cn=Rajesh Koothrappali,ou=People,$baseDn", $hm
);

In the code above, we’ve retrieved an entry by calling the getEntry method, passing in the record dn. We next called the setAttribute() method, specifying the record object, the property we want to set, then the value of the property.

Finally, we called the update() method, passion in the record dn and the record object. All being well, the record will be updated.

Deleting an Entry

Now that we can search and update records, let’s finish up by stepping through deleting a record. To do this, we call the delete method, passing in the dn of the record we want to delete.

As the operation could fail, in this example I’ve wrapped the call in a try/catch block, which catches an LdapException if thrown and prints out the reason for the failure.

There could be a variety of reasons for the exception to be thrown, such as the record not existing and the user we’ve authenticated as not having sufficient permission.

try {
    $ldap->delete("cn=Hans Meier,ou=People,$baseDn");
} catch (\Zend\Ldap\Exception\LdapException $e) {
    print $e->getMessage();
}

Wrapping Up

And that’s how to set up and interact with an LDAP server – specifically OpenLDAP – in PHP. I hope you’ve enjoyed this quick run through of how to do it. In the next article, we’ll be exploring LDAP in further depth by:

  • Performing more complicated searches
  • Inserting records
  • Moving records
  • Making secure connections

If you’d like more information on what we’ve covered today, there’s a host of links in the further reading section which should satisfy your curiosity.

Further Reading

Frequently Asked Questions on LDAP with PHP

What is the basic structure of an LDAP directory?

An LDAP (Lightweight Directory Access Protocol) directory follows a hierarchical structure, similar to a tree. The top level is known as the root or base, which branches out into various entries. Each entry consists of a set of attributes, with each attribute having a type and one or more values. The types are usually strings like “cn” for common name, “dc” for domain component, and “ou” for organizational unit. The values hold the actual data. For example, an entry representing a person might include attributes for the person’s first name, last name, and email address.

How do I connect to an LDAP server using PHP?

PHP provides a set of LDAP functions to connect and interact with an LDAP server. The first step is to establish a connection using the ldap_connect() function, which returns a link identifier on success or FALSE on error. Once the connection is established, you can bind to the LDAP directory using the ldap_bind() function. This function authenticates the user to the LDAP server.

How can I search for entries in an LDAP directory using PHP?

PHP’s ldap_search() function allows you to search for entries in an LDAP directory. This function requires the link identifier returned by ldap_connect(), the base DN (Distinguished Name) from where the search should start, and the filter as parameters. The filter is a string that specifies the criteria for selecting entries. The function returns a search result identifier that can be used with other LDAP functions like ldap_get_entries() to fetch the entries.

How do I add a new entry to an LDAP directory using PHP?

To add a new entry to an LDAP directory, you can use PHP’s ldap_add() function. This function requires the link identifier, the DN of the entry, and an array of attributes as parameters. The attributes array should be an associative array where the keys are attribute types and the values are arrays of attribute values.

How can I modify an existing entry in an LDAP directory using PHP?

PHP provides several functions to modify an existing entry in an LDAP directory. The ldap_modify() function can be used to replace the values of an attribute, ldap_mod_add() to add new values to an attribute, and ldap_mod_del() to remove values from an attribute. Each of these functions requires the link identifier, the DN of the entry, and an array of attributes as parameters.

How do I delete an entry from an LDAP directory using PHP?

To delete an entry from an LDAP directory, you can use PHP’s ldap_delete() function. This function requires the link identifier and the DN of the entry as parameters.

How can I handle errors when working with LDAP in PHP?

PHP provides the ldap_error() and ldap_errno() functions to handle errors when working with LDAP. The ldap_error() function returns a string error message, and ldap_errno() returns the LDAP error number.

How do I close a connection to an LDAP server using PHP?

To close a connection to an LDAP server, you can use PHP’s ldap_unbind() or ldap_close() function. Both functions do the same thing and require the link identifier as a parameter.

How can I retrieve the DN of an entry in an LDAP directory using PHP?

PHP’s ldap_get_dn() function allows you to retrieve the DN of an entry. This function requires the link identifier and a result entry identifier as parameters.

How can I retrieve the attributes of an entry in an LDAP directory using PHP?

To retrieve the attributes of an entry, you can use PHP’s ldap_get_attributes() function. This function requires the link identifier and a result entry identifier as parameters. It returns an associative array where the keys are attribute types and the values are arrays of attribute values.

Matthew SetterMatthew Setter
View Author

Matthew Setter is a software developer, specialising in reliable, tested, and secure PHP code. He’s also the author of Mezzio Essentials (https://mezzioessentials.com) a comprehensive introduction to developing applications with PHP's Mezzio Framework.

BrunoScontactsdatabasedirectoryldapOOPHPPHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week