Source for file LDAP.php
Documentation is available at LDAP.php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
// +--------------------------------------------------------------------------+
// +--------------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +--------------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or |
// | modify it under the terms of the GNU Lesser General Public |
// | License as published by the Free Software Foundation; either |
// | version 2.1 of the License, or (at your option) any later version. |
// | This library is distributed in the hope that it will be useful, |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
// | Lesser General Public License for more details. |
// | You should have received a copy of the GNU Lesser General Public |
// | License along with this library; if not, write to the Free Software |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// +--------------------------------------------------------------------------+
// | Authors: Tarjej Huse |
// +--------------------------------------------------------------------------+
// $Id: LDAP.php,v 1.19.2.10 2005/03/01 12:27:36 jw Exp $
require_once('PEAR.php');
require_once('LDAP/Entry.php');
require_once('LDAP/Search.php');
* Error constants for errors that are not LDAP errors
define ('NET_LDAP_ERROR', 1000 );
* Net_LDAP - manipulate LDAP servers the right way!
* @version $Revision: 1.19.2.10 $
* Class configuration array
* dn = the DN to bind as.
* host = the ldap host to connect to
* password = no explanation needed
* tls = when set, ldap_start_tls() is run after connecting.
* version = ldap version (defaults to v 3)
* filter = default search filter
* scope = default search scope
var $_config = array ('dn' => '',
'filter' => '(objectClass=*)',
* Net_LDAP Release Version
* @var object Net_LDAP_Schema
* Cache for attribute encoding checks
* @var array Hash with attribute names as key and boolean value
* to determine whether they should be utf8 encoded or not.
var $_schemaAttrs = array ();
* @param array Configuration array
$this->PEAR ('Net_LDAP_Error');
foreach ($_config as $k => $v) {
* Creates the initial ldap-object
* Static function that returns either an error object or the new Net_LDAP object.
* Something like a factory. Takes a config array with the needed parameters.
* @param array Configuration array
* @return mixed object Net_LDAP_Error or Net_LDAP
function &connect($config = array ())
return $this->raiseError ("It seems that you do not have the ldap-extension installed. Please install it before using this package.");
* Bind to the ldap-server
* The function may be used if you do not create the object using Net_LDAP::connect.
* @param array Configuration array
* @return mixed Net_LDAP_Error or true
function bind($config = array ())
foreach ($config as $k => $v) {
if ($this->_config['host']) {
$this->_link = @ldap_connect ($this->_config['host'], $this->_config['port']);
return $this->raiseError (" Host not defined in config. {$this->_config['host']}" );
// there is no good errorcode for this one! I chose 52.
return $this->raiseError ("Could not connect to server. ldap_connect failed.", 52 );
// You must set the version and start tls BEFORE binding!
if (isset ($this->_config['options']) &&
count($this->_config['options']))
foreach ($this->_config['options'] as $opt => $val) {
if (isset ($this->_config['dn']) && isset ($this->_config['password'])) {
$bind = @ldap_bind ($this->_link, $this->_config['dn'], $this->_config['password']);
$bind = @ldap_bind ($this->_link);
return $this->raiseError ("Bind failed " . @ldap_error ($this->_link), @ldap_errno ($this->_link));
* ReBind to the ldap-server using another dn and password
* The function may be used if you do not create the object using Net_LDAP::connect.
* @param string $dn - the DN to bind as.
* string $password - the bassword to use.
* @return mixed Net_LDAP_Error or true
function reBind ($dn = null , $password = null )
$bind = @ldap_bind ($this->_link, $dn, $password);
$bind = @ldap_bind ($this->_link);
return $this->raiseError ("Bind failed " . @ldap_error ($this->_link), @ldap_errno ($this->_link));
* Starts an encrypted session
* @return mixed True or Net_LDAP_Error
if (!@ldap_start_tls ($this->_link)) {
return $this->raiseError ("TLS not started. Error:" . @ldap_error ($this->_link), @ldap_errno ($this->_link));
* alias function of startTLS() for perl-ldap interface
* Closes the connection. Use this when the session is over.
@ldap_close ($this->_link);
* Add a new entryobject to a directory.
* Use add to add a new Net_LDAP_Entry object to the directory.
* @param object Net_LDAP_Entry
* @return mixed Net_LDAP_Error or true
if (@ldap_add ($this->_link, $entry->dn (), $entry->attributes ())) {
return $this->raiseError ("Could not add entry " . $entry->dn () . " " . @ldap_error ($this->_link),
@ldap_errno ($this->_link));
* Delete an entry from the directory
* The object may either be a string representing the dn or a Net_LDAP_Entry object.
* The param array may contain a boolean value named recursive. When set, all subentries
* of the Entry will be deleted as well
* @param mixed string or Net_LDAP_Entry
* @return mixed Net_LDAP_Error or true
function delete($dn, $param = array ())
// this is what the server would say: invalid_dn_syntax.
return $this->raiseError (" $dn not a string nor an entryobject!" , 34 );
if ($param['recursive'] ) {
$searchresult = @ldap_list ($this->_link, $dn, '(objectClass=*)', array ());
$entries = @ldap_get_entries ($this->_link, $searchresult);
for ($i=0; $i< $entries['count']; $i++ ) {
$result = $this->delete($entries[$i]['dn'], array ('recursive' => true ));
$errno = @ldap_errno ($this->_link);
return $this->raiseMessage ("Net_LDAP::delete: " . $this->errorMessage($errno), $errno);
if(PEAR ::isError ($result)){
if (!@ldap_delete ($this->_link, $dn)) {
$error = ldap_errno ($this->_link );
/* entry has subentries */
return $this->raiseError ('Net_LDAP::delete: Cound not delete entry ' . $dn .
' because of subentries. Use the recursive param to delete them.');
return $this->raiseError ("Net_LDAP::delete: Could not delete entry " . $dn . " because: ".
* This is taken from the perlpod of net::ldap, and explains things quite nicely.
* Modify the contents of DN on the server. DN May be a
* string or a Net::LDAP::Entry object.
* dn This option is here for compatibility only, and
* may be removed in future. Previous releases did
* not take the DN argument which replaces this
* add The add option should be a reference to a HASH.
* The values of the HASH are the attributes to add,
* and the values may be a string or a reference to a
* A reference to an ARRAY of attributes to delete.
* TODO: This does not support deleting one or two values yet - use
* The <replace> option takes a argument in the same
* form as add, but will cause any existing
* attributes with the same name to be replaced. If
* erence to an empty string the all instances of the
* attribute will be deleted.
* This is an alternative to add, delete and replace
* where the whole operation can be given in a single
* argument. The argument should be a array
* Values in the ARRAY are used in pairs, the first
* is the operation add, delete or replace and the
* second is a reference to an ARRAY of attribute
* The attribute value list is also used in pairs.
* The first value in each pair is the attribute name
* and the second is a reference to a list of values.
* $ldap->modify ( $dn, array (changes => array(
* 'delete' => array('faxNumber' => ''),
* 'add' => array('sn' => 'Barr'),
* 'replace' => array(email => 'tarjei@nu.no'))));
* @return mixed Net_LDAP_Error or true
function modify($dn , $params = array ())
// since $params['dn'] is not used in net::ldap now:
if (isset ($params['dn'])) {
return $this->raiseError ("This feature will not be implemented!");
// new code from rafael at krysciak dot de
$_params['changes'] = $params;
foreach($_params['changes'] AS $option => $atrr) {
$command = $dn_exists ? 'ldap_mod_add': 'ldap_add';
$command = 'ldap_mod_replace';
$command = 'ldap_mod_del';
// to delete an attribute with a specific value you
// need a hash array('attr_name' => array('attr_value_1', ... ,'attr_value_n'))
// the hash array('attr_name' => 'attr_value') will be converted
// automatically to array('attr_name' => array('attr_value'))
foreach($atrr AS $atrr_field => $atrr_value) {
$atrr[$atrr_field] = array ($atrr_value);
return $this->raiseError ("Net_LDAP::modify: not supported option " . $option);
} // end switch($option) {
return $this->raiseError (" Net_LDAP::modify: $dn not modified because:" . ldap_error ($this->_link), ldap_errno ($this->_link));
} // end foreach($_params['changes'] AS $option => $atrr) {
} // end if (is_array($_params['changes'])) {
// everything went fine :)
/* old broken code see bug#2987
if (isset($params['changes'])) {
if (isset($params['changes']['add']) &&
!@ldap_modify($this->_link, $dn, $params['changes']['add'])) {
return $this->raiseError("Net_LDAP::modify: $dn not modified because:" . ldap_error($this->_link),
ldap_errno($this->_link));
if (isset($params['changes']['replace']) &&
!@ldap_modify($this->_link, $dn, $params['changes']['replace'])) {
return $this->raiseError("Net_LDAP::modify: replace change didn't work: " . ldap_error($this->_link),
ldap_errno($this->_link));
if (isset($params['changes']['delete']) &&
!@ldap_mod_del($this->_link, $dn, $params['changes']['delete'])) {
return $this->raiseError("Net_LDAP::modify:delete did not work" . ldap_error($this->_link),
ldap_errno($this->_link));
if (isset($params['add']) && !@ldap_add($this->_link, $dn, $params['add'])) {
return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
if (isset($params['replace']) && !@ldap_modify($this->_link, $dn, $params['replace'])) {
return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
if (isset($params['delete'])) {
// since you delete an attribute by making it empty:
foreach ($params['delete'] as $k) {
$params['delete'][$k] = '';
if (!@ldap_modify($this->_link, $dn, $params['delete'])) {
return $this->raiseError(ldap_error($this->_link), ldap_errno($this->_link));
// everything went fine :)
* Search is used to query the ldap-database.
* $base and $filter may be ommitted. BaseDN and default filter will then be used.
* scope: The scope which will be used for searching
* one - Immediately below $base
* sizelimit: Limit the number of entries returned (default: 0),
* timelimit: Limit the time spent for searching (default: 0),
* attrsonly: If true, the search will only return the attribute names, NO values
* attributes: Array of attribute names, which the entry should contain. It is good practice
* to limit this to just the ones you need, so by default this function does not
* return any attributes at all.
* deref: By default aliases are dereferenced to locate the base object for the search, but not when
* searching subordinates of the base object. This may be changed by specifying one of the
* never - Do not dereference aliases in searching or in locating the base object of the search.
* search - Dereference aliases in subordinates of the base object in searching, but not in
* locating the base object of the search.
* @param string LDAP searchbase
* @param string LDAP search filter
* @param array Array of options
* @return object mixed Net_LDAP_Search or Net_LDAP_Error
function search($base = null , $filter = null , $params = array ())
$base = $this->_config['base'];
$filter = $this->_config['filter'];
/* setting searchparameters */
(isset ($params['sizelimit'])) ? $sizelimit = $params['sizelimit'] : $sizelimit = 0;
(isset ($params['timelimit'])) ? $timelimit = $params['timelimit'] : $timelimit = 0;
(isset ($params['attrsonly'])) ? $attrsonly = $params['attrsonly'] : $attrsonly = 0;
(isset ($params['attributes'])) ? $attributes = $params['attributes'] : $attributes = array ('');
$this->raiseError ("The param attributes must be an array!");
/* scoping makes searches faster! */
$scope = (isset ($params['scope']) ? $params['scope'] : $this->_config['scope']);
$search_function = 'ldap_list';
$search_function = 'ldap_read';
$search_function = 'ldap_search';
if ($err = ldap_errno ($this->_link)) {
// Errorcode 32 = no such object, i.e. a nullresult.
// Errorcode 4 = sizelimit exeeded. this will be handled better in time...
// return $obj = & new Net_LDAP_Search ($search, $this->_link);
return $this->raiseError ($this->errorMessage($err) . " ($filter)" , $err);
$msg = " \nParameters:\nBase: $base\nFilter: $filter\nScope: $scope";
return $this->raiseError ($this->errorMessage($err) . $msg, $err);
* @param string Option to set
* @param mixed Value to set Option to
* @return mixed Net_LDAP_Error or true
if (@ldap_set_option ($this->_link, constant($option), $value)) {
$err = @ldap_errno ($this->_link);
$msg = @ldap_err2str ($err);
return $this->raiseError ($msg, $err);
return $this->raiseError ("Unkown Option requested");
return $this->raiseError ("No LDAP connection");
* Get an LDAP option value
* @param string Option to get
* @return mixed Net_LDAP_Error or option value
if (@ldap_get_option ($this->_link, constant($option), $value)) {
$err = @ldap_errno ($this->_link);
$msg = @ldap_err2str ($err);
return $this->raiseError ($msg, $err);
$this->raiseError ("Unkown Option requested");
$this->raiseError ("No LDAP connection");
* Get the LDAP_PROTOCOL_VERSION that is used on the connection.
* A lot of ldap functionality is defined by what protocol version the ldap server speaks.
$version = $this->getOption("LDAP_OPT_PROTOCOL_VERSION");
$version = $this->_config['version'];
* Set the LDAP_PROTOCOL_VERSION that is used on the connection.
* @param int Version to set
* @return mixed Net_LDAP_Error or TRUE
$version = $this->_config['version'];
return $this->setOption("LDAP_OPT_PROTOCOL_VERSION", $version);
* Get the Net_LDAP version.
* Return the Net_LDAP version
* @return string Net_LDAP version
* Tell if a dn already exists
//$filter = '(objectclass=*)';
$result = @ldap_list ($this->_link, $base, $filter, array (), 1 , 1 );
if (ldap_errno ($this->_link) == 32 ) {
if (ldap_errno ($this->_link) != 0 ) {
$this->raiseError (ldap_error ($this->_link), ldap_errno ($this->_link));
if (@ldap_count_entries ($this->_link, $result)) {
* Get a specific entry based on the dn
* @param array Array of Attributes to select
* @return object Net_LDAP_Entry or Net_LDAP_Error
function &getEntry($dn, $attr = array (''))
$result = $this->search($dn, '(objectClass=*)', array ('scope' => 'base', 'attributes' => $attr));
$entry = $result->shiftEntry ();
return $this->raiseError ('Could not fetch entry');
* Returns the string for an ldap errorcode.
* Made to be able to make better errorhandling
* Function based on DB::errorMessage()
* Tip: The best description of the errorcodes is found here: http://www.directory-info.com/LDAP/LDAPErrorCodes.html
* @return string The errorstring for the error.
0x01 => "LDAP_OPERATIONS_ERROR",
0x02 => "LDAP_PROTOCOL_ERROR",
0x03 => "LDAP_TIMELIMIT_EXCEEDED",
0x04 => "LDAP_SIZELIMIT_EXCEEDED",
0x05 => "LDAP_COMPARE_FALSE",
0x06 => "LDAP_COMPARE_TRUE",
0x07 => "LDAP_AUTH_METHOD_NOT_SUPPORTED",
0x08 => "LDAP_STRONG_AUTH_REQUIRED",
0x09 => "LDAP_PARTIAL_RESULTS",
0x0b => "LDAP_ADMINLIMIT_EXCEEDED",
0x0c => "LDAP_UNAVAILABLE_CRITICAL_EXTENSION",
0x0d => "LDAP_CONFIDENTIALITY_REQUIRED",
0x0e => "LDAP_SASL_BIND_INPROGRESS",
0x10 => "LDAP_NO_SUCH_ATTRIBUTE",
0x11 => "LDAP_UNDEFINED_TYPE",
0x12 => "LDAP_INAPPROPRIATE_MATCHING",
0x13 => "LDAP_CONSTRAINT_VIOLATION",
0x14 => "LDAP_TYPE_OR_VALUE_EXISTS",
0x15 => "LDAP_INVALID_SYNTAX",
0x20 => "LDAP_NO_SUCH_OBJECT",
0x21 => "LDAP_ALIAS_PROBLEM",
0x22 => "LDAP_INVALID_DN_SYNTAX",
0x24 => "LDAP_ALIAS_DEREF_PROBLEM",
0x30 => "LDAP_INAPPROPRIATE_AUTH",
0x31 => "LDAP_INVALID_CREDENTIALS",
0x32 => "LDAP_INSUFFICIENT_ACCESS",
0x34 => "LDAP_UNAVAILABLE",
0x35 => "LDAP_UNWILLING_TO_PERFORM",
0x36 => "LDAP_LOOP_DETECT",
0x3C => "LDAP_SORT_CONTROL_MISSING",
0x3D => "LDAP_INDEX_RANGE_ERROR",
0x40 => "LDAP_NAMING_VIOLATION",
0x41 => "LDAP_OBJECT_CLASS_VIOLATION",
0x42 => "LDAP_NOT_ALLOWED_ON_NONLEAF",
0x43 => "LDAP_NOT_ALLOWED_ON_RDN",
0x44 => "LDAP_ALREADY_EXISTS",
0x45 => "LDAP_NO_OBJECT_CLASS_MODS",
0x46 => "LDAP_RESULTS_TOO_LARGE",
0x47 => "LDAP_AFFECTS_MULTIPLE_DSAS",
0x51 => "LDAP_SERVER_DOWN",
0x52 => "LDAP_LOCAL_ERROR",
0x53 => "LDAP_ENCODING_ERROR",
0x54 => "LDAP_DECODING_ERROR",
0x56 => "LDAP_AUTH_UNKNOWN",
0x57 => "LDAP_FILTER_ERROR",
0x58 => "LDAP_USER_CANCELLED",
0x59 => "LDAP_PARAM_ERROR",
0x5a => "LDAP_NO_MEMORY",
0x5b => "LDAP_CONNECT_ERROR",
0x5c => "LDAP_NOT_SUPPORTED",
0x5d => "LDAP_CONTROL_NOT_FOUND",
0x5e => "LDAP_NO_RESULTS_RETURNED",
0x5f => "LDAP_MORE_RESULTS_TO_RETURN",
0x60 => "LDAP_CLIENT_LOOP",
0x61 => "LDAP_REFERRAL_LIMIT_EXCEEDED",
1000 => "Unknown Net_LDAP error"
return isset ($errorMessages[$errorcode]) ? $errorMessages[$errorcode] : $errorMessages[NET_LDAP_ERROR];
* Tell whether value is a Net_LDAP_Error or not
return (is_a($value, "Net_LDAP_Error") || parent ::isError ($value));
* @author Jan Wagner <wagner@netsols.de>
* @param array Array of attributes to search for
* @return object mixed Net_LDAP_Error or Net_LDAP_RootDSE
require_once('Net/LDAP/RootDSE.php');
$attributes = array ('namingContexts',
'supportedSASLMechanisms',
$result = $this->search('', '(objectClass=*)', array ('attributes' => $attributes, 'scope' => 'base'));
$entry = $result->shift_entry ();
if (false === $entry) return $this->raiseError ('Could not fetch RootDSE entry');
* alias function of rootDse() for perl-ldap interface
* @author Jan Wagner <wagner@netsols.de>
* @param string Subschema entry dn
* @return object mixed Net_LDAP_Schema or Net_LDAP_Error
require_once('Net/LDAP/Schema.php');
// get the subschema entry via root dse
$dse = $this->rootDSE (array ('subschemaSubentry'));
$base = $dse->getValue ('subschemaSubentry', 'single');
// fetch the subschema entry
$result = $this->search($dn, '(objectClass=*)',
array ('attributes' => array_values($schema->types ), 'scope' => 'base'));
$entry = $result->shift_entry ();
return $this->raiseError ('Could not fetch Subschema entry');
* Encodes given attributes to UTF8 if needed
* This function takes attributes in an array and then checks against the schema if they need
* UTF8 encoding. If that is so, they will be encoded. An encoded array will be returned and
* can be used for adding or modifying.
* @param array Array of attributes
* @return array Array of UTF8 encoded attributes
return $this->_utf8 ($attributes, 'utf8_encode');
* Decodes the given attribute values
* @param array Array of attributes
* @return array Array with decoded attribute values
return $this->_utf8 ($attributes, 'utf8_decode');
* Encodes or decodes attribute values if needed
* @param array Array of attributes
* @param array Function to apply to attribute values
* @return array Array of attributes with function applied to values
function _utf8 ($attributes, $function)
$this->_schema = $this->schema();
foreach( $attributes as $k => $v ) {
if (!isset ($this->_schemaAttrs[$k])) {
$attr = $this->_schema->get('attribute', $k);
if (false !== strpos($attr['syntax'], '1.3.6.1.4.1.1466.115.121.1.15')) {
$this->_schemaAttrs[$k] = $encode;
$encode = $this->_schemaAttrs[$k];
foreach ($v as $ak => $av) {
* Net_LDAP_Error implements a class for reporting portable LDAP error messages.
* Net_LDAP_Error constructor.
* @param mixed Net_LDAP error code, or string with error message.
* @param integer what "error mode" to operate in
* @param integer what error level to use for $mode & PEAR_ERROR_TRIGGER
* @param mixed additional debug info, such as the last query
function Net_LDAP_Error($code = NET_LDAP_ERROR , $mode = PEAR_ERROR_RETURN ,
$level = E_USER_NOTICE , $debuginfo = null )
$this->PEAR_Error ('Net_LDAP_Error: ' . Net_LDAP::errorMessage($code), $code, $mode, $level, $debuginfo);
$this->PEAR_Error (" Net_LDAP_Error: $code" , NET_LDAP_ERROR, $mode, $level, $debuginfo);
Documentation generated on Mon, 11 Mar 2019 14:26:18 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|