# Net_LDAP2 Manual

Welcome to the Net_LDAP2 user manual! here you have a quick introduction on how to use Net_LDAP2 to acces your directory server with php.

Note that this manual is only a brief introduction and also may be outdated. The official manual is available at the PEAR project website. The reason this manual remains here is, that it may be useful in cases you don't have internet acces at the moment.

## First step: Connect

To do this, use the Net_LDAP2::connect function like this:

    require_once('Net_LDAP22/LDAP2.php');

    $config = array (
                'binddn'   => 'uid=tarjei,dc=php,dc=net',
                'bindpw' => 'secret',
                'basedn'   => dc=php,dc=net
              );

    $ldap = Net_LDAP2::connect($config);

But what are valid values in the config array?

Here's a quick table: (defaults)
- binddn = the DN to bind as. (none)
- bindpw = Password of the user specified by 'binddn' (none)
- host = the ldap host to connect to (localhost)
- base = ldap base, this is usually the Entry point of your directory (none)
- port = the server port (389)
- starttls = when set, ldap_start_tls() is run after connecting. (false)
- version = ldap version (defaults to v 3)
- filter = default search filter (objectclass=*)
- scope = default search scope (sub)

We'll get back to these later.

## Errorhandling

Now you should have the base ldapobject stored in the variable "$ldap". But, what if it is an error? Net_LDAP2 returns a Net_LDAP2_error object (basicly a pear_error object) when an error occurs. So wherever you need to check an error, do like this:

    $ldap = Net_LDAP2::connect($config); // copied from above!

    if (Net_LDAP2::isError($ldap)) {
       print $ldap->getMessage(); // this will tell you what went wrong!
    }

Two things to note:
1) The function is_a() might be faster:

    if (is_a($ldap,'net_ldap_error')) {
    // do the same as above
    }

In PHP5 you must use the instanceof operator instead of is_a().
2) Net_LDAP2_Error can also return an errornumber. These numbers are standardized. A good description of what they mean is found there: http://www.directory-info.com/LDAP2/LDAPErrorCodes.html

## Searching (basics)

Most of the work you do on an ldapserver is in searching, for example, you search for your boss's password or his wife's phonenumber.
Searching an ldapserver is a bit like doing SQL and a lot not like it at all. Think of the directory as some sort of "telephone book". Basically, searches are performed by applying a "filter" to objects under a specific "base" in the directory. Additionally, there is a "scope" applied to the search, so you can specify the recursion level in the directory tree.

### Base:

The "base" is the point under the directory where you want to search under. To search for all people under php.net, you may use: "ou=People,dc=php,dc=net". But if you want just to search the devs, you can use "ou=dev,ou=People,dc=php,dc=net".

### Filter:

Filters define what you are looking for. They "filter out" unwanted entries.
Filters start with a ( and end with a ). There is a lot to be said about filters, most is better said by examples:
(&(objectclass=posixAccount)(uid=boss)) : The object has to satisfy both filters. I.e. an object that is both boss and an posixAccount. If you had another object with uid=boss but that wasn't an postixaccount it would be excluded.
(|(uid=boss)(uid=secretary)) : Either the boss or the secretary. Note that both logical operators are placed before the filters not between the two conditions as you might used to from sql.
(&(objectclass=posixAccount)(|(uid=boss)(uid=secretary))) : Here they must have the posixAccount objectclass as well. (objectclass=*) : All objects must have an objectclass, so this is the simplest way of saying everything. (uid=t*) : With the right indexes on the server, you may search the substring of an attriute. Here; all users with the first name beginning with a "T".
Please note, that Net_LDAP2 provides a filter class for simplier generation and combination of filters. You should use that class unless you know how filters work. This will save you a lot of trouble, since there are some encoding issues with ldap-filters. If you want to provide the filter yourself, you should also have a look to RFC #1558 defining LDAP-Filters.

### Searchscope

The scope of an search may be three things:

- 'base' = Just the entry in question.
- 'sub' = All subentries.
- 'one' = All entries just below the searchbase.

Searching with scope 'base' may be handy for getting just one entry. But then again, that's what the getEntry function does.

### Searching some entries

We know now, how to search, so we will test out our new knowledge. We want to search all person entries whose second name starts with "Ha", but only developers. Later we want to know the name and the telephone number of the persons.

    $filter = '(&(objectclass=person)(sn=Ha*))';
    $searchbase = 'ou=dev,ou=People,dc=php,dc=net';
    $options = array(
                   'scope' => 'sub',        // all entries below the searchbase (recursive all subtrees from there)
                   'attributes' => array('sn','gn','telephonenumber')  // what attributes to select
               );
    $search = $ldap->search($searchbase, $filter, $options);

$search should now be an Net_LDAP2_Search object.
Okay, now we assume that everything was fine (in production, test for error!). We have several options now. We can fetch the found entries at once sorted ($search->sorted()) or unsorted ($search->entries()), or we can read the objects one by one inside a loop using $search->shiftEntry(). See the class documentation of Net_LDAP2_Search for more details.

## Entries

This describes how to get an entry and modifying it. If we just want one single entry, it may be useful to directly fetch that entry instead of searching it manually. To do this you can use Net_LDAP2s "getEntry()" method:

    $dn = 'cn=Foo Bar,ou=dev,ou=People,dc=php,dc=net';
    $entry = $ldap->getEntry($dn, array('sn','gn','telephonenumber'));

With this entry object you now can perform some actions like fetching the contents of attributes:

    $telephonenumber =    $entry->getValue('telephonenumber','single');

Or you can modify a attribute:

    $entry->replace("telephonenumber" => "0123456789");   // replace the attributes values with the new number
    $entry->update();  // update temporarily modified entry on the server

Of course there are much more other possibilitys. Please note that adding and deleting whole entrys is performed through the Net_LDAP2 class and not with the Net_LDAP2_Entry class.

## Schemas

You may also use Net_LDAP2 to find out what schemas your ldap-server supports. Here's an example of how:

    $schema = $ldap->schema();

Now you got a schemaobject. To read from this schemaobject, you have several methods defined in the class Net_LDAP2_Schema.
For example, to find out which attributes are required for inetOrgPerson, you do this:

    $required = $schema->must( 'inetOrgUser' );

    print_r($required);
    /* The output of this will be:
    Array
    (
         [0] => sn
         [1] => cn
    )
    */

Ok, but what kind of attribute is sn? Let's check:

    $att = $schema->get('attribute','sn');

    print_r($att);
    /* The output of this will be:
    Array
    (
         [aliases] => Array
                 (
                  [0] => surname
                 )

         [oid] => 2.5.4.4
         [name] => sn
         [desc] => RFC2256: last (family) name(s) for which the entity is known by
         [sup] => Array
         (
            [0] => name
         )
         [type] => attribute
    )
    */

Hmm, ok, the sup part is important. It means that surname derives it's syntax from another attribute, the name attribute. So , we need to check that as well.
We do:

    $att_dep = $schema->get('attribute',$att['sup'][0]);

    print_r($att_dep);
    /* The output of this will be:
    Array
    (
        [aliases] => Array
            (
            )

        [oid] => 2.5.4.41
        [name] => name
        [desc] => RFC2256: common supertype of name attributes
        [equality] => caseIgnoreMatch
        [substr] => caseIgnoreSubstringsMatch
        [syntax] => 1.3.6.1.4.1.1466.115.121.1.15{32768}
        [max_length] => 32768
        [type] => attribute
    )
    */

From this we find out that the attribute has a maxlength of 32768 characters and has the syntax 1.3.6.1.4.1.1466.115.121.1.15{32768}.