symfony/ldap
Symfony LDAP component: an LDAP client for PHP built on the PHP ldap extension. Provides tools to connect, bind, search, and manage LDAP directories. Stable since Symfony 3.1; earlier versions were internal and may break when upgrading.
Install the package:
composer require symfony/ldap
Ensure PHP’s ldap extension is enabled in php.ini.
Configure the connection (Laravel service provider):
use Symfony\Component\Ldap\LdapClient;
use Symfony\Component\Ldap\Adapter\ExtLdapAdapter;
$adapter = new ExtLdapAdapter('ldap://your-server:389');
$client = new LdapClient($adapter);
First use case: Bind and search
$client->bind('cn=admin,dc=example,dc=com', 'password');
$results = $client->search('ou=users,dc=example,dc=com', '(uid=jdoe)');
foreach ($results as $entry) {
echo $entry->getDn() . "\n";
}
ExtLdapAdapter (for connection management) and LdapClient (core operations).LdapQuery (for building complex searches) and LdapEntry (for handling results).$adapter = new ExtLdapAdapter('ldap://ad.example.com');
$client = new LdapClient($adapter);
try {
$client->bind('uid=jdoe,ou=users', 'password');
// Authenticated—proceed with operations.
} catch (\Symfony\Component\Ldap\Exception\ConnectionException $e) {
// Handle failed bind (e.g., invalid credentials).
}
$query = new LdapQuery();
$query
->where('objectClass', 'person')
->where('mail', '=john@example.com')
->limit(10);
$results = $client->search('ou=users,dc=example,dc=com', $query);
$entry = $client->findEntry('uid=jdoe,ou=users');
$entry->setAttribute('mail', 'john.doe@example.com');
$client->update($entry);
// In Laravel’s AuthServiceProvider:
public function boot()
{
$this->app['auth']->provider('ldap', function ($app) {
$adapter = new ExtLdapAdapter('ldap://ad.example.com');
$client = new LdapClient($adapter);
return new SymfonyLdapUserProvider($client, 'ou=users');
});
}
$users = $client->search('ou=users', '(objectClass=person)');
foreach ($users as $user) {
$user->setAttribute('employeeNumber', '12345');
$client->update($user);
}
Service Container Binding:
$this->app->singleton(LdapClient::class, function ($app) {
$adapter = new ExtLdapAdapter(config('ldap.server'));
return new LdapClient($adapter);
});
Configurable LDAP Servers:
// config/ldap.php
return [
'servers' => [
'primary' => [
'host' => 'ldap.example.com',
'port' => 389,
'encryption' => 'tls',
],
],
];
Queue Jobs with Resettable Adapter:
// In a Laravel queue job:
public function handle()
{
$adapter = new ExtLdapAdapter('ldap://ad.example.com');
$client = new LdapClient($adapter);
try {
$client->bind('...');
// Perform operations.
} finally {
$adapter->reset(); // Critical for long-running jobs!
}
}
form_login_ldap for authentication:
# config/packages/security.yaml
firewalls:
main:
form_login_ldap:
check_path: /login_check
provider: ldap
Connection Leaks in Queues/Jobs
$adapter->reset() in long-running processes (e.g., Laravel queues) can exhaust LDAP server connections.try/finally blocks to reset the adapter:
try {
$client->bind(...);
// Operations...
} finally {
$adapter->reset();
}
Case Sensitivity in Filters
uid) may behave unexpectedly.$query->where('uid', '=JohnDoe'); // May fail if 'uid' is stored as 'johndoe'.
Schema Mismatches
objectClass=person vs. objectClass=user).$schema = $client->getSchema('ou=users');
TLS/SSL Configuration
ExtLdapAdapter with explicit TLS settings:
$adapter = new ExtLdapAdapter('ldap://ad.example.com:636', [
'options' => [
LDAP_OPT_PROTOCOL_VERSION => 3,
LDAP_OPT_REFERRALS => 0,
],
'encryption' => 'ssl',
]);
Deprecated Methods
eraseCredentials() (deprecated in Symfony 7.3+).LdapUser objects; Symfony’s security component handles credentials securely.Enable LDAP Debugging
$adapter = new ExtLdapAdapter('ldap://ad.example.com', [
'options' => [
LDAP_OPT_DEBUG_LEVEL => 7, // Enable verbose logging
],
]);
Validate DN Formats
LdapEntry::getDn() to inspect Distinguished Names (DNs). Invalid DNs will cause silent failures.Test with a Local LDAP Server
docker run -p 389:389 -p 636:636 osixia/openldap
Handle Timeouts Gracefully
$adapter = new ExtLdapAdapter('ldap://ad.example.com', [
'options' => [
LDAP_OPT_NETWORK_TIMEOUT => 5, // 5 seconds
],
]);
Custom Query Builders
LdapQuery for domain-specific filters:
class UserQuery extends LdapQuery
{
public function active()
{
return $this->where('accountStatus', '=active');
}
}
Attribute Mappers
$user = new User();
$user->name = $entry->getAttribute('cn')[0];
$user->email = $entry->getAttribute('mail')[0];
Event Listeners
$client->addListener(new class implements LdapEventListenerInterface {
public function onSearch(LdapEvent $event) {
logger()->info('LDAP Search:', $event->getQuery());
}
});
Custom Adapters
LdapAdapterInterface for non-ExtLdap backends (e.g., Active Directory-specific logic):
class AdLdapAdapter implements LdapAdapterInterface
{
public function bind($dn, $password) { /* ... */ }
// Implement other methods...
}
How can I help you explore Laravel packages today?