Source for file ldap2.php
Documentation is available at ldap2.php
// Pear DB LDAP2 - Database independent query interface definition
// for PHP's LDAP extension with protocol version 2.
// Copyright (C) 2002-2003 Piotr Roszatycki <dexter@debian.org>
// Copyright (C) 2002 Ludovico Magnocavallo <ludo@sumatrasolutions.com>
// 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
// $Id: ldap2.php 302688 2010-08-23 11:10:32Z clockwerx $
require_once 'DB/common.php';
* LDAP2 DB interface class
* DB_ldap2 extends DB_common to provide DB compliant
* access to LDAP servers with protocol version 2.
* @author Piotr Roszatycki <dexter@debian.org>
* @version $Revision: 302688 $
* LDAP connection handler
* list of actions which manipulate data
var $action_manip = array (
'add', 'compare', 'delete', 'modify', 'mod_add', 'mod_del',
'mod_replace', 'rename');
* list of parameters for search actions
var $param_search = array (
'action', 'base_dn', 'attributes', 'attrsonly', 'sizelimit',
'timelimit', 'deref','sort');
* list of parameters for modify actions
var $param_modify = array (
'action', 'attribute', 'value', 'newrdn', 'newparent',
* default parameters for query
* parameters for last query
var $last_param = array ();
* array contained row counters for last query
* array contained number of rows for last query
* array contained entry handlers for last query
* array contained number of rows affected by last query
* Constructor, calls DB_common constructor
* @see DB_common::DB_common()
$this->phptype = 'ldap2';
$this->dbsyntax = 'ldap2';
$this->errorcode_map = array (
0x10 => DB_ERROR_NOSUCHFIELD , // LDAP_NO_SUCH_ATTRIBUTE
0x11 => DB_ERROR_NOSUCHFIELD , // LDAP_UNDEFINED_TYPE
0x12 => DB_ERROR_CONSTRAINT , // LDAP_INAPPROPRIATE_MATCHING
0x13 => DB_ERROR_CONSTRAINT , // LDAP_CONSTRAINT_VIOLATION
0x14 => DB_ERROR_ALREADY_EXISTS , // LDAP_TYPE_OR_VALUE_EXISTS
0x15 => DB_ERROR_INVALID , // LDAP_INVALID_SYNTAX
0x20 => DB_ERROR_NOSUCHTABLE , // LDAP_NO_SUCH_OBJECT
0x21 => DB_ERROR_NOSUCHTABLE , // LDAP_ALIAS_PROBLEM
0x22 => DB_ERROR_INVALID , // LDAP_INVALID_DN_SYNTAX
0x23 => DB_ERROR_INVALID , // LDAP_IS_LEAF
0x24 => DB_ERROR_INVALID , // LDAP_ALIAS_DEREF_PROBLEM
0x30 => DB_ERROR_ACCESS_VIOLATION , // LDAP_INAPPROPRIATE_AUTH
0x31 => DB_ERROR_ACCESS_VIOLATION , // LDAP_INVALID_CREDENTIALS
0x32 => DB_ERROR_ACCESS_VIOLATION , // LDAP_INSUFFICIENT_ACCESS
0x40 => DB_ERROR_MISMATCH , // LDAP_NAMING_VIOLATION
0x41 => DB_ERROR_CONSTRAINT , // LDAP_OBJECT_CLASS_VIOLATION
0x44 => DB_ERROR_ALREADY_EXISTS , // LDAP_ALREADY_EXISTS
0x51 => DB_ERROR_CONNECT_FAILED , // LDAP_SERVER_DOWN
0x57 => DB_ERROR_SYNTAX // LDAP_FILTER_ERROR
* Connect and bind to LDAPv2 server with either anonymous
* or authenticated bind depending on dsn info
* The format of the supplied DSN:
* ldap2://binddn:bindpw@host:port/basedn
* ldap2://uid=dexter,ou=People,dc=example,dc=net:secret@127.0.0.1/dc=example,dc=net
* @param $dsn the data source name (see DB::parseDSN for syntax)
* @param boolean $persistent kept for interface compatibility
* @return int DB_OK if successfully connected.
* A DB error code is returned on failure.
function connect($dsninfo, $persistent = false )
if (!PEAR ::loadExtension ('ldap')) {
return $this->raiseError (DB_ERROR_EXTENSION_NOT_FOUND );
$type = $dsninfo['phptype'];
$user = $dsninfo['username'];
$pw = $dsninfo['password'];
$host = $dsninfo['hostspec'];
$port = empty ($dsninfo['port']) ? 389 : $dsninfo['port'];
'base_dn' => $this->base_dn = $dsninfo['database'],
'deref' => LDAP_DEREF_NEVER ,
$this->last_param = $this->param;
$this->setOption ("seqname_format", "sn=%s," . $dsninfo['database']);
$this->fetchmode = DB_FETCHMODE_ASSOC;
$conn = @ldap_connect ($host, $port);
return $this->raiseError (" unknown host $host" );
return $this->raiseError (DB_ERROR_CONNECT_FAILED );
$bind = @ldap_bind ($conn, $user, $pw);
$bind = @ldap_bind ($conn);
return $this->raiseError (DB_ERROR_CONNECT_FAILED );
$this->connection = $conn;
* Unbinds from LDAP server
* @return int ldap_unbind() return value
$ret = @ldap_unbind ($this->connection);
$this->connection = null;
* Performs a request against the LDAP server
* The type of request depend on $query parameter. If $query is string,
* perform simple searching query with filter in $query parameter.
* If $query is array, the first element of array is filter string
* (for reading operations) or data array (for writing operations).
* Another elements of $query array are query parameters which overrides
* the default parameters.
* The following parameters can be passed for search queries:<br />
* <li />attributes - array, the attributes that shall be returned
* <li />sizelimit - integer, the max number of results to be returned
* <li />timelimit - integer, the timelimit after which to stop searching
* <li/>sort - string, which tells the attribute name by which to sort
* // 'base_dn' is not given, so the one passed to connect() will be used
* $db->simpleQuery("uid=dexter");
* // the 'attributes' key defines the attributes that shall be returned
* // 'sort' defines the sort order of the data
* $db->simpleQuery(array(
* 'base_dn' => 'ou=People,dc=example,dc=net',
* 'attributes'=>array('dn','o','l'),
* // use this kind of query for adding data
* 'dn' => 'cn=Piotr Roszatycki,dc=example,dc=com',
* 'objectClass' => array('top', 'person'),
* 'cn' => 'Piotr Roszatycki',
* @param mixed $query the ldap query
* @return int result from LDAP function for failure queries,
* DB_OK for successful queries or DB Error object if wrong syntax
$query = (isset ($query[0 ]) ? $query[0 ] : 'objectClass=*');
$action = (isset ($last_param['action']) ? $last_param['action'] : $this->param['action']);
// check if the given action is a valid modifier action, i.e. 'search'
$this->last_param = $this->param;
foreach($this->param_search as $k) {
if (isset ($last_param[$k])) {
$this->last_param[$k] = $last_param[$k];
// double escape char for filter: '(o=Przedsi\C4\99biorstwo)' => '(o=Przedsi\\C4\\99biorstwo)'
$this->last_query = $query;
// ldap_search, *list, *read have the same arguments
$ldap_action = " ldap_$action";
$result = @$ldap_action($this->connection, $base_dn, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
$this->row[$result] = 0; // reset the row counter.
$numrows = $this->numrows ($result);
$this->num_rows[$result] = $numrows;
ldap_sort ($this->connection,$result,$sort);
// If first argument is an array, it contains the entry with DN.
$this->last_param = $this->param;
foreach($this->param_modify as $k) {
if (isset ($last_param[$k])) {
$this->last_param[$k] = $last_param[$k];
$this->last_query = $query;
$ldap_action = " ldap_$action";
$result = @$ldap_action($this->connection, $dn, $entry);
$result = @ldap_compare ($this->connection, $dn, $attribute, $value);
$result = @ldap_delete ($this->connection, $dn);
$result = @ldap_rename ($this->connection, $dn, $newrdn, $newparent, $deleteoldrdn);
* Move the internal ldap result pointer to the next available result
* @param a valid ldap result resource
* @return true if a result is available otherwise return false
return @ldap_next_entry ($result);
* Fetch and return a row of data (it uses fetchInto for that)
* @param $result LDAP result identifier
* @param $fetchmode format of fetched row array
* @param $rownum the absolute row number to fetch
* @return array a row of data, or false on error
function fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT , $rownum=null )
if ($fetchmode == DB_FETCHMODE_DEFAULT ) {
$fetchmode = $this->fetchmode;
$res = $this->fetchInto($result, $arr, $fetchmode, $rownum);
* Fetch a row and insert the data into an existing array.
* DB_FETCHMODE_ORDERED returns a flat array of values
* ("value", "val1", "val2").
* DB_FETCHMODE_ASSOC returns an array of structuralized data
* ("field_name1" => "value", "field_name2" => array("val1", "val2")).
* @param $result PostgreSQL result identifier
* @param $arr (reference) array where data from the row is stored
* @param $fetchmode how the array data should be indexed
* @param $rownum the row number to fetch
* @return int DB_OK on success, a DB error code on failure
function fetchInto($result, &$arr, $fetchmode, $rownum=null )
// $rownum is unimplemented, yet
$rownum = $this->row[$result];
if ($rownum >= $this->num_rows[$result]) {
$entry = @ldap_first_entry ($this->connection, $result);
$entry = @ldap_next_entry ($this->connection, $this->entry[$result]);
$this->entry[$result] = $entry;
$errno = ldap_errno ($this->connection);
case DB_FETCHMODE_ORDERED:
if (!($attr = @ldap_get_attributes ($this->connection, $entry))) {
$errno = ldap_errno ($this->connection);
if ($attr["count"] == 0 ) {
if (!($arr[] = @ldap_get_dn ($this->connection, $entry))) {
$errno = ldap_errno ($this->connection);
while (list ($attr_name, $attr_val) = each($attr)) {
if ($attr_val["count"] == 1 ) {
} elseif ($attr_val["count"] > 1 ) {
for ($i=0; $i< $attr_val["count"]; $i++ ) {
if (!($arr["dn"] = @ldap_get_dn ($this->connection, $entry))) {
$errno = ldap_errno ($this->connection);
if (!($attr = @ldap_get_attributes ($this->connection, $entry))) {
$errno = ldap_errno ($this->connection);
while (list ($attr_name, $attr_val) = each($attr)) {
if ($attr_val["count"] == 1 ) {
} elseif ($attr_val["count"] > 1 ) {
for ($i=0; $i< $attr_val["count"]; $i++ ) {
$this->row[$result] = ++ $rownum;
* Free the internal resources associated with $result.
* @param $result int LDAP result identifier or DB statement identifier
* @return bool TRUE on success, FALSE if $result is invalid
return @ldap_free_result ($result);
if (!isset ($this->prepare_tokens[(int) $result])) {
unset ($this->prepare_tokens[(int) $result]);
unset ($this->prepare_types[(int) $result]);
unset ($this->prepared_queries[(int) $result]);
unset ($this->row[(int) $result]);
unset ($this->num_rows[(int) $result]);
unset ($this->entry[(int) $result]);
$this->last_param = $this->param;
$this->attributes = null;
* Quote the given string so it can be safely used within string delimiters
* @param $string mixed Data to be quoted
* @return mixed "NULL" string, quoted string or original data
function quote($str = null )
$str = str_replace(array ('\\', '"'), array ('\\\\', '\\"'), $str);
* Get the number of columns in a result set. This function
* is used only for compatibility reasons.
* @param $result resource LDAP result identifier
* @return int DB_ERROR_NOT_CAPABLE error code
* Get the number of rows in a result set.
* @param $result resource LDAP result identifier
* @return int the number of rows in $result
$rows = @ldap_count_entries ($this->connection, $result);
* Get the native error code of the last error (if any) that
* occured on the current connection.
* @return int native LDAP error code
return ldap_error ($this->connection);
* Gets the number of rows affected by the last query.
* if the last query was a select, returns 0.
* @return int number of rows affected by the last query or DB_ERROR
* Returns the query needed to get some backend info. This function is
* used only for compatibility reasons.
* @return int DB_ERROR_NOT_CAPABLE error code
* Tell whether an action is a data manipulation action (add, compare,
* delete, modify, mod_add, mod_del, mod_replace, rename)
* @param string $action the query
* @return boolean whether $query is a data manipulation action
return(in_array($action, $this->action_manip));
function base($base_dn = null )
$this->q_base_dn = ($base_dn !== null ) ? $base_dn : null;
$this->base_dn = ($base_dn !== null ) ? $base_dn : $this->d_base_dn;
* Get the next value in a sequence.
* LDAP provides transactions for only one entry and we need to
* prevent race condition. If unique value before and after modify
* aren't equal then wait and try again.
* @param string $seq_name the sequence name
* @param bool $ondemand whether to create the sequence on demand
* @return a sequence integer, or a DB error
function nextId($seq_name, $ondemand = true )
$seq_dn = $this->getSequenceName ($seq_name);
// Get the sequence entry
$this->expectError (DB_ERROR_NOSUCHTABLE );
$data = $this->getRow (array ('objectClass=*', 'action'=> 'read', 'base_dn'=> $seq_dn));
if (DB ::isError ($data)) {
if ($ondemand && $repeat == 0
&& $data->getCode () == DB_ERROR_NOSUCHTABLE ) {
// Try to create sequence and repeat
if (DB ::isError ($data)) {
// Increment sequence value
// Unique identificator of transaction
$data['uid'] = $seq_unique;
$data = $this->simpleQuery(array ($data, 'action'=> 'modify'));
if (DB ::isError ($data)) {
// Get the entry and check if it contains our unique value
$data = $this->getRow (array ('objectClass=*', 'action'=> 'read', 'base_dn'=> $seq_dn));
if (DB ::isError ($data)) {
if ($data['uid'] != $seq_unique) {
// It is not our entry. Wait a little time and repeat
if (DB ::isError ($data)) {
* The sequence entry is based on core schema with extensibleObject,
* so it should work with any LDAP server which doesn't check schema
* or supports extensibleObject object class.
* objectClass: extensibleObject
* @param string $seq_name the sequence name
* @return mixed DB_OK on success or DB error on error
$seq_dn = $this->getSequenceName ($seq_name);
// Create the sequence entry
'objectclass' => array ('top', 'extensibleObject'),
$data = $this->simpleQuery(array ($data, 'action'=> 'add'));
* @param string $seq_name the sequence name
* @return mixed DB_OK on success or DB error on error
$seq_dn = $this->getSequenceName ($seq_name);
// Delete the sequence entry
$data = $this->simpleQuery(array ($data, 'action'=> 'delete'));
* Generate error message for LDAP errors.
* @param int $errno error number
* @return mixed DB_OK on success or DB error on error
$errno = $this->errorCode (ldap_errno ($this->connection));
if ($this->last_param['action'] !== null ) {
return $this->raiseError ($errno, null , null ,
sprintf('%s base="%s" filter="%s"',
$this->last_param['action'] ? $this->last_param['action'] : $this->param['action'],
$this->last_param['base_dn'] ? $this->last_param['base_dn'] : $this->param['base_dn'],
is_array($this->last_query) ? "" : $this->last_query
$errno == @ldap_error ($this->connection)
return $this->raiseError ($errno, null , null , "???",
@ldap_error ($this->connection));
* Prepares a query for multiple execution with execute().
* This behaviour is emulated for LDAP backend.
* prepare() requires a generic query as an array with special
* characters (wildcards) as values.
* ? - a quoted scalar value, i.e. strings, integers
* & - requires a file name, the content of the file
* insert into the query (i.e. saving binary data
* ! - value is inserted 'as is'
* $sigfile = "/home/dexter/.signature";
* $res = $dbh->execute($sth, array(
* 'cn=Piotr Roszatycki,dc=example,dc=com',
* array('top', 'person'),
* 'Piotr Roszatycki', 'Roszatycki', $sigfile
* @param mixed the query to prepare
* @return resource handle for the query
return parent ::prepare ($query);
} elseif (is_array($query) && isset ($query[0 ]) &&
!$this->isManip(isset ($query['action']) ? $query['action'] : $this->param['action'])
for ($i = 0; $i < strlen($filter); $i++ ) {
$types[$token++ ] = DB_PARAM_SCALAR;
$types[$token++ ] = DB_PARAM_OPAQUE;
$types[$token++ ] = DB_PARAM_MISC;
$this->prepare_tokens[] = &$tokens;
end($this->prepare_tokens);
$k = key($this->prepare_tokens);
$this->prepare_types[$k] = $types;
$this->prepared_queries[$k] = &$query;
foreach ($query[0 ] as $k=> $v) {
$types[$k] = DB_PARAM_SCALAR;
$types[$k] = DB_PARAM_OPAQUE;
$types[$k] = DB_PARAM_MISC;
$this->prepare_tokens[] = &$tokens;
end($this->prepare_tokens);
$k = key($this->prepare_tokens);
$this->prepare_types[$k] = $types;
$this->prepared_queries[$k] = &$query;
return parent ::prepare ($query);
// {{{ executeEmulateQuery()
* Emulates the execute statement.
* @param resource $stmt query handle from prepare()
* @param array $data numeric array containing the
* data to insert into the query
* @return mixed an array containing the real query run when emulating
* prepare/execute. A DB error code is returned on failure.
$query = &$this->prepared_queries[$stmt];
return parent ::executeEmulateQuery ($stmt, $data);
} elseif (is_array($query) && isset ($query[0 ]) &&
!$this->isManip(isset ($query['action']) ? $query['action'] : $this->param['action'])
$p = &$this->prepare_tokens;
if (!isset ($this->prepare_tokens[$stmt]) ||
!is_array($this->prepare_tokens[$stmt]) ||
!sizeof($this->prepare_tokens[$stmt]))
return $this->raiseError (DB_ERROR_INVALID );
$qq = &$this->prepare_tokens[$stmt];
if ((!$data && $qp > 0 ) ||
$this->last_query = $this->prepared_queries[$stmt];
return $this->raiseError (DB_ERROR_NEED_MORE_DATA );
for ($i = 0; $i < $qp; $i++ ) {
$type = $this->prepare_types[$stmt][$i];
if ($type == DB_PARAM_OPAQUE ) {
$fp = fopen($data[$i], 'r');
while (($buf = fread($fp, 4096 )) != false ) {
$realquery[0 ] .= ($type != DB_PARAM_MISC ) ? $this->quote($pdata) : $pdata;
$realquery[0 ] .= $qq[$i + 1 ];
$p = &$this->prepare_tokens;
if (!isset ($this->prepare_tokens[$stmt]) ||
!is_array($this->prepare_tokens[$stmt]) ||
!sizeof($this->prepare_tokens[$stmt]))
return $this->raiseError (DB_ERROR_INVALID );
$qq = &$this->prepare_tokens[$stmt];
foreach ($qq as $k=> $v) {
$type = $this->prepare_types[$stmt][$k];
$this->last_query = $this->prepared_queries[$stmt];
return $this->raiseError (DB_ERROR_NEED_MORE_DATA );
if ($type == DB_PARAM_OPAQUE ) {
$fp = fopen($data[$i++ ], 'r');
while (($buf = fread($fp, 4096 )) != false ) {
} elseif ($type !== null ) {
$realquery[0 ][$k] = $pdata;
return parent ::executeEmulateQuery ($stmt, $data);
* Sets the default parameters for query.
* @param string $param the name of parameter for search actions (action,
* base_dn, attributes, attrsonly, sizelimit, timelimit, deref) or
* modify actions (action, attribute, value, newrdn, newparent,
* @param string $value the value of parameter
* @return mixed DB_OK on success or DB error on error
if (isset ($this->param[$param])) {
$this->param[$param] = $value;
return $this->raiseError (" unknown LDAP parameter $param" );
* Gets the default parameters for query.
* @param string $param the name of parameter for search or modify
* @return mixed value of parameter on success or DB error on error
if (isset ($this->param[$param])) {
return $this->param[$param];
return $this->raiseError (" unknown LDAP parameter $param" );
* Sets the value of the given option.
* @param int $option the specified option
* @param mixed $newval the value of specified option
* @return bool DB_OK on success or DB error on error
if (@ldap_set_option ($this->connection, $option, $newval)) {
return $this->raiseError ("failed to set LDAP option");
* Gets the current value for given option.
* @param int $option the specified option
* @param mixed $retval (reference) the new value of specified option
* @return bool DB_OK on success or DB error on error
if (@ldap_get_option ($this->connection, $option, $retval)) {
return $this->raiseError ("failed to get LDAP option");
* Splits the DN and breaks it up into its component parts.
* Each part is known as Relative Distinguished Name, or RDN.
* @param string $dn the DN to split
* @param int $with_attrib 0 to get RDNs with the attributes
* or 1 to get only values.
* @return array an array of all those components
$arr = ldap_explode_dn ($dn, $with_attrib ? 1 : 0 );
Documentation generated on Mon, 11 Mar 2019 15:41:27 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.
|