PHPackages                             codecrafting-io/adoldap - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. codecrafting-io/adoldap

ActiveLibrary[HTTP &amp; Networking](/categories/http)

codecrafting-io/adoldap
=======================

A PHP seamless way to search on the Active Directory with ADO and LDAP

v0.2.0(6y ago)1111MITPHPCI failing

Since Dec 5Pushed 6y ago1 watchersCompare

[ Source](https://github.com/codecrafting-io/adoldap)[ Packagist](https://packagist.org/packages/codecrafting-io/adoldap)[ RSS](/packages/codecrafting-io-adoldap/feed)WikiDiscussions master Synced today

READMEChangelog (8)Dependencies (4)Versions (9)Used By (0)

AdoLDAP
=======

[](#adoldap)

[![Latest Stable Version](https://camo.githubusercontent.com/9cd491cb91cd193d1a2be64b834caed4e74ed3479e470f561d4cc75ddac4ca07/68747470733a2f2f706f7365722e707567782e6f72672f636f64656372616674696e672d696f2f61646f6c6461702f762f737461626c65)](https://packagist.org/packages/codecrafting-io/adoldap)[![License](https://camo.githubusercontent.com/ba2c131bca9da1b7ef288662876e5f129dda737b8b9f4b644dca1c37eb5cbc84/68747470733a2f2f706f7365722e707567782e6f72672f636f64656372616674696e672d696f2f61646f6c6461702f6c6963656e7365)](https://packagist.org/packages/codecrafting-io/adoldap)[![Downloads](https://camo.githubusercontent.com/96363c70a865addac08cc33968c552172445891a1c9eb6fc9e1c171292ec5f67/68747470733a2f2f706f7365722e707567782e6f72672f636f64656372616674696e672d696f2f61646f6c6461702f646f776e6c6f616473)](https://packagist.org/packages/codecrafting-net/adoldap)

Summary
-------

[](#summary)

- [How it works](#how-it-works)
- [Requirements](#requirements)
- [Configuration](#configuration)
    - [Domain Information](#domain-information)
    - [Connection Configuration](#connection-configuration)
- [Searching](#searching)
    - [Query Builder](#query-builder)
    - [Search Dialects](#search-dialects)
- [Handling Data](#handling-data)
    - [Models &amp; Column Map Attributes](#models--column-map-attributes)
    - [Special Attributes](#special-attributes)
    - [Paging Data](#paging-data)
    - [After Fetch Callback](#after-fetch-callback)
- [Comming Soon](#comming-soon)

> ⚠️ **WARNING:** ⚠️ This library still on alfa, so testing is on the way and newer versions may break backwards compability.

The AdoLDAP is a small PHP library to seamless search and authenticate on Active Directory with ADO and LDAP. In short terms it provides the following benefits:

- ⭐ Seamless way to connect to Active Directory, not requiring a single configurtion, not even a server if you wanted.
- ⭐ A nice semantic syntax that is easy/reusable/fun to use. You can use pre built searchs to find Users, Computers and Groups, handling them as objects, with a easy human readable get/set syntax.
- ⭐ A fluent QueryBuilder having support for both [LDAP](https://docs.microsoft.com/en-us/windows/win32/adsi/ldap-dialect) and [SQL](https://docs.microsoft.com/en-us/windows/win32/adsi/sql-dialect) dialects.
- ⭐ Tools to discover information about you current env, regarding to the available Domain Controllers, connected DCs, main DCs, domain name etc.
- ⭐ Native PHP data type handling. No strangeous and obscure `VARIANT` objects for returned values, with a nice Iterator interface to loop through objects.

How it works
------------

[](#how-it-works)

[TOP](#adoldap)

The **main feature** that AdoLDAP provides is a **seamless way to authenticate on AD** with LDAP using the current security context of the thread in execution. This is a feature implemented by the [**ADODB Active Directory Interface**](https://docs.microsoft.com/en-us/windows/win32/adsi/searching-with-activex-data-objects-ado), which can be used through **COM objects** whithin PHP language (or any COM aware language). Usually this means if the current user are logged on a domain, and this user have permission to search on AD (which likely will), the ADODB don't require especific cridentials to connect.

In another words, now you can create web applications that can search through LDAP without the need of a especific read/write user to connect. For example, you can use [**Windows Authentication**](https://docs.microsoft.com/en-us/iis/configuration/system.webserver/security/authentication/windowsauthentication/) setup and take advantage of a seamless search information about the current authenticated user. You also can test on your local machine even not having Windows Authentication.

Requirements
------------

[](#requirements)

[TOP](#adoldap)

- Windows environtment.
- PHP &gt;= 7.1 64bits
- COM PHP extension enabled
- [Composer](https://getcomposer.org/)

The AdoLDAP only works on Windows, because COM extensions is the way to use ADODB, since ADO is a Windows only. To install AdoLDAP use the following command:

```
composer require codecrafting-io/adoldap
```

Configuration
-------------

[](#configuration)

[TOP](#adoldap)

### Domain Information

[](#domain-information)

[TOP](#configuration)

Like was said you can search providing no information to connect. This is due to the feature of ADODB that allows connections provding zero information about, credentials or host to connect. If you provide no information about the host or the baseDn to be used to connect, the library will likely connect to the user `logonDomainController`, which is the domain controller host that the user was last connected.

To understant what domain informations are available, use the following code:

```
try {
    $ad = new AdoLDAP();
    dump($ad->info());
} catch(\Exception $e) {
    dump($e);
}
```

The table below provides the information for each attribute returned:

AttributeTypeDescriptiondomainstringThe domain cnamedomainNamestringThe domain prefix for entries like a user, ex: DOMAIN\_NAME\\userdefaultNamingContextstringThe default naming context, e.g. the default BASE DN.logonDomainControllerstringThe last DC that the current user was loggedmachineDomainControllerstringThe last DC that the current machine was connectedprimaryDomainControllersarrayThe primary DCs for the domaindomainControllersarrayAll available DCs### Connection configuration

[](#connection-configuration)

[TOP](#configuration)

Even not beign necessary to provide a single configuration to connect, is recommended to do it due to consistency of the search results and specially for performance reasons. Is **recommeded** to provide a BASE\_DN (you can use the `defaultNamingContext`) and for servers is preferreable to use the `primaryDomainControllers`, to a faster connection. So after you inspect the values returned by the code above, use them to connect like in the code below:

```
try {
    //Minimal recommended configuration
    $config = [
        'host' => 'server01.mydomain.com', //use a primaryDomainController
        'baseDn' => 'DC=MYDOMAIN,DC=COM',
    ];
    $ad = new AdoLDAP($config); //auto connected
} catch(\AdoLDAPException $e) {
    dump($e);
}
```

The table below provides the information for each configuration:

ConfigTypeDefaultDescriptionhoststringnullThe server host address to connectportint`DialectInterface::PORT`The port to use for connecting to your host.dialectstring`LDAPDialect::class`The dialect class to use for the ADO LDAP search and bind. You can also use `SQLDialect::class`baseDnstring`DialectInterface::ROOT_DN`The base distinguished name of your domain. Use ROOT\_DN to discover the defaultNamingContext.usernamestringnullThe username to connect to your hosts with.passwordstringnullThe password that is utilized with the above user.sslboolfalseWhether or not to use SSL when connecting to your host. If true overrides the port configurationautoConnectbooltrueWhether or not to automaticly connect with the LDAP Provider.timeoutint30Timeout of connection execution in secondspageSizeint1000Maximum number of objects to return in a result set page, see checkConnectionboolfalseWhether or not to check connection execution on bindparserstring`Parser::class`The data parser of a result set.Searching
---------

[](#searching)

[TOP](#adoldap)

There are two ways to search, using RAW queries or building through the `QueryBuilder`.

Searching using RAW queries:

```
try {
    $config = [
        'host' => 'server01.mydomain.com', //use a primaryDomainController
        'baseDn' => 'DC=MYDOMAIN,DC=COM',
    ];
    $ad = new AdoLDAP($config);
    $ad->search()->query(";(&(objectCategory=user)(sAMAccountName=jdoe));sAMAccountName,name");
} catch(\AdoLDAPException $e) {
    dump($e);
}
```

Searching using `QueryBuilder`:

```
try {
    $config = [
        'host' => 'server01.mydomain.com', //use a primaryDomainController
        'baseDn' => 'DC=MYDOMAIN,DC=COM',
    ];
    $ad = new AdoLDAP($config);
    $ad->search()->whereEquals('objectCategory', 'user')->findBy('sAMAccountName', 'jdoe');
} catch(\AdoLDAPException $e) {
    dump($e);
}
```

### Query Builder

[](#query-builder)

[TOP](#searching)

The query builder allows you to easily create queries using both LDAP and SQL Dialects. The main class `AdoLDAP` provides a `search` method that is a new instance of a `SearchFactory`. The `SearchFactory` can setup a new `QueryBuilder` to construct a search. You can use the `newQuery` method of `SearchFactory` to build a `QueryBuilder`, but you also can use any available method of `QueryBuilder` due to the magic methods present on `SearchFactory`, like the example below:

```
$ad->search()->newQuery()->whereEquals('objectCategory', 'user')->findBy('sAMAccountName', 'jdoe');

//OR

$ad->search()->whereEquals('objectCategory', 'user')->findBy('sAMAccountName', 'jdoe');
```

The `SearchFactory` also provides pre built or scoped searchs, to facilitate search users, computers and groups.

```
$ad->search()->users();

//EQUALS

$ad->search()->whereEquals('objectCategory', 'user');
```

```
$ad->search()->user('jdoe', ['name']);

//EQUALS

$ad->search()->whereEquals('objectCategory', 'user')->firstBy('sAMAccountName', 'jdoe', ['name']);
```

Summarizing, the `QueryBuilder` is compound by methods to construct the selection of attributes and conditional clausules, to search from a sepecific source, using either the `LDAPDialect` or `SQLDialect`. You build a `SELECT` with the `select` method, define the clausules with the `where` methods, having the possibility to use either the `LDAPDialect` or `SQLDialect`. You can also change and set a specific `BASE_DN` with the `from` method.

```
$ad->search()
    ->select(['name'])
    ->from('DC=MYDOMAIN,DC=COM')
    ->whereEquals('objectCategory', 'user')
    ->orWhere('objectCategory', 'computer')
    ->get();
```

**NOTE:** Is not necessary to define a base dn using from, because the value provided in configuration is used by default.

### Search Dialects

[](#search-dialects)

[TOP](#searching)

The `QueryBuilder` can search using both the [LDAP](https://docs.microsoft.com/en-us/windows/win32/adsi/ldap-dialect) and [SQL](https://docs.microsoft.com/en-us/windows/win32/adsi/sql-dialect) dialects. You can configure the dialect on the configuration, like the example below:

```
try {
    $config = [
        'host' => 'server01.mydomain.com',
        'baseDn' => 'DC=MYDOMAIN,DC=COM',
        'dialect' => SQLDialect::class
    ];
    $ad = new AdoLDAP($config);
} catch(\AdoLDAPException $e) {
    dump($e);
}
```

The default dialect is `LDAPDialect`. You can have your own implementation of both dialects by implementing a `DialectInterface`.

Handling Data
-------------

[](#handling-data)

[TOP](#adoldap)

Once you create your search, the data is retrivied by a `ResultSetIterator`. Using *ADODB* the result data is returned by a [**RecordSet**](https://docs.microsoft.com/en-us/sql/ado/reference/ado-api/recordset-object-ado?view=sql-server-ver15) which have similar function as a cursor, or a iterator. The `ResultSetIterator` implements the native PHP classes `SeekableIterator` and `Countable`, so in this way it's possible to simply loop through like it was `array` on a `foreach`.

```
$users = $ad->search()->users()
            ->select(['sAMAccountName', 'name', 'thumbnailPhoto', 'mail'])
            ->whereMemberOf('CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM')->get();

foreach($users as $user) {
    echo $user->sAMAccountName . '';
    echo $user->name . '';
    echo $user->thumbnailPhoto . '';
    echo $user->mail . '';
}
```

Each position of a *RecordSet* is retrievied by the `current` method of a `ResultSetIterator`. The data is typically returned as a `Entry`. A `Entry` holds all attributes returned by the search and expose them as get/set magic attributes, like in the example above, or you can also use `getAttribute` and `setAttribute` to get the desired values. If a attribute does not exists a `null` value will be returned instead.

### Models &amp; Column Map Attributes

[](#models--column-map-attributes)

[TOP](#handling-data)

Most of the searchs actually returns one of the `Models`, could beign a `User`, `Computer` or `Group`. The `Model` extends a `Entry` by provinding a human readeable get/set methods for the "default attributes", that enhances and facilitates the handling of certain values. If you find particular hard to understand the meaning or just don't known the available main attributes for objects on LDAP, the `Model` provides a `COLUMN_MAP` that maps the most important attributes of the corresponding AD object, to the more "human readeable" names. For example take a look to the `COLUMN_MAP` of a `User`:

```
const COLUMN_MAP = [
    'objectclass'           => 'objectclass',
    'dn'                    => 'distinguishedname',
    'account'               => 'samaccountname',
    'firtname'              => 'givenname',
    'name'                  => 'name',
    'workstations'          => 'userworkstations',
    'mail'                  => 'mail',
    'jobtitle'              => 'description',
    'jobrole'               => 'title',
    'address'   => [
        'street',
        'postalcode',
        'st',
        'l',
        'co'
    ],
    'mailboxes'             => 'msexchdelegatelistbl',
    'mobile'                => 'mobile',
    'phone'                 => 'telephoneNumber',
    'department'            => 'department',
    'departmentcode'        => 'extensionAttribute1',
    'memberOf'              => 'memberOf',
    'company'               => 'company',
    'photo'                 => 'thumbnailphoto',
    'passwordlastset'       => 'pwdlastset',
    'passworderrorcount'    => 'badpwdcount',
    'passworderrortime'     => 'badpasswordtime',
    'lastlogin'             => 'lastlogontimestamp',
    'lockouttime'           => 'lockouttime',
    'createdat'             => 'whencreated',
    'objectguid'            => 'objectguid',
    'objectsid'             => 'objectsid'
];
```

So in this way, the `User` model provides a list of get/set attributes using the mapped names, but if you prefer to use the original name scheme, you can still use the get/set magic attributes or the `getAttribute` and `setAttribute` methods.

```
$users = $ad->search()->users()
            ->select(['sAMAccountName', 'name', 'thumbnailPhoto', 'mail'])
            ->whereMemberOf('CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM')->get();

foreach($users as $user) {
    echo $user->getAccount();

    //EQUALS

    echo $user->sAMAccountName;
}
```

The available mapped attributes are physically present on the `Model` class to facilitate the auto complete features of IDEs. The `Model` can also provides special treatment to facilitate the usage of certains attributes, like a user photo.

```
$users = $ad->search()->users()
            ->select(['sAMAccountName', 'name', 'thumbnailPhoto'])
            ->whereMemberOf('CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM')->get();

foreach($users as $user) {
    echo $user->getAccount() . '';
    echo $user->getName() . '';
    echo $user->getHtmlPhoto(['class' => 'profile-picture']) . '';
}
```

The `getHtmlPhoto` returns a `IMG` HTML tag, already containing the class `profile-picture`, using the `src` as base64 string representation of the image. For the photo attribute you can also get just the base64 or the raw binary string or even save the photo to a file, with the respective methods `getPhoto`, `getRawPhoto`, `savePhoto`.

You can also use the mapped attributes in a `QueryBuilder` selection.

```
$attributes = SearchFactory::translateAttributes(User::COLUMN_MAP, ['accountName', 'name', 'photo', 'mailboxes']);

$ad->search()->users()
            ->select($attributes)
            ->whereMemberOf('CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM')->get();
}
```

If you use the `SearchFactory` to search a single `Entry`, the `user`, `computer` and `group` methods also provides a parameter that already translate the attributes, in fact the default behavior of these methods is to translate attributes.

```
$ad->search()->user('jdoe', ['accountName', 'name', 'photo', 'mailboxes']);

//EQUALS

$ad->search()->user('jdoe', ['sAMAccountName', 'name', 'thumbnailPhoto', 'msExchDelegateListBl'], false);
```

If you provide no attributes to those methods, the entire `COLUMN_MAP` will be used instead.

```
$ad->search()->user('jdoe');

//EQUALS

$ad->search()->user('jdoe', User::getDefaultAttributes(), false);
```

> ⚠️ **IMPORTANT:** ⚠️ Is possible to select all attributes, by provinding the value `['*']`, but this is **EXTREMELY DISCOURAGED**, not only by the fact that may have a lot of attributes that possibily won't be used, but specially to performance reasons. When you use a query, using the wildcard `*` the ADODB returns the ADSPATH, which is the full distinguished name of the object, forcing the library to resolve them by binding directly to the object using `COM`, which is EXTREMELY slow even for a single object. Normally searching for a entry takes arround 200-400ms, but binding to the object can take 4s. So only use for test purposes.

### Special Attributes

[](#special-attributes)

[TOP](#handling-data)

In addition to the `Model`, some attributes are handled as objects, like the `DistinguishedName`, `OS`, `Address` and `ObjectClass`.

#### DistinguidedName

[](#distinguidedname)

The `DistinguishedName` class as the name says handles the distinguished name, providing easy way to extract components and parts inside the name.

```
$dn = new DistinguishedName('CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM');
echo $dn->getName() . ''; //Container Name - AWESOME GROUP
echo $dn->getPath() //Whole Path - CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM
```

#### ObjectClass

[](#objectclass)

The `ObjectClass` is a simple object wrap to the `array` of the `objectClass` attributes. The class also provides the method `getMostRelevant` which is the last and most significative name of a `ObjectClass`. Each `Model` already have a defined `objectClass` that can be obtained by using the static method `objectClass`.

```
echo User::objectClass()->getMostRelevant() //outputs user
```

#### Address

[](#address)

The `Address` is a simple POJO object for the all address related attributes present on `User` entry. You can note that the `COLUMN_MAP` maps the `address` as 4 other attributes, which are flatten to compose the selection on the `QueryBuilder`. The Address also have a more clear syntax and name scheme.

```
$user = $ad->search()->user('jdoe', ['accountName', 'address']);
//EQUALS TO: $ad->search()->user('jdoe', ['sAMAcountName', 'street', 'postalCode', 'st', 'l', 'co']);

echo $user->getAddress()->getCountry();
```

#### OS

[](#os)

The `OS` handles the OS related values of the attributes `operatingSystem` and `operatingSystemVersion`, that can be found on `Computer` entries. The class extracts and splits the data providing methods to retrieve the `name`, `version`, `flavour`, and also ways to easily compare OSs.

```
$computer = $ad->search()->computer('MACHINE01');
echo $computer->getOS()->getName();
$computer->compareTo($ad->search()->computer('MACHEINE02')) //outputs -1, 0, 1;
```

### Paging Data

[](#paging-data)

[TOP](#handling-data)

The ADODB natively page the results by using the [`RecordSet`](https://docs.microsoft.com/en-us/windows/win32/adsi/searching-with-activex-data-objects-ado), which are managed by the `ResultSetIterator`. You can provide a specific LDAP page size by a value for `pageSize` configuration. The default value is 1000. Nevertheless, the `ResultSetIterator` provides a separate paging using the `getEntries` method, which allows you to define a proper limit/offset.

```
//Get 10 users offseting 1 page. Returns a array of User objects
$users = $ad->search()->users()
            ->select(User::getDefaultAttributes())
            ->whereMemberOf('CN=AWESOME GROUP,DC=MYDOMAIN,DC=COM')
            ->get()->getEntries(10, 1);
```

### After Fetch Callback

[](#after-fetch-callback)

[TOP](#handling-data)

The `ResultSetIterator` also provides a `afterFetch` callback, which allows you to transform the entries every time the `current` method is called.

```
$user = $ad->search()->user('jdoe')->afterFetch(function($user) {
                return [
                    'name' => $user->getName(),
                    'accountName' => $user->getAccount(),
                    'photo' => $user->getPhoto()
                ];
            })->getEntries();
```

You can set afterFetch multiple times, which will transform the data multiple times by the order that was provided.

Comming Soon
------------

[](#comming-soon)

[TOP](#adoldap)

- Active Record `Model`. For now only search functionality is available.
- Event support.
- More robust `QueryBuilder`.
- Full and complete documentation site.
- Cache support.

For Last
--------

[](#for-last)

Thanks for the [Adldap2](https://github.com/Adldap2/Adldap2) for inspiration to create this library.

> Made with ❤️ by [@lucasmarotta](https://github.com/lucasmarotta).

###  Health Score

25

—

LowBetter than 35% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity8

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

8

Last Release

2370d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/2507127?v=4)[Code Crafting](/maintainers/codecrafting-io)[@codecrafting-io](https://github.com/codecrafting-io)

---

Top Contributors

[![lucasmarotta](https://avatars.githubusercontent.com/u/37968565?v=4)](https://github.com/lucasmarotta "lucasmarotta (4 commits)")

---

Tags

ldapadodbadoadsildap-sso

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/codecrafting-io-adoldap/health.svg)

```
[![Health](https://phpackages.com/badges/codecrafting-io-adoldap/health.svg)](https://phpackages.com/packages/codecrafting-io-adoldap)
```

###  Alternatives

[pocketmine/pocketmine-mp

A server software for Minecraft: Bedrock Edition written in PHP

3.5k78.3k90](/packages/pocketmine-pocketmine-mp)[danog/madelineproto

Async PHP client API for the telegram MTProto protocol.

3.5k902.0k23](/packages/danog-madelineproto)[php-http/cache-plugin

PSR-6 Cache plugin for HTTPlug

25126.1M82](/packages/php-http-cache-plugin)[illuminate/http

The Illuminate Http package.

11937.9M6.9k](/packages/illuminate-http)[buggregator/trap

A simple and powerful tool for debugging PHP applications.

2702.2M68](/packages/buggregator-trap)[rdkafka/rdkafka

A PHP extension for Kafka

2.2k24.3k1](/packages/rdkafka-rdkafka)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
