PEAR is archived and read-only

This mirror preserves historical PEAR package releases and metadata so existing references remain available.

Home » Networking » Net_LDAP » Manual

Net_LDAP allows you to query and manipulate the data stored in directory servers using PHP in an object-oriented way. Detailed usage examples are available at: /your/pear/path/docs/Net_LDAP/examples/ Please note, that as of 2009-05-28 Net_LDAP2, which is superseding Net_LDAP, has become stable. Please use Net_LDAP2 instead.

Introduction

Introduction – What Net_LDAP is and general information

Welcome to Net_LDAP!

Net_LDAP is a clone of Perls Net::LDAP package. PEAR Net_LDAP for PHP does, besides some own features, provide most of Perl Net::LDAP methods. Net_LDAP allows you to query and manipulate the data stored in directory servers using PHP in an object-oriented way. A directory server is a database server providing a hierarchical database and is usually queried using the LDAP protocol.

Classes of Net_LDAP

The following table gives you a short overview which classes are available in Net_LDAP and what they can be used for. Their relations will be explained too.

Class name Description
Net_LDAP This is the main class. It enables you to connect and bind to a LDAP-server and to run ldap-querys like searching and manipulating entries. Most common, you will run a search using a LDAP-Filter and will get a Net_LDAP_Search-object. You may also fetch an entry directly, which gives you a Net_LDAP_Entry-object.
Net_LDAP_Search Objects of this class are returned from search querys. You can use this object to retrieve informations about a search result, like how much entries you have found for the provided filter. You can retrieve the found entries as Net_LDAP_Entry-objects in various forms: sorted, consecutively starting from the end or beginning or unsorted at once.
Net_LDAP_Entry Objects of this kind are either produced by casting fresh entries manually, by retrieving the result of a LDAP-search or by fetching an entry directly. It gives you the possibility to read and/or manipulate the attributes of an entry which describes the characteristic of the specific object.
Net_LDAP_Util The utility class contains only static methods, so you should not need to make an instance of it. It features some helpful methods, some of them are used internally by Net_LDAP but may be used externally from you as well. The most methods deal with escaping issues, since LDAP has some metacharacters with special meaning so that they usually need to be properly escaped.
Net_LDAP_Filter You are free to give LDAP-Filters on your own to the Net_LDAP->search() method, however this has some drawbacks (including escaping issues). For this reason, you can use the Net_LDAP_Filter class to easily build and combine your filters. LDAP filters are extensively explained at the chapter LDAP filters.
Net_LDAP_Error This is a error class. Most methods of Net_LDAP will return a object of this class if something went wrong. You can use this object to identify errors and to get detailed knowledge on what went wrong. See Errorhandling for more information.
Net_LDAP_LDIF LDIF files are human readable, plain text files containing directory data and/or change commands, much like an SQL file. Unlike SQL files, it is data centric, not action centric. Net_LDAP_LDIF enables you to convert between Net_LDAP_Entry-objects and LDIF files. Please note, that Net_LDAP_LDIF has a little different error handling explained later.

Error handling

Error handling – How handling errors works in Net_LDAP

Error handling

Nearly all of Net_LDAPs methods return a Net_LDAP_Error object if something went wrong. You always should check for errors after you performed an action to be sure that your application doesn't do things you don't want it to do.

Handling errors is an easy task, you just have to test the return value as shown below. If an error occured, you can halt the script for example. In other cases, you may just log the error, but what exactly happens depends on your specific situation, of course.

You can use the getMessage() method of the error object to retrieve the error message explaining the problem and getCode() to get the error code which is usually the LDAP-Error code (see Table below) and may be used for automated reaction on errors.

Dealing with errors

<?php
// Perform an arbitrary action:
$result = $ldap->search($searchbase, $filter, $options);

// Check, if an error occured and do something.
// Here we use die() to show the message of the error.
if (PEAR::isError($result)) {
    die($result->getMessage());
}
?>
Error codes Net_LDAP
Error code Description
0x00LDAP_SUCCESS
0x01LDAP_OPERATIONS_ERROR
0x02LDAP_PROTOCOL_ERROR
0x03LDAP_TIMELIMIT_EXCEEDED
0x04LDAP_SIZELIMIT_EXCEEDED
0x05LDAP_COMPARE_FALSE
0x06LDAP_COMPARE_TRUE
0x07LDAP_AUTH_METHOD_NOT_SUPPORTED
0x08LDAP_STRONG_AUTH_REQUIRED
0x09LDAP_PARTIAL_RESULTS
0x0aLDAP_REFERRAL
0x0bLDAP_ADMINLIMIT_EXCEEDED
0x0cLDAP_UNAVAILABLE_CRITICAL_EXTENSION
0x0dLDAP_CONFIDENTIALITY_REQUIRED
0x0eLDAP_SASL_BIND_INPROGRESS
0x10LDAP_NO_SUCH_ATTRIBUTE
0x11LDAP_UNDEFINED_TYPE
0x12LDAP_INAPPROPRIATE_MATCHING
0x13LDAP_CONSTRAINT_VIOLATION
0x14LDAP_TYPE_OR_VALUE_EXISTS
0x15LDAP_INVALID_SYNTAX
0x20LDAP_NO_SUCH_OBJECT
0x21LDAP_ALIAS_PROBLEM
0x22LDAP_INVALID_DN_SYNTAX
0x23LDAP_IS_LEAF
0x24LDAP_ALIAS_DEREF_PROBLEM
0x30LDAP_INAPPROPRIATE_AUTH
0x31LDAP_INVALID_CREDENTIALS
0x32LDAP_INSUFFICIENT_ACCESS
0x33LDAP_BUSY
0x34LDAP_UNAVAILABLE
0x35LDAP_UNWILLING_TO_PERFORM
0x36LDAP_LOOP_DETECT
0x3CLDAP_SORT_CONTROL_MISSING
0x3DLDAP_INDEX_RANGE_ERROR
0x40LDAP_NAMING_VIOLATION
0x41LDAP_OBJECT_CLASS_VIOLATION
0x42LDAP_NOT_ALLOWED_ON_NONLEAF
0x43LDAP_NOT_ALLOWED_ON_RDN
0x44LDAP_ALREADY_EXISTS
0x45LDAP_NO_OBJECT_CLASS_MODS
0x46LDAP_RESULTS_TOO_LARGE
0x47LDAP_AFFECTS_MULTIPLE_DSAS
0x50LDAP_OTHER
0x51LDAP_SERVER_DOWN
0x52LDAP_LOCAL_ERROR
0x53LDAP_ENCODING_ERROR
0x54LDAP_DECODING_ERROR
0x55LDAP_TIMEOUT
0x56LDAP_AUTH_UNKNOWN
0x57LDAP_FILTER_ERROR
0x58LDAP_USER_CANCELLED
0x59LDAP_PARAM_ERROR
0x5aLDAP_NO_MEMORY
0x5bLDAP_CONNECT_ERROR
0x5cLDAP_NOT_SUPPORTED
0x5dLDAP_CONTROL_NOT_FOUND
0x5eLDAP_NO_RESULTS_RETURNED
0x5fLDAP_MORE_RESULTS_TO_RETURN
0x60LDAP_CLIENT_LOOP
0x61LDAP_REFERRAL_LIMIT_EXCEEDED
1000Unknown Net_LDAP Error

Configuration and connecting

Configuration and connecting – How to configure Net_LDAP and connect to an LDAP server

Connecting to an LDAP server

To connect to an LDAP server, you should use Net_LDAP's static connect() method. It takes one parameter, an array full of configuration options, and either returns a Net_LDAP object if connecting works, or a Net_LDAP_Error object in case of a failure.

The following table lists all configuration options. If the default value for an option fits your needs, you don't need add it to your configuration array.

Possible configuration options
Name Description Default
host LDAP server name to connect to. You can provide several hosts in an array in which case the hosts are tried from left to right. localhost
port Port on the server 389
version LDAP version 3
starttls TLS is started after connecting false
binddn The distinguished name to bind as (username) (none)
bindpw Password for the binddn (none)
basedn LDAP base name (root directory) (none)
options Array of additional ldap options as key-value pairs array()
filter Default search filter (string or preferably Net_LDAP_Filter object). See LDAP filters (objectClass=*)
scope Default search scope, see Search sub

Connecting to an LDAP server

<?php
// Inclusion of the Net_LDAP package:
require_once 'Net/LDAP.php';

// The configuration array:
$config = array (
    'binddn'    => 'cn=admin,ou=users,dc=example,dc=org',
    'bindpw'    => 'password',
    'basedn'    => 'dc=example,dc=org',
    'host'      => 'ldap.example.org'
);

// Connecting using the configuration:
$ldap = Net_LDAP::connect($config);

// Testing for connection error
if (PEAR::isError($ldap)) {
    die('Could not connect to LDAP-server: '.$ldap->getMessage());
}
?>

Fetching entries

Fetching entries – Retrieving entries directly or from a searchresult

Retrieving entries directly

You can retrieve directory entries in several ways, either directly or from a performed search request. If you want to fetch an entry directly, you need to know its absolute distinguished name (DN). To directly fetch an known entry from the directory server, you use Net_LDAP's getEntry() method. It takes two parameters: The DN of the entry and the attributes you want to read from the entry. It returns a Net_LDAP_Entry object if fetching worked, or a Net_LDAP_Error object in case of a failure.

You may also check if the entry exists in the server before you fetch it. This can be achieved by Net_LDAP's dnExists() which takes the DN to test and returns either true or false.

Fetching an entry directly

<?php
// Defining the DN we want to fetch;
// we want to select the given- and the surname
$dn = 'cn=admin,o=example,dc=org';
$entry = $ldap->getEntry($dn, array('gn', 'sn'));

// Error checking is important!
if (Net_LDAP::isError($entry)) {
    die('Could not fetch entry: '.$entry->getMessage());
}
?>

Retrieving entries from a searchresult

The second way to retrieve entries is from a searchresult. As described in chapter "Search", you access the entries of a search result through the Net_LDAP_Search-object resulting from Net_LDAP's search() method. Each of the following methods return a Net_LDAP_Error-object if something goes wrong, so remember to test for errors! You have several ways to read the entries:

Possible ways to fetch entries
Method of Net_LDAP_Search Description
entries() This returns the entries at once unsorted.
as_struct() This returns all entries as multidimensional array instead of Net_LDAP_Entry-objects. The array keys of the first dimension are the DNs and the value is an array containing all attributes. The array keys of the second level are the attributes names; the value of the second level is an array containing all the attributes values. Note, that even if there are no or just one value, an array is present.
sorted() Use this if you want to get the entries at once but sorted. You can sort by several attributes which can contain multiple values. You can of course sort ascending (default) or descending - just pass the PHP constant SORT_ASC or SORT_DESC as second parameter.
sorted_as_struct() Like as_struct(), this returns the entries as multidimensional array, but in this case sorted. For parameters, see sorted()
shiftEntry() This returns one entry from the beginning of the search result. Since this returns FALSE if all entries are fetched, shiftEntry() is perfectly appropriate to get used inside a while-loop. Take care not to mix shiftEntry() and popEntry()!
popEntry() Exactly the same as shiftEntry, but returns the entry from the end of the searchresult. Again, be sure to not mix shiftEntry() and popEntry()!

To directly fetch an known entry from the directory server, you use Net_LDAP's getEntry() method. It takes two parameters: The DN of the entry and the attributes you want to read from the entry. It returns a Net_LDAP_Entry object if fetching worked, or a Net_LDAP_Error object in case of a failure.

You may also check if the entry exists in the server before you fetch it. This can be achieved by Net_LDAP's dnExists() which takes the DN to test and returns either true or false.

Fetching all entries from searchresult

<?php
// return all entries:
$entry = $search->entries();
?>

Fetching all entries from searchresult: sorted

<?php
// return sorted by first 'sn' then 'gn', but descending:
$entry = $search->sorted(array('sn', 'gn'), SORT_DESC);
?>

Fetching entries one by one inside a while loop

<?php
// return entries one by one:
while ( $entry = $search->shiftEntry() ) {
    // do something, like printing the DN of the entry;
    // in a real case, dont forget to test for errors!
    echo "ENTRY: " . $entry->dn();
}
?>

Attributes

Attributes – Reading/adding/changing/deleting attributes from entries

Reading attributes

Reading attribute values depends on the selection of those attributes at search time. You can only access attributes that where selected! You can read attribute values using either Net_LDAP_Entry's getValues() or getValue() method. getValue() will return an array where the keys are the attributes names. If you use getValues() you may pass an option:

Reading attributes

<?php
// read Surename, singlevalued
$surename = $entry->getValue('sn', 'single');

// read mail adress which may be multivalued
$mail = $entry->getValue('mail', 'all');
?>

If you want to read the distinguished name of an Entry (DN), you must use a different method: dn()

Reading an entries DN

<?php
$dn = $entry->dn();
?>

Regular expressions on attributes

PEAR::Net_LDAP has the unique feature to apply a regular expression match directly against attributes, so you do not need to manually fetch all values and run the regex against them. Instead, you can use Net_LDAP_Entry's preg_match() function. The behavior of this function is the same as PHPs preg_match(), but the $matches array is slightly different. It features one dimension more, since it may match for several attribute values if the attribute is multivalued. If you pass $matches, be sure to do it via REFERENCE, because otherwise $matches remains empty. preg_match() returns true or false, depending on match.

Performing preg_match on attribute values

<?php
// Look, if the user has an emailadress for 'example', if so,
// we want to display the tld:
// (be sure to pass $matches as reference!)
$matches = array();
if ( $entry->preg_match('mail', '/example\.(.+)/', &$matches) ) {
    // print every TLD found for 'example':
    foreach ($matches as $match) {
        echo $match[1];
    }
}
?>

General information regarding attribute changing

It is important to know how attribute changing works. Modifications to an entry through the Net_LDAP_Entry-object are local only. After you have made all changes and want to transfer them to the directory server, you must call update() of the Net_LDAP_Entry object. This will return either TRUE or an Net_LDAP_Error. Another good information is, that you must select attributes at search time if you want to add/change/delete attribute values. Otherwise Net_LDAP will most likely fail silently giving you the wrong assumtion that everything was okay - Net_LDAP needs knowledge of the attributes it should work with!

Modification of attributes is also possible through Net_LDAP's modify() method. This method will call the methods described here on the Net_LDAP_Entry object given, and directly calls an update() after that, thus performing the changes directly on the server. The parameter is an complex array describing the changes to be performed. It is considered for more advanced users, because it is more compact, so please refer to the latest API documentation for more information.

Adding attributes

Adding attrbiute values to an entry is an easy task. You just need to call add()! The parameter is an array whose keys are the attribute names and values the attributes values. If only one attribute value should be added, the second level may be a string. If the attribute doesn't exist so far, it will be added, if it exists, the attributes values will be added.

Adding attributes

<?php
// Adding several attributes:
$result = $entry->add(
    array(
        'sn'   => 'Doe',
        'gn'   => array('John'),
        'mail' => array('john@doe.org', 'j.doe@example.org')
    )
);
?>

Changing attributes

Changing values is with the replace() method as easy as adding values. However, you have to be a little more careful. The expected parameter is an array describing the new absolute state of the named attributes. This means, if you specify a NULL value for an attribute, this attribute will get deleted! You may specify single values as string too. The keys of the array are expected to be the attributes names.

Changing attributes

<?php
// Changing several attributes:
// 'sn' is changed to "Smith", 'gn' gets deleted and mail will
// be changed to te two new adresses
$result = $entry->replace(
    array(
        'sn'   => 'Smith',
        'gn'   => null,
        'mail' => array('smith@example.org', 'smith@example.de')
    )
);
?>

Deleting attributes

Using the delete() method you are able to delete specific attributes values as well as delete a whole attribute. You need to specify the attribute names as array keys, the array values are the values you want to delete. If you want to delete whole attributes, specify them as single level array. Special care must be taken not to delete the whole entry which will be the case if the parameter array is omitted or set to NULL! Also, don't mix syntax modes. If you want to delete whole attributes you can't delete specific values from another attribute in the same function call.

Deleting attributes

<?php
// Delete the whole entry:
$result = $entry->delete();

// Delete the whole telephone number attribute:
$result = $entry->delete('telephoneNumber');

// Delete one specific mail attributes value:
$result = $entry->delete( array('mail' => 'j.doe@example.org') );

// Delete mail and telephone attributes as a whole:
$result = $entry->delete( array('mail', 'telephoneNumber') );

// Delete two specific mail adresses:
$result = $entry->delete( array('mail' => array('smith@example.org', 'smith@example.de')) );
?>

Changing Objectclasses

Object classes describe the attribute set of an entry with this objectclass set. The entry stores the objectclass in a special attribute named "objectClass", and of course you may alter that attribute like any other attribute.

However, special care must be taken if changing this attribute since most directory servers impose rules on the other attributes the object class define. For example, it is usually not possible to delete an objectclass if some of the attributes the class describes are still in use by the entry. This should be not much of a problem with optional attributes, but sometimes objectclasses have mandatory attributes set. Also structural objectclasses can only be added when creating new entrys. Because of the internal architecture of Net_LDAP it is currently not possible to resolve those cases.

To add or remove objectclasses with mandatory attributes or new structural object classes, you need to delete the old entry from the directory server and add the new one with the new objectclass and attributes as fresh entry.

Changing complex objectclasses

<?php
// Let's assume that the objectclass myClass enforce the attribute "fooattr"
// Take care that you have all attributes requested, otherwise the new
// entry will not have all attributes set!
$entry->add(array(
    'objectClass'   => 'myClass',
    'fooatrr'       => 'foo',
    'someotherattr' => array('bar', 'baz')
    ));

// Calling $entry->update() now will not succeed under some circumstances!
// We construct a fresh entry object which is in fact a copy of the already
// existing entry with all changes already applied (the local copy).
// It is important, that at fetching time of $entry all attributes where selected!
// Only the selected attributes will get copied.
$changed_entry = Net_LDAP_Entry::createFresh($entry->dn(), $entry->getValues());

// Now delete the old entry and add the new one:
$ldap->delete($entry);
$ldap->add($changed_entry);
?>

Managing entries

Managing entries – Adding/renaming/moving/deleting entries

Adding (fresh or old) entries to the directory

Adding new entries is performed in two ways. First, you need to establish a fresh Net_LDAP_Entry object. After that, you can add that entry like you would add already-existent entries using Net_LDAP's add() method.

Adding a fresh entry

<?php
// Build a new fresh entry:
$dn         = 'cn=new-admin,o=example,dc=org';
$attributes = array(
    'cn'              => 'new-admin',
    'mail'            => array('new-admin@exaple.org', 'n.admin@example.de'),
    'telephoneNumber' => '1234567890'
);
$entry = Net_LDAP_Entry::createFresh($dn, $attributes);

// Add the entry to the directory:
$ldap->add($entry);
?>

Renaming or moving entries

Renaming and/or moving an entry is an operation on the DN of an entry. Moving an entry means, to rename a DN in such a way, that the entry becomes a new base-DN. You can rename or move an entry, if you call the dn() method of the entry you want to relocate. Alternatively, you may call Net_LDAP's move() method that also ca handle only DNs. Remember that you must call the entires update() method to carry out the move/rename. Net_LDAP's move() will move the entry immediately. If you use an entryobject togehter with Net_LDAP's move(), you are able to perform cross directory moves.

Moving an entry using Net_LDAP_Entry

<?php
// Defining the DN we want to fetch;
$dn    = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap->getEntry($dn);

$entry->dn($newdn);
?>

Moving an entry using Net_LDAP and Net_LDAP_Entry

<?php
// Defining the DN we want to fetch;
$dn    = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap->getEntry($dn);

$ldap->move($entry, $newdn);
?>

Renaming an entry using Net_LDAP and DNs

<?php
// Defining the DN we want to fetch;
$dn    = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin2,o=new-example,dc=org';

$ldap->move($dn, $newdn);
?>

Performing a cross directory move

<?php
// $ldap_src is the source ldap and $ldap_tgt the target
$dn    = 'cn=admin,o=example,dc=org';
$newdn = 'cn=admin,o=new-example,dc=org';
$entry = $ldap_src->getEntry($dn);

$ldap_src->move($entry, $newdn, $ldap_tgt);
?>

Deleting entries

Deleting entries is performed using Net_LDAP's delete() method. Just pass the Net_LDAP_Entry object or the DN of the entry you want to delete. In the case that the DN contains subentrys, you need to pass TRUE as second parameter which will make delete() delete recursive.

A second way exist: You may simply call delete() from the Net_LDAP_Entry you want to delete. Don't forget that you must call update() to carry out the delete in this case.

Deleting an entry

<?php
$dn  = 'cn=new-admin,o=example,dc=org';
$ldap->delete($dn);
?>

Deleting an entry using a Net_LDAP_Entry object

<?php
$entry->delete();
$entry->update();
?>

LDAP filters

LDAP filters – Introduction to and usage of LDAP filters

What are LDAP filters?

LDAP filters are defined in RFC 2254 and can be compared to the WHERE clause in SQL select statements - they filter the data returned from some search request - in this case the entries returned from the directory server. With Net_LDAP, you may use plain strings as filters, or preferably, the Net_LDAP_Filter class which mostly releases you of the burden to escape yourself and to remember all the various special characters needed for constructing and combining filters.

Where and how to use filters is described in chapter Search.

Some LDIF filter basics

Although you should preferably use the Net_LDAP_Filter class to construct your LDAP filters, some theory may be interesting and helpful in understanding how to construct LDAP filters and what they are capable of.

Basic LDAP filters are composed of an "[attribute][operator][value]" pair enclosed by round brackets. There are several comparison operators available: "=" (equal), ">" (greater), "<" (less), ">=" (greater or equal), "<=" (less or equal) and "=~" (phonetical similar). The exact match behavior is defined by the attribute syntax of the attribute to which the filter should apply.

Some basic string filters

<?php
// Filter entries whose first name is 'Benedikt':
$filter = '(givenName=Benedikt)';

// Filter entries which have an employeenumber higher or equal than 1424:
$filter = '(employeeNumber>=1424)';

// Filter entries whose first name sounds similar to "Stephane"
// This should also find "Stephen" and "Stefan" (depending on implementation)
$filter = '(givenName=~Stephane)';
?>

The value part of the basic filter construct could also include a special character: "*". The star acts as placeholder for none, one or several characters at that position. "V*lue" would therefore match against "Value", "Vlue", "VaaAaAalue" and so on. There are some special named combinations using the star, but they work exactly the same way:

Special named placeholder combinations
Name Filter Description
present(attr=*)Also refered to as "any". Finds any entry containing any (unless empty) value for the named attribute.
begins(attr=value*)value starts with some fixed string
ends(attr=*value)value ends with some fixed string
contains(attr=*value*)value contains some fixed string

Combining string filters

Basic filters can be combined using the three logical operators "&" (and), "|" (or) and "!" (not). Note, that the smallest filter component, the basic filter enclosed in round brackets, remains isolated: instead of just adding another "[attribute][operator][value]" pair into the brackets, a new bracket level is introduced that contains all filter components that should be combined. Note also, that the logical operator does stand in front of all filter components, not between them as common in programming languages.

Combining string filters

<?php
// Search all 'Benedikt's with phone number 1234567890
    $filter = '(&(givenName=Benedikt)(telephoneNumber=1234567890))';
    
    // Search the same, but exclude person "Benedikt Foobar"
    // Note that the "not" is a logical operator and thus needs its own
    // surrounding bracket. This explains nicely, that each bracket level
    // is evaluated independently from surrounding brackets.
    $filter = '(&(givenName=Benedikt)(telephoneNumber=1234567890)(!(sureName=Foobar)))';
?>

The Net_LDAP_Filter class

As you will read below, there are some special characters inside the LDAP filter definition and thus must be escaped. These are mainly the special characters used directly by the filter syntax like braces and the logical operators. Despite those, there are some other cases which need special threatment. Nearly all of this cases are hidden through the Net_LDAP_Filter class so you should only consider using string filters if you need to or you know what you are doing. If you need a filter string, you may also use Net_LDAP_Filters toString() function after building the filter.

The filter class has two different usage models: one for constructing basic filters and another to combine them logically. This has to do with the syntax of LDAP filters you may read below.

Creating filters

For creating basic filter components, you need to use the create() factory method. There, you combine three items: an attribute to filter for, a matching rule for comparison and a value that is beeing compared with the servers entries. The given value is automatically escaped, so you need take care if you want to use the star placeholder. In this case, you need to pass FALSE as fourth parameter to create() which causes value to be threaten as-is. This of course also means, that you need to escape the parts of the value that may contain restricted characters yourself using Net_LDAP_Util::escape_filter_value(). To learn what characters are restricted, refer to RFC 2254 or the documentation of Net_LDAP_Util::escape_filter_value(); otherwise its safe to always escape.

The matching rules partly follow the basic filter matching rules described above, but are enhanced to make your life easier:

create()s matching rules
Rule Description
equalsOne of attributes values is exactly value. Please note that case sensitiviness depends on the matching rule defined in the attributes schema syntax.
beginsOne of attributes values must begin with value
endsOne of attributes values must end with value
containsOne of attributes values must contain value
present | anyThe attribute can contain any value but must be existent
greaterThe attributes value is greater than value
lessThe attributes value is less than value
greaterOrEqualThe attributes value is greater or equal than value
lessOrEqualThe attributes value is less or equal than value
approxOne of attributes values sounds similar to value. The matching behavior depends on the server implementation.

Creating LDAP filters

<?php
// Filter entries whose first name is 'Benedikt':
$filter = Net_LDAP_Filter::create('givenName', 'equals', 'Benedikt');

// Filter entries whose first name starts with 'Steph':
$filter = Net_LDAP_Filter::create('givenName', 'begins', 'Steph');

// Filter entries containing 'Lone*'; matching the star character.
// The automatic escaping of $value will conveniently escape the star for us.
$filter = Net_LDAP_Filter::create('givenName', 'contains', 'Lone*');

// Filter entries containing 'Foo[something]Bar'; not matching the star character.
// For this to work, we need to disable automatic escaping of $value by passing
// false as fourth parameter. This however implies, that we take care of
// proper escaping, which is showed in the example.
$escaped_values = Net_LDAP_Util::escape_filter_value(array('Foo', 'Bar'));
$foo =& $escaped_values[0];
$bar =& $escaped_values[1];
$filter = Net_LDAP_Filter::create('givenName', 'contains', "$foo*$bar", false);

// Filter entries whose first name sounds similar to "Stephane"
// This should also find "Stephen" and "Stefan" (depending on implementation)
$filter = '(givenName=~Stephane)';
?>

Combining filters

Although the filters can be used stand alone, they can be combined to match sophisticated search requiremets. This is done by using the combine() to combine several present Net_LDAP_Filter objects using a logical operator. The execption is the not operator since it only allows one filter object to be negated.

combine()s operators
Rule Description
andAll filter components must evaluate to true for the combined filter to be true
orAt least one filter component must evaluate to true for the combined filter to be true
notThe result of the filter component is inversed (true becomes false and vice versa). Note that this operator only accepts one filter object.

Combining LDAP filters

<?php
// Create some test filters
$filter_benedikt = Net_LDAP_Filter::create('givenName', 'equals', 'Benedikt');
$filter_steph    = Net_LDAP_Filter::create('givenName', 'begins', 'Steph');
$filter_foobar   = Net_LDAP_Filter::create('sureName', 'equals', 'Foobar');
$filter_height   = Net_LDAP_Filter::create('personHeight', 'greater', '175');

// Negate 'foobar' filter.
// This filters every entry whose sure name is not 'Foobar'
$filter_not_foobar = Net_LDAP_Filter::combine('not', $filter_foobar);

// Build a 'and' combination to be able to search for people whose
// first names start with 'Steph' and who are are taller than 175
// except those whose surname is 'Foobar'
$filter_stephs_tall = Net_LDAP_Filter::combine('and',
    array($filter_steph, $filter_height, $filter_not_foobar));

// In any case, add every person whose first name is
// 'Benedikt' to the search result.
$filter_add_benedikt = Net_LDAP_Filter::combine('or', array($filter_benedikt, $filter_stephs_tall));
?>

Advanced features

You may need some advanced functionality if you have to deal with string representation of filters.

Advanced methods
Method Description
parse()Takes an filter string and parses it into a Net_LDAP_Filter object. It also verifies, that the filter syntax is correct.
printMe()In PERLs interface, this method is called "print" but due to language constraints, we cannot use that name. Prints the string representation of this filter object to standard output or to an optional filehandle passed as parameter.
toString()Returns the string representation of the filter object.

LDIF files

LDIF files – Converting between Net_LDAP_Entries and LDIF files

Avaible since

LDIF support was added to Net_LDAP in release 1.1.0a1.

What are LDIF files?

LDIF files are in detail described at RFC 2849. Shortly, they contain directory data in an plain text, human readable kind, much like a SQL file does. However, unlike SQL files LDIF files are mostly data based, not action based. There are two different LDIF file contents, which can be mixed freely - content and change files. The first and most often used one is the LDIF content file:

Example LDIF content file

#
# This is a content LDIF file.
# It contains one single entry featuring several
# attributes and one comment (this one).
# attr1, attr4 and cn are single valued, the others are
# multivalued. objectclass is a special case, LDAP servers
# will interpret this operational attribute to define the
# classes the object will belong to and thus, which attributes
# it may contain. the OCL-attribute is usually multivalued.
#
version: 1
dn: cn=test1,ou=example,dc=cno
objectclass: someobjectclass
attr1: 12345
attr2: 1234
attr2: baz
attr3: foo
attr3: bar
attr4: brrrzztt
cn: test1

LDIF files could describe not only the data an entry contains, but also various changes to the entry itself. If such an LDIF file would then be given to a LDAP server, he would interpret those changes instead just importing the data. Note in the example below, that even though LDIF content and LDIF change files could be mixed freely, this is not true for individual entries: a specific entry may be either describing content or changes, but not both.

Example LDIF change file

#
# This is a content+change LDIF file.
# It does contain the (shortened) entry from the example above to show
# that LDIF files can contain multiple entry modes.
# The second entry is a change entry. In this case, some
# operations will be done on the entries attributes.
#
version: 1
dn: cn=test1,ou=example,dc=cno
objectclass: someobjectclass
attr1: 12345
cn: test1

# Delete attr1, replace values of attr2 and add new attribute attr42
# The attribute "changetype" is special: it says, what to do with
# this entries dataset. It could also be "delete" or "add" to delete
# a whole entry or to add a completely fresh one. "modrdn" will
# move the entry to a new location once the LDIF file is imported.
dn: cn=test2,ou=example,dc=cno
changetype: modify
delete: attr1
-
replace: attr2
attr2: 123456_newtest
-
add: attr42
attr42: the answer

Error handling when using Net_LDAP_LDIF

Before we can start using Net_LDAP_LDIF we must say some short words about how error handling works. Net_LDAP_LDIF was designed to have mostly the same API as the original PERL Net::LDAP::LDIF has. Because of this, the methods of Net_LDAP_LDIF do not return a Net_LDAP_Error object. You must use the error() method that will return a Net_LDAP_Error object in case of failure or true in case everything was ok. In LDIF reading mode, you can additionally use error_lines() to get knowledge about where in the input file the error occured.

Construction and options

Regardless if you want to read or write a LDIF file, you always have to use the constructor of Net_LDAP_LDIF to initialize your access to the LDIF file. You need to pass at least one parameter to Net_LDAP_LDIF(): the path of the file that should be read or written. You may pass the open mode as second parameter. The possible file open modes are "r" (read), "w" (write, clears the file first) and "a" (append to the end). In case you omit the open mode, read mode is assumed. The third optional parameter is an associative array containing one or several of the following options:

Possible configuration options
Name Description Default
encode Some DN values in LDIF cannot be written verbatim and have to be encoded in some way. Possible values are: "none", "canonical" and "base64" (RFC default) base64
onerror What should be done on errors? "undef" will let error handling in your hands, in this case you use error() and error_lines() to process errors manually. "die" aborts the script printing the error - this is sometimes useful for CLI scripts. "warn" just prints out the error but continues like "undef" would. undef
change Turning this to "1" (true) will tell Net_LDAP_LDIF to write change sets instead of content files. false
lowercase Set this to true to convert attribute names to lowercase when writing. 0
sort If true, sort attribute names when writing entries according to the rule: objectclass first then all other attributes alphabetically sorted by attribute name 0
version Set the LDIF version to write to the resulting LDIF file. According to RFC 2849 currently the only legal value for this option is 1 currently. 1
wrap Number of columns where output line wrapping shall occur. Setting it to 40 or lower inhibits wrapping. Useful for better human readability of the resulting file. 78

For advanced users: instead of passing a file path, you also may pass an already initialized file handle. In this case, the mode parameter will be ignored. You may use this, if you want to mix LDIF content and LDIF change mode by using two Net_LDAP_LDIF instances to write to the same filehandle, but it could be very useful in other cases too. To initialize the second instance of Net_LDAP_LDIF, you can use handle() to get the filehandle from the first instance.

Reading a LDIF file into Net_LDAP_Entry-objects

One of the two modes how Net_LDAP_LDIF can be used is to read a LDIF file and parse its contents into an array of Net_LDAP_Entry objects. This is done using the read_entry()-method which will return the next entry. If you want to fetch all entries, you use the eof() to detect the end of the input file:

Parsing a LDIF file into Net_LDAP_Entry objects

<?php
// open some LDIF file for reading
$ldif = new Net_LDAP_LDIF('somefile.ldif', 'r');
if ($ldif->error()) {
    $error_o = $ldif->error(); // get Net_LDAP_Error object on error
            die('ERROR: '.$error_o->getMessage());
}

// parse the entries of the LDIF file into objects
 do {
    $entry = $ldif->read_entry();
    if ($ldif->error()) {
        // in case of error, print error.
        // here we use the shorthand parameter, so error()
        // returns a string instead of a Net_LDAP_Object
        die('ERROR AT INPUT LINE '.$ldif->error_lines().': '.$ldif->error(true));
    } else {
        // No error: do something with the entry
        // Here we just print the entries DN
        echo 'sucessfully parsed '.$entry->dn();
    }
} while (!$ldif->eof());

// We should call done() once we are finished
$ldif->done();
?>

Writing Net_LDAP_Entry objects to a LDIF content file

Writing an LDIF file is very easy too. Just pass the entries you want to have written to the write_entry()-method. Beware, that if you have opened the file in "w" write mode this will clear any previous data of that file. Use "a" (append) if you just want add data.

Writing entries

<?php
// Assume we have some valid Net_LDAP_Entry objects inside $entries
        // $entries = array( ... );

        // open some file for writing
        $ldif = new Net_LDAP_LDIF('somewritefile.ldif', 'w');
        if ($ldif->error()) die('ERROR: '.$error_o->getMessage());

        // write the data and check for error
        // you could pass one single Net_LDAP_Entry object or
        // several objects inside an array
        $ldif->write_entry($entries);
        if ($ldif->error()) die('WRITE ERROR: '.$error_o->getMessage());
?>

Writing Net_LDAP_Entry objects to a LDIF change file

The process of writing changes is exactly the same like writing entry contents. However there are two differences: Firstly you need to pass the "changes" option and secondly, the entries you want to write need changes. Entries not containing changes will silently be ignored since there is nothing to write.

Writing entry changes

<?php
// cast some test data and three entries
        $testattrs = array(
                'attr1' => '1234',
                'attr2' => 'foo',
                'attr3' => array('bar', 'baz')
            );
        $entries = array(
                Net_LDAP_Entry::createFresh('cn=foo,dc=example,dc=cno',
                    array_merge(array('cn' => 'foo'), $testattrs)),
                Net_LDAP_Entry::createFresh('cn=bar,dc=example,dc=cno',
                    array_merge(array('cn' => 'bar'), $testattrs)),
                Net_LDAP_Entry::createFresh('cn=baz,dc=example,dc=cno',
                    array_merge(array('cn' => 'baz'), $testattrs))
            );

        // make some changes to the first and the last entry
        $entries[0]->add(array('someattr' => 'added'));
        $entries[0]->replace(array('attr1' => 'replaced'));
        $entries[2]->delete(array('attr2'));
        $entries[2]->delete(array('attr3' => 'bar'));

        // open some file for writing, but in change mode
        $ldif = new Net_LDAP_LDIF('somewritefile.ldif', 'w', array('change' => true));
        if ($ldif->error()) die('ERROR: '.$error_o->getMessage());

        // write the data and check for error
        // you could pass one single Net_LDAP_Entry object or
        // several objects inside an array
        $ldif->write_entry($entries);
        if ($ldif->error()) die('WRITE ERROR: '.$error_o->getMessage());

        // Now, only two entries are contained in the LDIF file,
        // cn=foo,dc=example,dc=cno and cn=baz,dc=example,dc=cno.
        // cn=bar,dc=example,dc=cno had no changes and was skipped.
?>

Resulting LDIF change file

version: 1
dn: cn=foo,dc=example,dc=cno
changetype: modify
add: someattr
someattr: added
-
replace: attr1
attr1: replaced
-

dn: cn=baz,dc=example,dc=cno
changetype: modify
delete: attr2
-
delete: attr3
attr3: bar
-

Fetching LDIF data

Sometimes you are interested in the lines inside the LDIF file. For those cases you can use the current_lines() and next_lines() methods. They work in the current context, which may be confusing: current_lines() will always return the lines that have built up the current Net_LDAP_Entry object when called current_entry() after read_entry() has been called. next_lines() will always return the lines, that will build up the next entry from the current point of view, meaning "relative to the entry that was just been read". However, you can override this by activating the "force" parameter of next_lines() which allows you to loop over all entries. current_entry() behaves exactly like current_lines().

If you think, that the lines you have read would be better in form of an Net_LDAP_Entry object, use the parseLines() method to parse those lines into an entry. This is a good way if you need just a few specific entries of a large LDIF file.

Reading LDIF lines

<?php
// open some LDIF file for reading
// (error checking code is ommitted in this example for
//  better readability - in production, test for errors!)
$ldif = new Net_LDAP_LDIF('somefile.ldif', 'r');

// since nothing has been read until now, this will
// return an empty array
$empty_array = $ldif->current_lines();

// so let's read the first entries data
$first_entry_lines = $ldif->next_lines();

// if we call it again, we will not read ahead to the
// second entry - we again read the first one!
$first_entry_lines_again = $ldif->next_lines();

// If we call current_lines() now, we haven't read ahead
// like we learned from the last statement.
$empty_array_again = $ldif->current_lines();

// If we want to shift, we must use
// the read_entry() method, which will read ahead.
$first_entry = $ldif->read_entry();

// Now, current_lines() returns the lines of the
// first entry and next_lines() the lines of the second:
$first_entry_lines  = $ldif->current_lines();
$second_entry_lines = $ldif->next_lines();

// There is another way to shift the lines which is faster if
// you are just interested in the LDIFs content - you
// need to pass the "force" parameter to next_lines():
$third_entry_lines  = $ldif->next_lines(true);
$fourth_entry_lines = $ldif->next_lines(true);
$fifth_entry_lines  = $ldif->next_lines(true);

// If you want to convert the lines to an Net_LDAP_Entry,
// you may do so anytime by using parseLines()
$fourth_entry = $ldif->parseLines($fourth_entry_lines);

// Since we shifted manually only the lines,
// current_lines() will return the lines that built up the
// last (e.g. the first entry) Net_LDAP_Entry object:
$first_entry_lines  = $ldif->current_lines();

// If we decide to read the next entry, we can do that:
$sixth_entry = $ldif->read_entry();

// current_lines() is shifted now:
$sixth_entry_lines = $ldif->current_lines();
?>