{
try {
$filter = $this->_criteria->convert($this);
- $result = $filter->asString();
- return $result;
+ return (string)$filter;
} catch (Horde_Kolab_Server_Exception $e) {
return '';
}
public function __construct($config = array())
{
$this->setConfig($config);
+ $this->bind();
}
/**
* If the above call failed, we try an v2 bind here and set the
* version afterwards (with checking to the rootDSE). */
try {
- $msg = $this->bind();
+ $this->bind();
} catch (Exception $e) {
/* The bind failed, discard link and save error msg.
* Then record the host as down and try next one. */
public function add(Horde_Ldap_Entry $entry)
{
/* Rebind as the write DN. */
- if (!empty($this->writedn)) {
- $this->bind($this->writedn, $this->writepw);
+ if (!empty($this->_config['writedn'])) {
+ $this->bind($this->_config['writedn'], $this->_config['writepw']);
}
/* Continue attempting the add operation in a loop until we get a
if (@ldap_add($link, $entry->dn(), $entry->getValues())) {
/* Entry successfully added, we should update its Horde_Ldap
* reference in case it is not set so far (fresh entry). */
- if (!($entry->getLDAP() instanceof Horde_Ldap)) {
+ try {
+ $entry->getLDAP();
+ } catch (Horde_Ldap_Exception $e) {
$entry->setLDAP($this);
}
/* Store that the entry is present inside the directory. */
/* We have a failure. What kind? We may be able to reconnect and
* try again. */
$error_code = @ldap_errno($link);
- $error_name = $this->errorMessage($error_code);
-
- if ($error_name != 'LDAP_OPERATIONS_ERROR' |
+ if ($this->errorName($error_code) != 'LDAP_OPERATIONS_ERROR' |
!$this->_config['auto_reconnect']) {
/* Errors other than the above are just passed back to the user
* so he may react upon them. */
- throw new Horde_Ldap_Exception('Could not add entry ' . $entry->dn() . ' ' . $error_name, $error_code);
+ throw new Horde_Ldap_Exception('Could not add entry ' . $entry->dn() . ': ' . ldap_err2str($error_code), $error_code);
}
/* The server has disconnected before trying the operation. We
}
/* Re-bind as the write DN if not using searchdn credentials. */
- if (!empty($this->writedn)) {
- $this->bind($this->writedn, $this->writepw);
+ if (!empty($this->_config['writedn'])) {
+ $this->bind($this->_config['writedn'], $this->_config['writepw']);
}
/* Recursive delete searches for children and calls delete for them. */
if ($recursive) {
$result = @ldap_list($this->_link, $dn, '(objectClass=*)', array(null), 0, 0);
- if (@ldap_count_entries($this->_link, $result)) {
+ if ($result && @ldap_count_entries($this->_link, $result)) {
for ($subentry = @ldap_first_entry($this->_link, $result);
- $subentry = @ldap_next_entry($this->_link, $subentry);) {
+ $subentry;
+ $subentry = @ldap_next_entry($this->_link, $subentry)) {
$this->delete(@ldap_get_dn($this->_link, $subentry), true);
}
}
throw new Horde_Ldap_Exception('Could not add entry ' . $dn . ' no valid LDAP connection could be found.');
}
- if (@ldap_delete($link, $dn)) {
+ $s = @ldap_delete($link, $dn);
+ if ($s) {
/* Entry successfully deleted. */
return;
}
/* We have a failure. What kind? We may be able to reconnect and
* try again. */
$error_code = @ldap_errno($link);
- $error_name = $this->errorMessage($error_code);
- if ($this->errorMessage($error_code) == 'LDAP_OPERATIONS_ERROR' &&
+ if ($this->errorName($error_code) == 'LDAP_OPERATIONS_ERROR' &&
$this->_config['auto_reconnect']) {
/* The server has disconnected before trying the operation. We
* should try again, possibly with a different server. */
$this->_link = false;
$this->_reconnect();
- } elseif ($error_code == 66) {
+ } elseif ($this->errorName($error_code) == 'LDAP_NOT_ALLOWED_ON_NONLEAF') {
/* Subentries present, server refused to delete.
* Deleting subentries is the clients responsibility, but since
* the user may not know of the subentries, we do not force
* that here but instead notify the developer so he may take
* actions himself. */
- throw new Horde_Ldap_Exception('Could not delete entry ' . $dn . ' because of subentries. Use the recursive parameter to delete them.');
+ throw new Horde_Ldap_Exception('Could not delete entry ' . $dn . ' because of subentries. Use the recursive parameter to delete them.', $error_code);
} else {
/* Errors other than the above catched are just passed back to
* the user so he may react upon them. */
- throw new Horde_Ldap_Exception('Could not delete entry ' . $dn . ' ' . $error_name, $error_code);
+ throw new Horde_Ldap_Exception('Could not delete entry ' . $dn . ': ' . ldap_err2str($error_code), $error_code);
}
}
}
public function modify($entry, $parms = array())
{
/* Re-bind as the write DN. */
- if (!empty($this->writedn)) {
- $this->bind($this->writedn, $this->writepw);
+ if (!empty($this->_config['writedn'])) {
+ $this->bind($this->_config['writedn'], $this->_config['writepw']);
}
if (is_string($entry)) {
/* We have a failure. What kind? We may be able to
* reconnect and try again. */
$error_code = $e->getCode();
- $error_name = $this->errorMessage($error_code);
+ $error_name = $this->errorName($error_code);
- if ($this->errorMessage($error_code) != 'LDAP_OPERATIONS_ERROR' ||
+ if ($this->errorName($error_code) != 'LDAP_OPERATIONS_ERROR' ||
!$this->_config['auto_reconnect']) {
/* Errors other than the above catched are just passed
* back to the user so he may react upon them. */
}
if ($filter instanceof Horde_Ldap_Filter) {
/* Convert Horde_Ldap_Filter to string representation. */
- $filter = $filter->asString();
+ $filter = (string)$filter;
}
/* Setting search parameters. */
$sizelimit,
$timelimit);
- if ($err = @ldap_errno($link)) {
- if ($err == 32) {
- /* Errorcode 32 = no such object, i.e. a nullresult. */
- return new Horde_Ldap_Search ($search, $this, $attributes);
+ if ($errno = @ldap_errno($link)) {
+ $err = $this->errorName($errno);
+ if ($err == 'LDAP_NO_SUCH_OBJECT' ||
+ $err == 'LDAP_SIZELIMIT_EXCEEDED') {
+ return new Horde_Ldap_Search($search, $this, $attributes);
}
- if ($err == 4) {
- /* Errorcode 4 = sizelimit exeeded. */
- return new Horde_Ldap_Search ($search, $this, $attributes);
- }
- if ($err == 87) {
+ if ($err == 'LDAP_FILTER_ERROR') {
/* Bad search filter. */
- throw new Horde_Ldap_Exception($this->errorMessage($err) . '($filter)', $err);
+ throw new Horde_Ldap_Exception(ldap_err2str($errno) . ' ($filter)', $errno);
}
- if (($err == 1) && ($this->_config['auto_reconnect'])) {
- /* Errorcode 1 = LDAP_OPERATIONS_ERROR but we can try a
- * reconnect. */
+ if ($err == 'LDAP_OPERATIONS_ERROR' &&
+ $this->_config['auto_reconnect']) {
$this->_link = false;
$this->_reconnect();
} else {
$msg = "\nParameters:\nBase: $base\nFilter: $filter\nScope: $scope";
- throw new Horde_Ldap_Exception($this->errorMessage($err) . $msg, $err);
+ throw new Horde_Ldap_Exception(ldap_err2str($errno) . $msg, $errno);
}
} else {
return new Horde_Ldap_Search($search, $this, $attributes);
}
$err = @ldap_errno($this->_link);
if ($err) {
- $msg = @ldap_err2str($err);
- } else {
- $err = 1000;
- $msg = $this->errorMessage($err);
+ throw new Horde_Ldap_Exception(ldap_err2str($err), $err);
}
- throw new Horde_Ldap_Exception($msg, $err);
+ throw new Horde_Ldap_Exception('Unknown error');
}
/**
}
$err = @ldap_errno($this->_link);
if ($err) {
- $msg = @ldap_err2str($err);
- } else {
- $err = 1000;
- $msg = $this->errorMessage($err);
+ throw new Horde_Ldap_Exception(ldap_err2str($err), $err);
}
- throw new Horde_Ldap_Exception($msg, $err);
+ throw new Horde_Ldap_Exception('Unknown error');
}
/**
/* Check to see if the server supports this version first.
*
- * TODO: Why is this so horribly slow? $this->rootDSE() is
- * very fast, as well as Horde_Ldap_RootDSE::fetch() seems
- * like a problem at copiyng the object inside PHP??
- * Additionally, this is not always reproducable... */
+ * TODO: Why is this so horribly slow? $this->rootDSE() is very fast,
+ * as well as Horde_Ldap_RootDse(). Seems like a problem at copying the
+ * object inside PHP?? Additionally, this is not always
+ * reproducable... */
if (!$force) {
$rootDSE = $this->rootDSE();
$supported_versions = $rootDSE->getValue('supportedLDAPVersion');
}
$check_ok = in_array($version, $supported_versions);
}
+ $check_ok = true;
if ($force || $check_ok) {
return $this->setOption('LDAP_OPT_PROTOCOL_VERSION', $version);
if (@ldap_count_entries($this->_link, $result)) {
return true;
}
- if (ldap_errno($this->_link) == 32) {
+ if ($this->errorName(@ldap_errno($this->_link)) == 'LDAP_NO_SUCH_OBJECT') {
return false;
}
- if (ldap_errno($this->_link) != 0) {
- throw new Horde_Ldap_Exception(ldap_error($this->_link), ldap_errno($this->_link));
+ if (@ldap_errno($this->_link)) {
+ throw new Horde_Ldap_Exception(@ldap_error($this->_link), @ldap_errno($this->_link));
}
return false;
}
*
* @return string The description for the error.
*/
- public function errorMessage($errorcode)
+ public static function errorName($errorcode)
{
$errorMessages = array(
0x00 => 'LDAP_SUCCESS',
*
* @param array $attrs Array of attributes to search for.
*
- * @return Horde_Ldap_RootDSE Horde_Ldap_RootDSE object
+ * @return Horde_Ldap_RootDse Horde_Ldap_RootDse object
* @throws Horde_Ldap_Exception
*/
public function rootDSE(array $attrs = array())
/* See if we need to fetch a fresh object, or if we already
* requested this object with the same attributes. */
if (!isset($this->_rootDSECache[$attrs_signature])) {
- $this->_rootDSECache[$attrs_signature] = Horde_Ldap_RootDSE::fetch($this, $attrs);
+ $this->_rootDSECache[$attrs_signature] = new Horde_Ldap_RootDse($this, $attrs);
}
return $this->_rootDSECache[$attrs_signature];
/* Store a temporary error message so subsequent calls to
* schema() can detect that we are fetching the schema
* already. Otherwise we will get an infinite loop at
- * Horde_Ldap_Schema::fetch(). */
+ * Horde_Ldap_Schema. */
$this->_schema = new Horde_Ldap_Exception('Schema not initialized');
- $this->_schema = Horde_Ldap_Schema::fetch($this, $dn);
+ $this->_schema = new Horde_Ldap_Schema($this, $dn);
/* If schema caching is active, advise the cache to store
* the schema. */
is_resource($this->_link)) {
/* Fetch schema. */
if ($this->_ldap instanceof Horde_Ldap) {
- $schema = $this->_ldap->schema();
+ try {
+ $schema = $this->_ldap->schema();
+ } catch (Horde_Ldap_Exception $e) {
+ $schema = null;
+ }
}
/* Fetch attributes. */
{
/* Ensure we have a valid LDAP object. */
$ldap = $this->getLDAP();
- if (!($ldap instanceof Horde_Ldap)) {
- throw new Horde_Ldap_Exception('The entries LDAP object is not valid');
- }
/* Get and check link. */
$link = $ldap->getLink();
public function getLDAP()
{
if (!($this->_ldap instanceof Horde_Ldap)) {
- throw new Horde_Ldap_Exception('LDAP is not a valid Horde_Ldap object');
+ throw new Horde_Ldap_Exception('ldap property is not a valid Horde_Ldap object');
}
return $this->_ldap;
}
<?php
/**
- * File containing the Horde_Ldap_Filter interface class.
+ * Object representation of a part of a LDAP filter.
*
- * PHP version 5
+ * The purpose of this class is to easily build LDAP filters without having to
+ * worry about correct escaping etc.
*
- * @category Net
- * @package Horde_Ldap
- * @author Benedikt Hallinger <beni@php.net>
- * @author Ben Klang <ben@alkaloid.net>
- * @copyright 2009 Benedikt Hallinger, The Horde Project
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
- */
-
-/**
- * Includes
- */
-#require_once 'PEAR.php';
-#require_once 'Util.php';
-
-/**
- * Object representation of a part of a LDAP filter.
+ * A filter is built using several independent filter objects which are
+ * combined afterwards. This object works in two modes, depending how the
+ * object is created.
*
- * This Class is not completely compatible to the PERL interface!
+ * If the object is created using the {@link create()} method, then this is a
+ * leaf-object. If the object is created using the {@link combine()} method,
+ * then this is a container object.
*
- * The purpose of this class is, that users can easily build LDAP filters
- * without having to worry about right escaping etc.
- * A Filter is built using several independent filter objects
- * which are combined afterwards. This object works in two
- * modes, depending how the object is created.
- * If the object is created using the {@link create()} method, then this is a leaf-object.
- * If the object is created using the {@link combine()} method, then this is a container object.
+ * LDAP filters are defined in RFC 2254.
*
- * LDAP filters are defined in RFC-2254 and can be found under
- * {@link http://www.ietf.org/rfc/rfc2254.txt}
+ * @see http://www.ietf.org/rfc/rfc2254.txt
*
- * Here a quick copy&paste example:
+ * A short example:
* <code>
- * $filter0 = Horde_Ldap_Filter::create('stars', 'equals', '***');
+ * $filter0 = Horde_Ldap_Filter::create('stars', 'equals', '***');
* $filter_not0 = Horde_Ldap_Filter::combine('not', $filter0);
*
- * $filter1 = Horde_Ldap_Filter::create('gn', 'begins', 'bar');
- * $filter2 = Horde_Ldap_Filter::create('gn', 'ends', 'baz');
- * $filter_comp = Horde_Ldap_Filter::combine('or',array($filter_not0, $filter1, $filter2));
+ * $filter1 = Horde_Ldap_Filter::create('gn', 'begins', 'bar');
+ * $filter2 = Horde_Ldap_Filter::create('gn', 'ends', 'baz');
+ * $filter_comp = Horde_Ldap_Filter::combine('or', array($filter_not0, $filter1, $filter2));
*
- * echo $filter_comp->asString();
- * // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar *)(gn= *baz))
+ * echo (string)$filter_comp;
+ * // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar*)(gn=*baz))
* // The stars in $filter0 are treaten as real stars unless you disable escaping.
* </code>
*
- * @category Net
- * @package Horde_Ldap
- * @author Benedikt Hallinger <beni@php.net>
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- * @link http://pear.php.net/package/Horde_Ldap/
+ * @category Horde
+ * @package Ldap
+ * @author Benedikt Hallinger <beni@php.net>
+ * @author Jan Schneider <jan@horde.org>
+ * @copyright 2009 Benedikt Hallinger
+ * @copyright 2010 The Horde Project
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
-class Horde_Ldap_Filter extends PEAR
+class Horde_Ldap_Filter
{
/**
- * Storage for combination of filters
+ * Storage for combination of filters.
*
- * This variable holds a array of filter objects
- * that should be combined by this filter object.
+ * This variable holds a array of filter objects that should be combined by
+ * this filter object.
*
- * @access protected
* @var array
*/
- protected $_subfilters = array();
+ protected $_filters = array();
/**
- * Match of this filter
+ * Operator for sub-filters.
*
- * If this is a leaf filter, then a matching rule is stored,
- * if it is a container, then it is a logical operator
- *
- * @access protected
* @var string
*/
- protected $_match;
+ protected $_operator;
/**
- * Single filter
+ * Single filter.
*
- * If we operate in leaf filter mode,
- * then the constructing method stores
- * the filter representation here
+ * If this is a leaf filter, the filter representation is store here.
*
- * @acces private
* @var string
*/
protected $_filter;
/**
- * Create a new Horde_Ldap_Filter object and parse $filter.
+ * Constructor.
*
- * This is for PERL Net::LDAP interface.
* Construction of Horde_Ldap_Filter objects should happen through either
* {@link create()} or {@link combine()} which give you more control.
- * However, you may use the perl iterface if you already have generated filters.
- *
- * @param string $filter LDAP filter string
+ * However, you may use the constructor if you already have generated
+ * filters.
*
- * @see parse()
+ * @param array $params List of object parameters
*/
- public function __construct($filter = false)
+ protected function __construct(array $params)
{
- // The optional parameter must remain here, because otherwise create() crashes
- if (false !== $filter) {
- $filter_o = self::parse($filter);
- if (PEAR::isError($filter_o)) {
- $this->_filter = $filter_o; // assign error, so asString() can report it
- } else {
- $this->_filter = $filter_o->asString();
+ foreach ($params as $param => $value) {
+ if (in_array($param, array('filter', 'filters', 'operator'))) {
+ $this->{'_' . $param} = $value;
}
}
}
/**
- * Constructor of a new part of a LDAP filter.
+ * Creates a new part of an LDAP filter.
*
* The following matching rules exists:
- * - equals: One of the attributes values is exactly $value
- * Please note that case sensitiviness is depends on the
- * attributes syntax configured in the server.
- * - begins: One of the attributes values must begin with $value
- * - ends: One of the attributes values must end with $value
- * - contains: One of the attributes values must contain $value
- * - present | any: The attribute can contain any value but must be existent
- * - greater: The attributes value is greater than $value
- * - less: The attributes value is less than $value
- * - greaterOrEqual: The attributes value is greater or equal than $value
- * - lessOrEqual: The attributes value is less or equal than $value
- * - approx: One of the attributes values is similar to $value
- *
- * If $escape is set to true (default) then $value will be escaped
- * properly. If it is set to false then $value will be treaten as raw filter value string.
- * You should escape yourself using {@link Horde_Ldap_Util::escape_filter_value()}!
+ * - equals: One of the attributes values is exactly $value.
+ * Please note that case sensitiviness depends on the
+ * attributes syntax configured in the server.
+ * - begins: One of the attributes values must begin with $value.
+ * - ends: One of the attributes values must end with $value.
+ * - contains: One of the attributes values must contain $value.
+ * - present | any: The attribute can contain any value but must exist.
+ * - greater: The attributes value is greater than $value.
+ * - less: The attributes value is less than $value.
+ * - greaterOrEqual: The attributes value is greater or equal than $value.
+ * - lessOrEqual: The attributes value is less or equal than $value.
+ * - approx: One of the attributes values is similar to $value.
+ *
+ * If $escape is set to true then $value will be escaped. If set to false
+ * then $value will be treaten as a raw filter value string. You should
+ * then escape it yourself using {@link
+ * Horde_Ldap_Util::escape_filter_value()}.
*
* Examples:
* <code>
- * // This will find entries that contain an attribute "sn" that ends with "foobar":
- * $filter = new Horde_Ldap_Filter('sn', 'ends', 'foobar');
+ * // This will find entries that contain an attribute "sn" that ends with
+ * // "foobar":
+ * $filter = Horde_Ldap_Filter::create('sn', 'ends', 'foobar');
*
- * // This will find entries that contain an attribute "sn" that has any value set:
- * $filter = new Horde_Ldap_Filter('sn', 'any');
+ * // This will find entries that contain an attribute "sn" that has any
+ * // value set:
+ * $filter = Horde_Ldap_Filter::create('sn', 'any');
* </code>
*
- * @param string $attr_name Name of the attribute the filter should apply to
- * @param string $match Matching rule (equals, begins, ends, contains, greater, less, greaterOrEqual, lessOrEqual, approx, any)
- * @param string $value (optional) if given, then this is used as a filter
- * @param boolean $escape Should $value be escaped? (default: yes, see {@link Horde_Ldap_Util::escape_filter_value()} for detailed information)
+ * @param string $attribute Name of the attribute the filter should apply
+ * to.
+ * @param string $match Matching rule (equals, begins, ends, contains,
+ * greater, less, greaterOrEqual, lessOrEqual,
+ * approx, any).
+ * @param string $value If given, then this is used as a filter value.
+ * @param boolean $escape Should $value be escaped?
*
- * @return Horde_Ldap_Filter|Horde_Ldap_Error
+ * @return Horde_Ldap_Filter
+ * @throws Horde_Ldap_Exception
*/
- public static function &create($attr_name, $match, $value = '', $escape = true)
+ public static function create($attribute, $match, $value = '',
+ $escape = true)
{
- $leaf_filter = new Horde_Ldap_Filter();
if ($escape) {
$array = Horde_Ldap_Util::escape_filter_value(array($value));
$value = $array[0];
}
- switch (strtolower($match)) {
+
+ switch (Horde_String::lower($match)) {
case 'equals':
- $leaf_filter->_filter = '(' . $attr_name . '=' . $value . ')';
+ $filter = '(' . $attribute . '=' . $value . ')';
break;
case 'begins':
- $leaf_filter->_filter = '(' . $attr_name . '=' . $value . '*)';
+ $filter = '(' . $attribute . '=' . $value . '*)';
break;
case 'ends':
- $leaf_filter->_filter = '(' . $attr_name . '=*' . $value . ')';
+ $filter = '(' . $attribute . '=*' . $value . ')';
break;
case 'contains':
- $leaf_filter->_filter = '(' . $attr_name . '=*' . $value . '*)';
+ $filter = '(' . $attribute . '=*' . $value . '*)';
break;
case 'greater':
- $leaf_filter->_filter = '(' . $attr_name . '>' . $value . ')';
+ $filter = '(' . $attribute . '>' . $value . ')';
break;
case 'less':
- $leaf_filter->_filter = '(' . $attr_name . '<' . $value . ')';
+ $filter = '(' . $attribute . '<' . $value . ')';
break;
case 'greaterorequal':
case '>=':
- $leaf_filter->_filter = '(' . $attr_name . '>=' . $value . ')';
+ $filter = '(' . $attribute . '>=' . $value . ')';
break;
case 'lessorequal':
case '<=':
- $leaf_filter->_filter = '(' . $attr_name . '<=' . $value . ')';
+ $filter = '(' . $attribute . '<=' . $value . ')';
break;
case 'approx':
case '~=':
- $leaf_filter->_filter = '(' . $attr_name . '~=' . $value . ')';
+ $filter = '(' . $attribute . '~=' . $value . ')';
break;
case 'any':
- case 'present': // alias that may improve user code readability
- $leaf_filter->_filter = '(' . $attr_name . '=*)';
+ case 'present':
+ $filter = '(' . $attribute . '=*)';
break;
default:
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter create error: matching rule "' . $match . '" not known!');
+ throw new Horde_Ldap_Exception('Matching rule "' . $match . '" unknown');
}
- return $leaf_filter;
+
+ return new Horde_Ldap_Filter(array('filter' => $filter));
+
}
/**
- * Combine two or more filter objects using a logical operator
+ * Combines two or more filter objects using a logical operator.
+ *
+ * Example:
+ * <code>
+ * $filter = Horde_Ldap_Filter::combine('or', array($filter1, $filter2));
+ * </code>
*
- * This static method combines two or more filter objects and returns one single
- * filter object that contains all the others.
- * Call this method statically: $filter = Horde_Ldap_Filter('or', array($filter1, $filter2))
- * If the array contains filter strings instead of filter objects, we will try to parse them.
+ * If the array contains filter strings instead of filter objects, they
+ * will be parsed.
*
- * @param string $log_op The locicall operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!"
- * @param array|Horde_Ldap_Filter $filters array with Horde_Ldap_Filter objects
+ * @param string $operator
+ * The logical operator, either "and", "or", "not" or the logical
+ * equivalents "&", "|", "!".
+ * @param array|Horde_Ldap_Filter|string $filters
+ * Array with Horde_Ldap_Filter objects and/or strings or a single
+ * filter when using the "not" operator.
*
- * @return Horde_Ldap_Filter|Horde_Ldap_Error
- * @static
+ * @return Horde_Ldap_Filter
+ * @throws Horde_Ldap_Exception
*/
- public static function &combine($log_op, $filters)
+ public static function combine($operator, $filters)
{
- // substitude named operators to logical operators
- if ($log_op == 'and') $log_op = '&';
- if ($log_op == 'or') $log_op = '|';
- if ($log_op == 'not') $log_op = '!';
+ // Substitute named operators with logical operators.
+ switch ($operator) {
+ case 'and': $operator = '&'; break;
+ case 'or': $operator = '|'; break;
+ case 'not': $operator = '!'; break;
+ }
- // tests for sane operation
- if ($log_op == '!') {
- // Not-combination, here we only accept one filter object or filter string
+ // Tests for sane operation.
+ switch ($operator) {
+ case '!':
+ // Not-combination, here we only accept one filter object or filter
+ // string.
if ($filters instanceof Horde_Ldap_Filter) {
$filters = array($filters); // force array
} elseif (is_string($filters)) {
- try {
- $filter_o = self::parse($filters);
- } catch (Exception $e) {
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter combine error: '.$e->getMessage());
- }
- $filters = array($filter_o);
+ $filters = array(self::parse($filters));
} elseif (is_array($filters)) {
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter combine error: operator is "not" but $filter is an array!');
+ throw new Horde_Ldap_Exception('Operator is "not" but $filter is an array');
} else {
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter combine error: operator is "not" but $filter is not a valid Horde_Ldap_Filter nor a filter string!');
+ throw new Horde_Ldap_Exception('Operator is "not" but $filter is not a valid Horde_Ldap_Filter nor a filter string');
}
- } elseif ($log_op == '&' || $log_op == '|') {
+ break;
+
+ case '&':
+ case '|':
if (!is_array($filters) || count($filters) < 2) {
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter combine error: parameter $filters is not an array or contains less than two Horde_Ldap_Filter objects!');
+ throw new Horde_Ldap_Exception('Parameter $filters is not an array or contains less than two Horde_Ldap_Filter objects');
}
- } else {
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter combine error: logical operator is not known!');
+ break;
+
+ default:
+ throw new Horde_Ldap_Exception('Logical operator is unknown');
}
- $combined_filter = new Horde_Ldap_Filter();
- foreach ($filters as $key => $testfilter) { // check for errors
+ foreach ($filters as $key => $testfilter) {
+ // Check for errors.
if (is_string($testfilter)) {
- // string found, try to parse into an filter object
- $filter_o = self::parse($testfilter);
- $filters[$key] = $filter_o;
- } elseif (!$testfilter instanceof Horde_Ldap_Filter) {
- throw new Horde_Ldap_Exception('Horde_Ldap_Filter combine error: invalid object passed in array $filters!');
+ // String found, try to parse into an filter object.
+ $filters[$key] = self::parse($testfilter);
+ } elseif (!($testfilter instanceof Horde_Ldap_Filter)) {
+ throw new Horde_Ldap_Exception('Invalid object passed in array $filters!');
}
}
- $combined_filter->_subfilters = $filters;
- $combined_filter->_match = $log_op;
- return $combined_filter;
+ return new Horde_Ldap_Filter(array('filters' => $filters,
+ 'operator' => $operator));
}
/**
- * Parse FILTER into a Horde_Ldap_Filter object
+ * Parses a string into a Horde_Ldap_Filter object.
*
- * This parses an filter string into Horde_Ldap_Filter objects.
+ * @todo Leaf-mode: Do we need to escape at all? what about *-chars? Check
+ * for the need of encoding values, tackle problems (see code comments).
*
- * @param string $FILTER The filter string
+ * @param string $filter An LDAP filter string.
*
- * @access static
- * @return Horde_Ldap_Filter|Horde_Ldap_Error
- * @todo Leaf-mode: Do we need to escape at all? what about *-chars?check for the need of encoding values, tackle problems (see code comments)
+ * @return Horde_Ldap_Filter
+ * @throws Horde_Ldap_Exception
*/
- public static function parse($FILTER)
+ public static function parse($filter)
{
- if (preg_match('/^\((.+?)\)$/', $FILTER, $matches)) {
- if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) {
- // Subfilter processing: pass subfilters to parse() and combine
- // the objects using the logical operator detected
- // we have now something like "&(...)(...)(...)" but at least one part ("!(...)").
- // Each subfilter could be an arbitary complex subfilter.
-
- // extract logical operator and filter arguments
- $log_op = substr($matches[1], 0, 1);
- $remaining_component = substr($matches[1], 1);
-
- // split $remaining_component into individual subfilters
- // we cannot use split() for this, because we do not know the
- // complexiness of the subfilter. Thus, we look trough the filter
- // string and just recognize ending filters at the first level.
- // We record the index number of the char and use that information
- // later to split the string.
- $sub_index_pos = array();
- $prev_char = ''; // previous character looked at
- $level = 0; // denotes the current bracket level we are,
- // >1 is too deep, 1 is ok, 0 is outside any
- // subcomponent
- for ($curpos = 0; $curpos < strlen($remaining_component); $curpos++) {
- $cur_char = substr($remaining_component, $curpos, 1);
-
- // rise/lower bracket level
- if ($cur_char == '(' && $prev_char != '\\') {
- $level++;
- } elseif ($cur_char == ')' && $prev_char != '\\') {
- $level--;
- }
-
- if ($cur_char == '(' && $prev_char == ')' && $level == 1) {
- array_push($sub_index_pos, $curpos); // mark the position for splitting
- }
- $prev_char = $cur_char;
- }
-
- // now perform the splits. To get also the last part, we
- // need to add the "END" index to the split array
- array_push($sub_index_pos, strlen($remaining_component));
- $subfilters = array();
- $oldpos = 0;
- foreach ($sub_index_pos as $s_pos) {
- $str_part = substr($remaining_component, $oldpos, $s_pos - $oldpos);
- array_push($subfilters, $str_part);
- $oldpos = $s_pos;
- }
-
- // some error checking...
- if (count($subfilters) == 1) {
- // only one subfilter found
- } elseif (count($subfilters) > 1) {
- // several subfilters found
- if ($log_op == "!") {
- throw new Horde_Ldap_Exception("Filter parsing error: invalid filter syntax - NOT operator detected but several arguments given!");
- }
- } else {
- // this should not happen unless the user specified a wrong filter
- throw new Horde_Ldap_Exception("Filter parsing error: invalid filter syntax - got operator '$log_op' but no argument!");
- }
-
- // Now parse the subfilters into objects and combine them using the operator
- $subfilters_o = array();
- foreach ($subfilters as $s_s) {
- $o = self::parse($s_s);
- array_push($subfilters_o, self::parse($s_s));
- }
-
- $filter_o = self::combine($log_op, $subfilters_o);
- return $filter_o;
-
- } else {
- // This is one leaf filter component, do some syntax checks, then escape and build filter_o
- // $matches[1] should be now something like "foo=bar"
+ if (!preg_match('/^\((.+?)\)$/', $filter, $matches)) {
+ throw new Horde_Ldap_Exception('Invalid filter syntax, filter components must be enclosed in round brackets');
+ }
- // detect multiple leaf components
- // [TODO] Maybe this will make problems with filters containing brackets inside the value
- if (stristr($matches[1], ')(')) {
- throw new Horde_Ldap_Exception("Filter parsing error: invalid filter syntax - multiple leaf components detected!");
- } else {
- $filter_parts = preg_split('/(?<!\\\\)(=|=~|>|<|>=|<=)/', $matches[1], 2, PREG_SPLIT_DELIM_CAPTURE);
- if (count($filter_parts) != 3) {
- throw new Horde_Ldap_Exception("Filter parsing error: invalid filter syntax - unknown matching rule used");
- } else {
- $filter_o = new Horde_Ldap_Filter();
- // [TODO]: Do we need to escape at all? what about *-chars user provide and that should remain special?
- // I think, those prevent escaping! We need to check against PERL Net::LDAP!
- // $value_arr = Horde_Ldap_Util::escape_filter_value(array($filter_parts[2]));
- // $value = $value_arr[0];
- $value = $filter_parts[2];
- $filter_o->_filter = '('.$filter_parts[0].$filter_parts[1].$value.')';
- return $filter_o;
- }
- }
- }
+ if (in_array(substr($matches[1], 0, 1), array('!', '|', '&'))) {
+ return self::_parseCombination($matches[1]);
} else {
- // ERROR: Filter components must be enclosed in round brackets
- throw new Horde_Ldap_Exception("Filter parsing error: invalid filter syntax - filter components must be enclosed in round brackets");
+ return self::_parseLeaf($matches[1]);
}
}
/**
- * Get the string representation of this filter
+ * Parses combined subfilter strings.
*
- * This method runs through all filter objects and creates
- * the string representation of the filter. If this
- * filter object is a leaf filter, then it will return
- * the string representation of this filter.
+ * Passes subfilters to parse() and combines the objects using the logical
+ * operator detected. Each subfilter could be an arbitary complex
+ * subfilter.
*
- * @return string|Horde_Ldap_Error
+ * @param string $filter An LDAP filter string.
+ *
+ * @return Horde_Ldap_Filter
+ * @throws Horde_Ldap_Exception
*/
- public function asString()
+ protected static function _parseCombination($filter)
{
- if ($this->isLeaf()) {
- $return = $this->_filter;
- } else {
- $return = '';
- foreach ($this->_subfilters as $filter) {
- $return = $return.$filter->asString();
+ // Extract logical operator and filter arguments.
+ $operator = substr($filter, 0, 1);
+ $filter = substr($filter, 1);
+
+ // Split $filter into individual subfilters. We cannot use split() for
+ // this, because we do not know the complexiness of the
+ // subfilter. Thus, we look trough the filter string and just recognize
+ // ending filters at the first level. We record the index number of the
+ // char and use that information later to split the string.
+ $sub_index_pos = array();
+ // Previous character looked at.
+ $prev_char = '';
+ // Denotes the current bracket level we are, >1 is too deep, 1 is ok, 0
+ // is outside any subcomponent.
+ $level = 0;
+ for ($curpos = 0; $curpos < strlen($filter); $curpos++) {
+ $cur_char = $filter{$curpos};
+
+ // Rise/lower bracket level.
+ if ($cur_char == '(' && $prev_char != '\\') {
+ $level++;
+ } elseif ($cur_char == ')' && $prev_char != '\\') {
+ $level--;
+ }
+
+ if ($cur_char == '(' && $prev_char == ')' && $level == 1) {
+ // Mark the position for splitting.
+ array_push($sub_index_pos, $curpos);
}
- $return = '(' . $this->_match . $return . ')';
+ $prev_char = $cur_char;
}
- return $return;
- }
- /**
- * Alias for perl interface as_string()
- *
- * @see asString()
- * @return string|Horde_Ldap_Error
- */
- public function as_string()
- {
- return $this->asString();
- }
+ // Now perform the splits. To get the last part too, we need to add the
+ // "END" index to the split array.
+ array_push($sub_index_pos, strlen($filter));
+ $subfilters = array();
+ $oldpos = 0;
+ foreach ($sub_index_pos as $s_pos) {
+ $str_part = substr($filter, $oldpos, $s_pos - $oldpos);
+ array_push($subfilters, $str_part);
+ $oldpos = $s_pos;
+ }
- /**
- * Print the text representation of the filter to FH, or the currently selected output handle if FH is not given
- *
- * This method is only for compatibility to the perl interface.
- * However, the original method was called "print" but due to PHP language restrictions,
- * we can't have a print() method.
- *
- * @param resource $FH (optional) A filehandle resource
- *
- * @return true|Horde_Ldap_Error
- */
- public function printMe($FH = false)
- {
- if (!is_resource($FH)) {
- $filter_str = $this->asString();
- print($filter_str);
- } else {
- $filter_str = $this->asString();
- $res = @fwrite($FH, $this->asString());
- if ($res == false) {
- throw new Horde_Ldap_Exception("Unable to write filter string to filehandle \$FH!");
+ // Some error checking...
+ if (count($subfilters) == 1) {
+ // Only one subfilter found.
+ } elseif (count($subfilters) > 1) {
+ // Several subfilters found.
+ if ($operator == '!') {
+ throw new Horde_Ldap_Exception('Invalid filter syntax: NOT operator detected but several arguments given');
}
+ } else {
+ // This should not happen unless the user specified a wrong filter.
+ throw new Horde_Ldap_Exception('Invalid filter syntax: got operator ' . $operator . ' but no argument');
+ }
+
+ // Now parse the subfilters into objects and combine them using the
+ // operator.
+ $subfilters_o = array();
+ foreach ($subfilters as $s_s) {
+ $o = self::parse($s_s);
+ array_push($subfilters_o, self::parse($s_s));
+ }
+ if (count($subfilters_o) == 1) {
+ $subfilters_o = $subfilters_o[0];
}
- return true;
+
+ return self::combine($operator, $subfilters_o);
}
/**
- * This can be used to escape a string to provide a valid LDAP-Filter.
- *
- * LDAP will only recognise certain characters as the
- * character istself if they are properly escaped. This is
- * what this method does.
- * The method can be called statically, so you can use it outside
- * for your own purposes (eg for escaping only parts of strings)
+ * Parses a single leaf component.
*
- * In fact, this is just a shorthand to {@link Horde_Ldap_Util::escape_filter_value()}.
- * For upward compatibiliy reasons you are strongly encouraged to use the escape
- * methods provided by the Horde_Ldap_Util class.
+ * @param string $filter An LDAP filter string.
*
- * @param string $value Any string who should be escaped
- *
- * @static
- * @return string The string $string, but escaped
- * @deprecated Do not use this method anymore, instead use Horde_Ldap_Util::escape_filter_value() directly
+ * @return Horde_Ldap_Filter
+ * @throws Horde_Ldap_Exception
*/
- public static function escape($value)
+ protected static function _parseLeaf($filter)
{
- $return = Horde_Ldap_Util::escape_filter_value(array($value));
- return $return[0];
+ // Detect multiple leaf components.
+ // [TODO] Maybe this will make problems with filters containing
+ // brackets inside the value.
+ if (strpos($filter, ')(')) {
+ throw new Horde_Ldap_Exception('Invalid filter syntax: multiple leaf components detected');
+ }
+
+ $filter_parts = preg_split('/(?<!\\\\)(=|=~|>|<|>=|<=)/', $filter, 2, PREG_SPLIT_DELIM_CAPTURE);
+ if (count($filter_parts) != 3) {
+ throw new Horde_Ldap_Exception('Invalid filter syntax: unknown matching rule used');
+ }
+
+ // [TODO]: Do we need to escape at all? what about *-chars user provide
+ // and that should remain special? I think, those prevent
+ // escaping! We need to check against PERL Net::LDAP!
+ // $value_arr = Horde_Ldap_Util::escape_filter_value(array($filter_parts[2]));
+ // $value = $value_arr[0];
+
+ return new Horde_Ldap_Filter(array('filter' => '(' . $filter_parts[0] . $filter_parts[1] . $filter_parts[2] . ')'));
}
/**
- * Is this a container or a leaf filter object?
+ * Returns the string representation of this filter.
+ *
+ * This method runs through all filter objects and creates the string
+ * representation of the filter.
*
- * @access protected
- * @return boolean
+ * @return string
*/
- protected function isLeaf()
+ public function __toString()
{
- if (count($this->_subfilters) > 0) {
- return false; // Container!
- } else {
- return true; // Leaf!
+ if (!count($this->_filters)) {
+ return $this->_filter;
+ }
+
+ $return = '';
+ foreach ($this->_filters as $filter) {
+ $return .= (string)$filter;
}
+
+ return '(' . $this->_operator . $return . ')';
}
}
-?>
* @see http://www.ietf.org/rfc/rfc2849.txt
* @todo LDAPv3 controls are not implemented yet
*/
-class Horde_Ldap_LDIF
+class Horde_Ldap_Ldif
{
/**
* Options.
return;
}
- switch ($imode) {
+ switch ($mode) {
case 'r':
if (!file_exists($file)) {
throw new Horde_Ldap_Exception('Unable to open ' . $file . ' for reading: file not found');
<?php
/**
- * File containing the Horde_Ldap_RootDSE interface class.
+ * Getting the rootDSE entry of a LDAP server.
*
- * PHP version 5
- *
- * @category Net
- * @package Horde_Ldap
+ * @category Horde
+ * @package Ldap
* @author Jan Wagner <wagner@netsols.de>
+ * @author Jan Schneider <jan@horde.org>
* @copyright 2009 Jan Wagner
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
- * @version SVN: $Id: RootDSE.php 286718 2009-08-03 07:30:49Z beni $
- * @link http://pear.php.net/package/Horde_Ldap/
- */
-
-/**
- * Includes
- */
-#require_once 'PEAR.php';
-
-/**
- * Getting the rootDSE entry of a LDAP server
- *
- * @category Net
- * @package Horde_Ldap
- * @author Jan Wagner <wagner@netsols.de>
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- * @link http://pear.php.net/package/Horde_Ldap2/
+ * @copyright 2010 The Horde Project
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
-class Horde_Ldap_RootDSE
+class Horde_Ldap_RootDse
{
/**
- * @access protected
* @var object Horde_Ldap_Entry
- **/
- protected $_entry;
-
- /**
- * Class constructor
- *
- * @param Horde_Ldap_Entry &$entry Horde_Ldap_Entry object of the RootDSE
*/
- protected function __construct(&$entry)
- {
- $this->_entry = $entry;
- }
+ protected $_entry;
/**
- * Fetches a RootDSE object from an LDAP connection
+ * Constructor.
*
- * @param Horde_Ldap $ldap Directory from which the RootDSE should be fetched
- * @param array $attrs Array of attributes to search for
+ * Fetches a RootDSE object from an LDAP connection.
*
- * @access static
- * @return Horde_Ldap_RootDSE
+ * @param Horde_Ldap $ldap Directory from which the RootDSE should be
+ * fetched.
+ * @param array $attrs Array of attributes to search for.
*
* @throws Horde_Ldap_Exception
*/
- public static function fetch(Horde_Ldap $ldap, $attrs = null)
+ public function __construct(Horde_Ldap $ldap, $attrs = null)
{
- if (is_array($attrs) && count($attrs) > 0 ) {
+ if (is_array($attrs) && count($attrs)) {
$attributes = $attrs;
} else {
$attributes = array('vendorName',
'supportedControl',
'supportedSASLMechanisms',
'supportedLDAPVersion',
- 'subschemaSubentry' );
+ 'subschemaSubentry');
}
- $result = $ldap->search('', '(objectClass=*)', array('attributes' => $attributes, 'scope' => 'base'));
+ $result = $ldap->search('', '(objectClass=*)',
+ array('attributes' => $attributes,
+ 'scope' => 'base'));
$entry = $result->shiftEntry();
- if (false === $entry) {
+ if (!$entry) {
throw new Horde_Ldap_Exception('Could not fetch RootDSE entry');
}
- $ret = new Horde_Ldap_RootDSE($entry);
- return $ret;
+ $this->_entry = $entry;
}
/**
- * Gets the requested attribute value
- *
- * Same usuage as {@link Horde_Ldap_Entry::getValue()}
+ * Returns the requested attribute value.
*
- * @param string $attr Attribute name
- * @param array $options Array of options
+ * @see Horde_Ldap_Entry::getValue()
*
- * @access public
- * @return mixed Horde_Ldap_Error object or attribute values
- * @see Horde_Ldap_Entry::get_value()
- */
- public function getValue($attr = '', $options = '')
- {
- return $this->_entry->get_value($attr, $options);
- }
-
- /**
- * Alias function of getValue() for perl-ldap interface
+ * @param string $attr Attribute name.
+ * @param array $options Array of options.
*
- * @see getValue()
- * @return mixed
+ * @return string|array Attribute value(s).
+ * @throws Horde_Ldap_Exception
*/
- public function get_value()
+ public function getValue($attr, $options = '')
{
- $args = func_get_args();
- return call_user_func_array(array( &$this, 'getValue' ), $args);
+ return $this->_entry->getValue($attr, $options);
}
/**
- * Determines if the extension is supported
+ * Determines if the extension is supported.
*
- * @param array $oids Array of oids to check
+ * @param array $oids Array of OIDs to check.
*
- * @access public
* @return boolean
*/
public function supportedExtension($oids)
}
/**
- * Alias function of supportedExtension() for perl-ldap interface
+ * Determines if the version is supported.
*
- * @see supportedExtension()
- * @return boolean
- */
- public function supported_extension()
- {
- $args = func_get_args();
- return call_user_func_array(array( &$this, 'supportedExtension'), $args);
- }
-
- /**
- * Determines if the version is supported
+ * @param array $versions Versions to check.
*
- * @param array $versions Versions to check
- *
- * @access public
* @return boolean
*/
public function supportedVersion($versions)
}
/**
- * Alias function of supportedVersion() for perl-ldap interface
- *
- * @see supportedVersion()
- * @return boolean
- */
- public function supported_version()
- {
- $args = func_get_args();
- return call_user_func_array(array(&$this, 'supportedVersion'), $args);
- }
-
- /**
- * Determines if the control is supported
+ * Determines if the control is supported.
*
- * @param array $oids Control oids to check
+ * @param array $oids Control OIDs to check.
*
- * @access public
* @return boolean
*/
public function supportedControl($oids)
}
/**
- * Alias function of supportedControl() for perl-ldap interface
+ * Determines if the sasl mechanism is supported.
*
- * @see supportedControl()
- * @return boolean
- */
- public function supported_control()
- {
- $args = func_get_args();
- return call_user_func_array(array(&$this, 'supportedControl' ), $args);
- }
-
- /**
- * Determines if the sasl mechanism is supported
+ * @param array $mechlist SASL mechanisms to check.
*
- * @param array $mechlist SASL mechanisms to check
- *
- * @access public
* @return boolean
*/
public function supportedSASLMechanism($mechlist)
}
/**
- * Alias function of supportedSASLMechanism() for perl-ldap interface
+ * Checks for existance of value in attribute.
*
- * @see supportedSASLMechanism()
- * @return boolean
- */
- public function supported_sasl_mechanism()
- {
- $args = func_get_args();
- return call_user_func_array(array(&$this, 'supportedSASLMechanism'), $args);
- }
-
- /**
- * Checks for existance of value in attribute
- *
- * @param array $values values to check
- * @param string $attr attribute name
+ * @param array $values Values to check.
+ * @param string $attr Attribute name.
*
- * @access protected
* @return boolean
*/
protected function checkAttr($values, $attr)
{
- if (!is_array($values)) $values = array($values);
+ if (!is_array($values)) {
+ $values = array($values);
+ }
foreach ($values as $value) {
- if (!@in_array($value, $this->get_value($attr, 'all'))) {
+ if (!in_array($value, $this->get_value($attr, 'all'))) {
return false;
}
}
+
return true;
}
}
-
-?>
<?php
/**
- * File containing the Horde_Ldap_Schema interface class.
- *
- * PHP version 5
- *
- * @category Net
- * @package Horde_Ldap
- * @author Jan Wagner <wagner@netsols.de>
- * @author Benedikt Hallinger <beni@php.net>
- * @copyright 2009 Jan Wagner, Benedikt Hallinger
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
- * @version SVN: $Id: Schema.php 286718 2009-08-03 07:30:49Z beni $
- * @link http://pear.php.net/package/Horde_Ldap/
- * @todo see the comment at the end of the file
- */
-
-/**
- * Syntax definitions
- *
- * Please don't forget to add binary attributes to isBinary() below
- * to support proper value fetching from Horde_Ldap_Entry
- */
-define('HORDE_LDAP_SYNTAX_BOOLEAN', '1.3.6.1.4.1.1466.115.121.1.7');
-define('HORDE_LDAP_SYNTAX_DIRECTORY_STRING', '1.3.6.1.4.1.1466.115.121.1.15');
-define('HORDE_LDAP_SYNTAX_DISTINGUISHED_NAME', '1.3.6.1.4.1.1466.115.121.1.12');
-define('HORDE_LDAP_SYNTAX_INTEGER', '1.3.6.1.4.1.1466.115.121.1.27');
-define('HORDE_LDAP_SYNTAX_JPEG', '1.3.6.1.4.1.1466.115.121.1.28');
-define('HORDE_LDAP_SYNTAX_NUMERIC_STRING', '1.3.6.1.4.1.1466.115.121.1.36');
-define('HORDE_LDAP_SYNTAX_OID', '1.3.6.1.4.1.1466.115.121.1.38');
-define('HORDE_LDAP_SYNTAX_OCTET_STRING', '1.3.6.1.4.1.1466.115.121.1.40');
-
-/**
* Load an LDAP Schema and provide information
*
* This class takes a Subschema entry, parses this information
* inspired by perl-ldap( http://perl-ldap.sourceforge.net).
* You will find portions of their implementation in here.
*
- * @category Net
- * @package Horde_Ldap
- * @author Jan Wagner <wagner@netsols.de>
- * @author Benedikt Hallinger <beni@php.net>
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- * @link http://pear.php.net/package/Horde_Ldap2/
+ * @category Horde
+ * @package Ldap
+ * @author Jan Wagner <wagner@netsols.de>
+ * @author Benedikt Hallinger <beni@php.net>
+ * @author Jan Schneider <jan@horde.org>
+ * @copyright 2009 Jan Wagner, Benedikt Hallinger
+ * @copyright 2010 The Horde Project
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
class Horde_Ldap_Schema
{
/**
- * Map of entry types to ldap attributes of subschema entry
+ * Syntax definitions.
+ *
+ * Please don't forget to add binary attributes to isBinary() below to
+ * support proper value fetching from Horde_Ldap_Entry.
+ */
+ const SYNTAX_BOOLEAN = '1.3.6.1.4.1.1466.115.121.1.7';
+ const SYNTAX_DIRECTORY_STRING = '1.3.6.1.4.1.1466.115.121.1.15';
+ const SYNTAX_DISTINGUISHED_NAME = '1.3.6.1.4.1.1466.115.121.1.12';
+ const SYNTAX_INTEGER = '1.3.6.1.4.1.1466.115.121.1.27';
+ const SYNTAX_JPEG = '1.3.6.1.4.1.1466.115.121.1.28';
+ const SYNTAX_NUMERIC_STRING = '1.3.6.1.4.1.1466.115.121.1.36';
+ const SYNTAX_OID = '1.3.6.1.4.1.1466.115.121.1.38';
+ const SYNTAX_OCTET_STRING = '1.3.6.1.4.1.1466.115.121.1.40';
+
+ /**
+ * Map of entry types to LDAP attributes of subschema entry.
*
- * @access public
* @var array
*/
public $types = array(
- 'attribute' => 'attributeTypes',
- 'ditcontentrule' => 'dITContentRules',
- 'ditstructurerule' => 'dITStructureRules',
- 'matchingrule' => 'matchingRules',
- 'matchingruleuse' => 'matchingRuleUse',
- 'nameform' => 'nameForms',
- 'objectclass' => 'objectClasses',
- 'syntax' => 'ldapSyntaxes'
- );
+ 'attribute' => 'attributeTypes',
+ 'ditcontentrule' => 'dITContentRules',
+ 'ditstructurerule' => 'dITStructureRules',
+ 'matchingrule' => 'matchingRules',
+ 'matchingruleuse' => 'matchingRuleUse',
+ 'nameform' => 'nameForms',
+ 'objectclass' => 'objectClasses',
+ 'syntax' => 'ldapSyntaxes' );
/**
* Array of entries belonging to this type
*
- * @access protected
* @var array
*/
protected $_attributeTypes = array();
/**
- * hash of all fetched oids
+ * Hash of all fetched OIDs.
*
- * @access protected
* @var array
*/
protected $_oids = array();
/**
- * Tells if the schema is initialized
+ * Whether the schema is initialized.
*
- * @access protected
- * @var boolean
* @see parse(), get()
+ * @var boolean
*/
protected $_initialized = false;
/**
- * Fetch the Schema from an LDAP connection
+ * Constructor.
+ *
+ * Fetches the Schema from an LDAP connection.
*
- * @param Horde_Ldap $ldap LDAP connection
- * @param string $dn (optional) Subschema entry dn
+ * @param Horde_Ldap $ldap LDAP connection.
+ * @param string $dn Subschema entry DN.
*
- * @access public
- * @return Horde_Ldap_Schema|Horde_Ldap_Error
+ * @throws Horde_Ldap_Exception
*/
- public function fetch(Horde_Ldap $ldap, $dn = null)
+ public function __construct(Horde_Ldap $ldap, $dn = null)
{
- $schema_o = new Horde_Ldap_Schema();
-
if (is_null($dn)) {
- // get the subschema entry via root dse
+ // Get the subschema entry via rootDSE.
$dse = $ldap->rootDSE(array('subschemaSubentry'));
- $base = $dse->getValue('subschemaSubentry', 'single');
- $dn = $base;
+ $base = $dse->getValue('subschemaSubentry', 'single');
+ $dn = $base;
}
- // Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that incorrectly
- // call this entry subSchemaSubentry instead of subschemaSubentry.
- // Note the correct case/spelling as per RFC 2251.
+ // Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that
+ // incorrectly call this entry subSchemaSubentry instead of
+ // subschemaSubentry. Note the correct case/spelling as per RFC 2251.
if (is_null($dn)) {
- // get the subschema entry via root dse
+ // Get the subschema entry via rootDSE.
$dse = $ldap->rootDSE(array('subSchemaSubentry'));
- $base = $dse->getValue('subSchemaSubentry', 'single');
- $dn = $base;
+ $base = $dse->getValue('subSchemaSubentry', 'single');
+ $dn = $base;
}
- // Final fallback case where there is no subschemaSubentry attribute
- // in the root DSE (this is a bug for an LDAP v3 server so report this
- // to your LDAP vendor if you get this far).
+ // Final fallback in case there is no subschemaSubentry attribute in
+ // the root DSE (this is a bug for an LDAPv3 server so report this to
+ // your LDAP vendor if you get this far).
if (is_null($dn)) {
$dn = 'cn=Subschema';
}
- // fetch the subschema entry
+ // Fetch the subschema entry.
$result = $ldap->search($dn, '(objectClass=*)',
- array('attributes' => array_values($schema_o->types),
- 'scope' => 'base'));
+ array('attributes' => array_values($this->types),
+ 'scope' => 'base'));
$entry = $result->shiftEntry();
- if (!$entry instanceof Horde_Ldap_Entry) {
+ if (!($entry instanceof Horde_Ldap_Entry)) {
throw new Horde_Ldap_Exception('Could not fetch Subschema entry');
}
- $schema_o->parse($entry);
- return $schema_o;
+ $this->parse($entry);
}
/**
- * Return a hash of entries for the given type
+ * Returns a hash of entries for the given type.
*
- * Returns a hash of entry for th givene type. Types may be:
- * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules,
- * matchingruleuses, nameforms, syntaxes
+ * Types may be: objectclasses, attributes, ditcontentrules,
+ * ditstructurerules, matchingrules, matchingruleuses, nameforms, syntaxes.
*
- * @param string $type Type to fetch
+ * @param string $type Type to fetch.
*
- * @access public
- * @return array|Horde_Ldap_Error Array or Horde_Ldap_Error
+ * @return array
+ * @throws Horde_Ldap_Exception
*/
- public function &getAll($type)
+ public function getAll($type)
{
- $map = array('objectclasses' => &$this->_objectClasses,
- 'attributes' => &$this->_attributeTypes,
- 'ditcontentrules' => &$this->_dITContentRules,
- 'ditstructurerules' => &$this->_dITStructureRules,
- 'matchingrules' => &$this->_matchingRules,
- 'matchingruleuses' => &$this->_matchingRuleUse,
- 'nameforms' => &$this->_nameForms,
- 'syntaxes' => &$this->_ldapSyntaxes );
-
- $key = strtolower($type);
- if (!key_exists($key, $map)) {
- throw new Horde_Ldap_Exception("Unknown type $type");
- }
+ $map = array('objectclasses' => $this->_objectClasses,
+ 'attributes' => $this->_attributeTypes,
+ 'ditcontentrules' => $this->_dITContentRules,
+ 'ditstructurerules' => $this->_dITStructureRules,
+ 'matchingrules' => $this->_matchingRules,
+ 'matchingruleuses' => $this->_matchingRuleUse,
+ 'nameforms' => $this->_nameForms,
+ 'syntaxes' => $this->_ldapSyntaxes);
+
+ $key = Horde_String::lower($type);
+ if (!isset($map[$key])) {
+ throw new Horde_Ldap_Exception("Unknown type $type");
+ }
+
return $map[$key];
}
/**
- * Return a specific entry
+ * Returns a specific entry.
*
- * @param string $type Type of name
- * @param string $name Name or OID to fetch
+ * @param string $type Type of name.
+ * @param string $name Name or OID to fetch.
*
- * @access public
- * @return mixed Entry or Horde_Ldap_Error
+ * @return mixed
+ * @throws Horde_Ldap_Exception
*/
- public function &get($type, $name)
+ public function get($type, $name)
{
- if ($this->_initialized) {
- $type = strtolower($type);
- if (false == key_exists($type, $this->types)) {
- throw new Horde_Ldap_Exception("No such type $type");
- }
+ if (!$this->_initialized) {
+ return null;
+ }
- $name = strtolower($name);
- $type_var = &$this->{'_' . $this->types[$type]};
+ $type = Horde_String::lower($type);
+ if (!isset($this->types[$type])) {
+ throw new Horde_Ldap_Exception("No such type $type");
+ }
- if (key_exists($name, $type_var)) {
- return $type_var[$name];
- } elseif (key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) {
- return $this->_oids[$name];
- } else {
- throw new Horde_Ldap_Exception("Could not find $type $name");
- }
- } else {
- $return = null;
- return $return;
+ $name = Horde_String::lower($name);
+ $type_var = $this->{'_' . $this->types[$type]};
+
+ if (isset($type_var[$name])) {
+ return $type_var[$name];
}
+ if (isset($this->_oids[$name]) &&
+ $this->_oids[$name]['type'] == $type) {
+ return $this->_oids[$name];
+ }
+ throw new Horde_Ldap_Exception("Could not find $type $name");
}
/**
- * Fetches attributes that MAY be present in the given objectclass
+ * Fetches attributes that MAY be present in the given objectclass.
*
- * @param string $oc Name or OID of objectclass
+ * @param string $oc Name or OID of objectclass.
*
- * @access public
- * @return array|Horde_Ldap_Error Array with attributes or Horde_Ldap_Error
+ * @return array Array with attributes.
+ * @throws Horde_Ldap_Exception
*/
public function may($oc)
{
}
/**
- * Fetches attributes that MUST be present in the given objectclass
+ * Fetches attributes that MUST be present in the given objectclass.
*
- * @param string $oc Name or OID of objectclass
+ * @param string $oc Name or OID of objectclass.
*
- * @access public
- * @return array|Horde_Ldap_Error Array with attributes or Horde_Ldap_Error
+ * @return array Array with attributes.
+ * @throws Horde_Ldap_Exception
*/
public function must($oc)
{
}
/**
- * Fetches the given attribute from the given objectclass
+ * Fetches the given attribute from the given objectclass.
*
- * @param string $oc Name or OID of objectclass
- * @param string $attr Name of attribute to fetch
+ * @param string $oc Name or OID of objectclass.
+ * @param string $attr Name of attribute to fetch.
*
- * @access protected
- * @return array|Horde_Ldap_Error The attribute or Horde_Ldap_Error
+ * @return array The attribute.
+ * @throws Horde_Ldap_Exception
*/
protected function _getAttr($oc, $attr)
{
- $oc = strtolower($oc);
- if (key_exists($oc, $this->_objectClasses) && key_exists($attr, $this->_objectClasses[$oc])) {
+ $oc = Horde_String::lower($oc);
+ if (isset($this->_objectClasses[$oc]) &&
+ isset($this->_objectClasses[$oc][$attr])) {
return $this->_objectClasses[$oc][$attr];
- } elseif (key_exists($oc, $this->_oids) &&
- $this->_oids[$oc]['type'] == 'objectclass' &&
- key_exists($attr, $this->_oids[$oc])) {
+ }
+ if (isset($this->_oids[$oc]) &&
+ $this->_oids[$oc]['type'] == 'objectclass' &&
+ isset($this->_oids[$oc][$attr])) {
return $this->_oids[$oc][$attr];
- } else {
- throw new Horde_Ldap_Exception("Could not find $attr attributes for $oc ");
}
+ throw new Horde_Ldap_Exception("Could not find $attr attributes for $oc ");
}
/**
- * Returns the name(s) of the immediate superclass(es)
+ * Returns the name(s) of the immediate superclass(es).
*
- * @param string $oc Name or OID of objectclass
+ * @param string $oc Name or OID of objectclass.
*
- * @access public
- * @return array|Horde_Ldap_Error Array of names or Horde_Ldap_Error
+ * @return array
+ * @throws Horde_Ldap_Exception
*/
public function superclass($oc)
{
$o = $this->get('objectclass', $oc);
- return (key_exists('sup', $o) ? $o['sup'] : array());
+ return isset($o['sup']) ? $o['sup'] : array();
}
/**
- * Parses the schema of the given Subschema entry
+ * Parses the schema of the given subschema entry.
*
- * @param Horde_Ldap_Entry &$entry Subschema entry
- *
- * @access public
- * @return void
+ * @param Horde_Ldap_Entry $entry Subschema entry.
*/
- public function parse(&$entry)
+ public function parse($entry)
{
foreach ($this->types as $type => $attr) {
- // initialize map type to entry
+ // Initialize map type to entry.
$type_var = '_' . $attr;
$this->{$type_var} = array();
- // get values for this type
- if ($entry->exists($attr)) {
- $values = $entry->getValue($attr);
- if (is_array($values)) {
- foreach ($values as $value) {
-
- unset($schema_entry); // this was a real mess without it
-
- // get the schema entry
- $schema_entry = $this->_parse_entry($value);
-
- // set the type
- $schema_entry['type'] = $type;
-
- // save a ref in $_oids
- $this->_oids[$schema_entry['oid']] = &$schema_entry;
+ if (!$entry->exists($attr)) {
+ continue;
+ }
- // save refs for all names in type map
- $names = $schema_entry['aliases'];
- array_push($names, $schema_entry['name']);
- foreach ($names as $name) {
- $this->{$type_var}[strtolower($name)] = &$schema_entry;
- }
- }
+ // Get values for this type.
+ $values = $entry->getValue($attr);
+ if (!is_array($values)) {
+ continue;
+ }
+ foreach ($values as $value) {
+ // Get the schema entry.
+ $schema_entry = $this->_parse_entry($value);
+ // Set the type.
+ $schema_entry['type'] = $type;
+ // Save a ref in $_oids.
+ $this->_oids[$schema_entry['oid']] = $schema_entry;
+ // Save refs for all names in type map.
+ $names = $schema_entry['aliases'];
+ array_push($names, $schema_entry['name']);
+ foreach ($names as $name) {
+ $this->{$type_var}[Horde_String::lower($name)] = $schema_entry;
}
}
}
}
/**
- * Parses an attribute value into a schema entry
+ * Parses an attribute value into a schema entry.
*
- * @param string $value Attribute value
+ * @param string $value Attribute value.
*
- * @access protected
- * @return array|false Schema entry array or false
+ * @return array Schema entry array.
*/
- protected function &_parse_entry($value)
+ protected function _parse_entry($value)
{
- // tokens that have no value associated
+ // Tokens that have no value associated.
$noValue = array('single-value',
'obsolete',
'collective',
'structural',
'auxiliary');
- // tokens that can have multiple values
+ // Tokens that can have multiple values.
$multiValue = array('must', 'may', 'sup');
- $schema_entry = array('aliases' => array()); // initilization
-
- $tokens = $this->_tokenize($value); // get an array of tokens
+ // Get an array of tokens.
+ $tokens = $this->_tokenize($value);
- // remove surrounding brackets
- if ($tokens[0] == '(') array_shift($tokens);
- if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-(
+ // Remove surrounding brackets.
+ if ($tokens[0] == '(') {
+ array_shift($tokens);
+ }
+ if ($tokens[count($tokens) - 1] == ')') {
+ array_pop($tokens);
+ }
- $schema_entry['oid'] = array_shift($tokens); // first token is the oid
+ // First token is the oid.
+ $schema_entry = array('aliases' => array(),
+ 'oid' => array_shift($tokens));
- // cycle over the tokens until none are left
+ // Cycle over the tokens until none are left.
while (count($tokens) > 0) {
- $token = strtolower(array_shift($tokens));
+ $token = Horde_String::lower(array_shift($tokens));
if (in_array($token, $noValue)) {
- $schema_entry[$token] = 1; // single value token
+ // Single value token.
+ $schema_entry[$token] = 1;
} else {
- // this one follows a string or a list if it is multivalued
+ // Follow a string or a list if it is multivalued.
if (($schema_entry[$token] = array_shift($tokens)) == '(') {
- // this creates the list of values and cycles through the tokens
- // until the end of the list is reached ')'
+ // Create the list of values and cycles through the tokens
+ // until the end of the list is reached ')'.
$schema_entry[$token] = array();
while ($tmp = array_shift($tokens)) {
- if ($tmp == ')') break;
- if ($tmp != '$') array_push($schema_entry[$token], $tmp);
+ if ($tmp == ')') {
+ break;
+ }
+ if ($tmp != '$') {
+ array_push($schema_entry[$token], $tmp);
+ }
}
}
- // create a array if the value should be multivalued but was not
- if (in_array($token, $multiValue) && !is_array($schema_entry[$token])) {
+ // Create an array if the value should be multivalued but was
+ // not.
+ if (in_array($token, $multiValue) &&
+ !is_array($schema_entry[$token])) {
$schema_entry[$token] = array($schema_entry[$token]);
}
}
}
- // get max length from syntax
- if (key_exists('syntax', $schema_entry)) {
+
+ // Get max length from syntax.
+ if (isset($schema_entry['syntax'])) {
if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) {
$schema_entry['max_length'] = $matches[1];
}
}
- // force a name
+
+ // Force a name.
if (empty($schema_entry['name'])) {
$schema_entry['name'] = $schema_entry['oid'];
}
- // make one name the default and put the other ones into aliases
+
+ // Make one name the default and put the other ones into aliases.
if (is_array($schema_entry['name'])) {
$aliases = $schema_entry['name'];
$schema_entry['name'] = array_shift($aliases);
$schema_entry['aliases'] = $aliases;
}
+
return $schema_entry;
}
/**
- * Tokenizes the given value into an array of tokens
+ * Tokenizes the given value into an array of tokens.
*
- * @param string $value String to parse
+ * @param string $value String to parse.
*
- * @access protected
- * @return array Array of tokens
+ * @return array Array of tokens.
*/
protected function _tokenize($value)
{
- $tokens = array(); // array of tokens
- $matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns
-
- // this one is taken from perl-ldap, modified for php
- $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x";
-
- /**
- * This one matches one big pattern wherin only one of the three subpatterns matched
- * We are interested in the subpatterns that matched. If it matched its value will be
- * non-empty and so it is a token. Tokens may be round brackets, a string, or a string
- * enclosed by '
- */
- preg_match_all($pattern, $value, $matches);
-
- for ($i = 0; $i < count($matches[0]); $i++) { // number of tokens (full pattern match)
- for ($j = 1; $j < 4; $j++) { // each subpattern
- if (null != trim($matches[$j][$i])) { // pattern match in this subpattern
- $tokens[$i] = trim($matches[$j][$i]); // this is the token
+ /* Match one big pattern where only one of the three subpatterns
+ * matches. We are interested in the subpatterns that matched. If it
+ * matched its value will be non-empty and so it is a token. Tokens may
+ * be round brackets, a string, or a string enclosed by ''. */
+ preg_match_all("/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x", $value, $matches);
+
+ $tokens = array();
+ // Number of tokens (full pattern match).
+ for ($i = 0; $i < count($matches[0]); $i++) {
+ // Each subpattern.
+ for ($j = 1; $j < 4; $j++) {
+ // Pattern match in this subpattern.
+ if (null != trim($matches[$j][$i])) {
+ // This is the token.
+ $tokens[$i] = trim($matches[$j][$i]);
}
}
}
+
return $tokens;
}
/**
- * Returns wether a attribute syntax is binary or not
+ * Returns wether a attribute syntax is binary or not.
*
- * This method gets used by Horde_Ldap_Entry to decide which
- * PHP function needs to be used to fetch the value in the
- * proper format (e.g. binary or string)
+ * This method is used by Horde_Ldap_Entry to decide which PHP function
+ * needs to be used to fetch the value in the proper format (e.g. binary or
+ * string).
*
- * @param string $attribute The name of the attribute (eg.: 'sn')
+ * @param string $attribute The name of the attribute (eg.: 'sn').
*
- * @access public
- * @return boolean
+ * @return boolean True if the attribute is a binary type.
*/
public function isBinary($attribute)
{
- $return = false; // default to false
-
- // This list contains all syntax that should be treaten as
- // containing binary values
- // The Syntax Definitons go into constants at the top of this page
- $syntax_binary = array(
- HORDE_LDAP_SYNTAX_OCTET_STRING,
- HORDE_LDAP_SYNTAX_JPEG
- );
-
- // Check Syntax
- try {
- $attr_s = $this->get('attribute', $attribute);
- } catch (Horde_Ldap_Exception $e) {
- // Attribute not found in schema
- $return = false; // consider attr not binary
- }
- if (isset($attr_s['syntax']) && in_array($attr_s['syntax'], $syntax_binary)) {
+ // All syntax that should be treaten as containing binary values.
+ $syntax_binary = array(self::SYNTAX_OCTET_STRING, self::SYNTAX_JPEG);
+
+ // Check Syntax.
+ try {
+ $attr_s = $this->get('attribute', $attribute);
+ } catch (Horde_Ldap_Exception $e) {
+ // Attribute not found in schema, consider attr not binary.
+ return false;
+ }
+
+ if (isset($attr_s['syntax']) &&
+ in_array($attr_s['syntax'], $syntax_binary)) {
// Syntax is defined as binary in schema
- $return = true;
- } else {
- // Syntax not defined as binary, or not found
- // if attribute is a subtype, check superior attribute syntaxes
- if (isset($attr_s['sup'])) {
- foreach ($attr_s['sup'] as $superattr) {
- $return = $this->isBinary($superattr);
- if ($return) {
- break; // stop checking parents since we are binary
- }
+ return true;
+ }
+
+ // Syntax not defined as binary, or not found if attribute is a
+ // subtype, check superior attribute syntaxes.
+ if (isset($attr_s['sup'])) {
+ foreach ($attr_s['sup'] as $superattr) {
+ if ($this->isBinary($superattr)) {
+ // Stop checking parents since we are binary.
+ return true;
}
}
}
- return $return;
+ return false;
}
-
- // [TODO] add method that allows us to see to which objectclasses a certain attribute belongs to
- // it should return the result structured, e.g. sorted in "may" and "must". Optionally it should
- // be able to return it just "flat", e.g. array_merge()d.
- // We could use get_all() to achieve this easily, i think
}
-?>
<?php
/**
- * File containing the Horde_Ldap_Search interface class.
- *
- * PHP version 5
+ * Result set of an LDAP search
*
- * @category Net
- * @package Horde_Ldap
+ * @category Horde
+ * @package Ldap
* @author Tarjej Huse <tarjei@bergfald.no>
* @author Benedikt Hallinger <beni@php.net>
- * @copyright 2009 Tarjej Huse, Benedikt Hallinger
- * @license http://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3
- * @version SVN: $Id: Search.php 286718 2009-08-03 07:30:49Z beni $
- * @link http://pear.php.net/package/Horde_Ldap/
- */
-
-/**
- * Includes
- */
-#require_once 'PEAR.php';
-
-/**
- * Result set of an LDAP search
- *
- * @category Net
- * @package Horde_Ldap
- * @author Tarjej Huse <tarjei@bergfald.no>
- * @author Benedikt Hallinger <beni@php.net>
- * @license http://www.gnu.org/copyleft/lesser.html LGPL
- * @link http://pear.php.net/package/Horde_Ldap2/
+ * @author Jan Schneider <jan@horde.org>
+ * @copyright 2009 Jan Wagner, Benedikt Hallinger
+ * @copyright 2010 The Horde Project
+ * @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
class Horde_Ldap_Search implements Iterator
{
/**
- * Search result identifier
+ * Search result identifier.
*
- * @access protected
* @var resource
*/
protected $_search;
/**
- * LDAP resource link
+ * LDAP resource link.
*
- * @access protected
* @var resource
*/
protected $_link;
/**
- * Horde_Ldap object
+ * Horde_Ldap object.
*
- * A reference of the Horde_Ldap object for passing to Horde_Ldap_Entry
+ * A reference of the Horde_Ldap object for passing to Horde_Ldap_Entry.
*
- * @access protected
- * @var object Horde_Ldap
+ * @var Horde_Ldap
*/
protected $_ldap;
/**
- * Result entry identifier
+ * Result entry identifier.
*
- * @access protected
* @var resource
*/
- protected $_entry = null;
+ protected $_entry;
/**
- * The errorcode the search got
+ * The errorcode from the search.
*
- * Some errorcodes might be of interest, but might not be best handled as errors.
- * examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indicates a huge search.
- * Incomplete results are returned. If you just want to check if there's anything in the search.
- * than this is a point to handle.
- * 32 - no such object - search here returns a count of 0.
+ * Some errorcodes might be of interest that should not be considered
+ * errors, for example:
+ * - 4: LDAP_SIZELIMIT_EXCEEDED - indicates a huge search. Incomplete
+ * results are returned. If you just want to check if there is
+ * anything returned by the search at all, this could be catched.
+ * - 32: no such object - search here returns a count of 0.
*
- * @access protected
- * @var int
+ * @var integer
*/
- protected $_errorCode = 0; // if not set - sucess!
+ protected $_errorCode = 0;
/**
- * Cache for all entries already fetched from iterator interface
+ * Cache for all entries already fetched from iterator interface.
*
- * @access protected
* @var array
*/
protected $_iteratorCache = array();
/**
- * What attributes we searched for
+ * Attributes we searched for.
*
- * The $attributes array contains the names of the searched attributes and gets
- * passed from $Horde_Ldap->search() so the Horde_Ldap_Search object can tell
- * what attributes was searched for ({@link searchedAttrs())
+ * This variable gets set from the constructor and can be retrieved through
+ * {@link searchedAttributes()}.
*
- * This variable gets set from the constructor and returned
- * from {@link searchedAttrs()}
- *
- * @access protected
* @var array
*/
protected $_searchedAttrs = array();
/**
- * Cache variable for storing entries fetched internally
+ * Cache variable for storing entries fetched internally.
*
- * This currently is only used by {@link pop_entry()}
+ * This currently is only used by {@link pop_entry()}.
*
- * @access protected
* @var array
*/
protected $_entry_cache = false;
/**
- * Constructor
- *
- * @param resource &$search Search result identifier
- * @param Horde_Ldap|resource &$ldap Horde_Ldap object or just a LDAP-Link resource
- * @param array $attributes (optional) Array with searched attribute names. (see {@link $_searchedAttrs})
+ * Constructor.
*
- * @access public
+ * @param resource $search Search result identifier.
+ * @param Horde_Ldap|resource $ldap Horde_Ldap object or a LDAP link
+ * resource
+ * @param array $attributes The searched attribute names,
+ * see {@link $_searchedAttrs}.
*/
- public function __construct(&$search, &$ldap, $attributes = array())
+ public function __construct($search, $ldap, $attributes = array())
{
$this->setSearch($search);
if ($ldap instanceof Horde_Ldap) {
- $this->_ldap =& $ldap;
+ $this->_ldap = $ldap;
$this->setLink($this->_ldap->getLink());
} else {
$this->setLink($ldap);
}
/**
- * Returns an array of entry objects
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ @ldap_free_result($this->_search);
+ }
+
+ /**
+ * Returns all entries from the search result.
*
- * @return array Array of entry objects.
+ * @return array All entries.
+ * @throws Horde_Ldap_Exception
*/
public function entries()
{
$entries = array();
-
while ($entry = $this->shiftEntry()) {
$entries[] = $entry;
}
-
return $entries;
}
/**
- * Get the next entry in the searchresult.
+ * Get the next entry from the search result.
*
- * This will return a valid Horde_Ldap_Entry object or false, so
- * you can use this method to easily iterate over the entries inside
- * a while loop.
+ * This will return a valid Horde_Ldap_Entry object or false, so you can
+ * use this method to easily iterate over the entries inside a while loop.
*
- * @return Horde_Ldap_Entry|false Reference to Horde_Ldap_Entry object or false
+ * @return Horde_Ldap_Entry|false Reference to Horde_Ldap_Entry object or
+ * false if no more entries exist.
+ * @throws Horde_Ldap_Exception
*/
- public function &shiftEntry()
+ public function shiftEntry()
{
- if ($this->count() == 0 ) {
- $false = false;
- return $false;
+ if (!$this->count()) {
+ return false;
}
if (is_null($this->_entry)) {
$this->_entry = @ldap_first_entry($this->_link, $this->_search);
$entry = Horde_Ldap_Entry::createConnected($this->_ldap, $this->_entry);
- if ($entry instanceof Horde_Ldap_Error) $entry = false;
} else {
if (!$this->_entry = @ldap_next_entry($this->_link, $this->_entry)) {
- $false = false;
- return $false;
+ return false;
}
$entry = Horde_Ldap_Entry::createConnected($this->_ldap, $this->_entry);
- if ($entry instanceof Horde_Ldap_Error) $entry = false;
}
- return $entry;
- }
- /**
- * Alias function of shiftEntry() for perl-ldap interface
- *
- * @see shiftEntry()
- * @return Horde_Ldap_Entry|false
- */
- public function shift_entry()
- {
- $args = func_get_args();
- return call_user_func_array(array( &$this, 'shiftEntry' ), $args);
+ return $entry;
}
/**
- * Retrieve the next entry in the searchresult, but starting from last entry
+ * Retrieve the next entry in the search result, but starting from last
+ * entry.
*
- * This is the opposite to {@link shiftEntry()} and is also very useful
- * to be used inside a while loop.
+ * This is the opposite to {@link shiftEntry()} and is also very useful to
+ * be used inside a while loop.
*
* @return Horde_Ldap_Entry|false
+ * @throws Horde_Ldap_Exception
*/
public function popEntry()
{
if (false === $this->_entry_cache) {
- // fetch entries into cache if not done so far
+ // Fetch entries into cache if not done so far.
$this->_entry_cache = $this->entries();
}
- $return = array_pop($this->_entry_cache);
- return (null === $return)? false : $return;
+ return count($this->_entry_cache) ? array_pop($this->_entry_cache) : false;
}
/**
- * Alias function of popEntry() for perl-ldap interface
+ * Return entries sorted as array.
*
- * @see popEntry()
- * @return Horde_Ldap_Entry|false
- */
- public function pop_entry()
- {
- $args = func_get_args();
- return call_user_func_array(array( &$this, 'popEntry' ), $args);
- }
-
- /**
- * Return entries sorted as array
+ * This returns a array with sorted entries and the values. Sorting is done
+ * with PHPs {@link array_multisort()}.
*
- * This returns a array with sorted entries and the values.
- * Sorting is done with PHPs {@link array_multisort()}.
- * This method relies on {@link as_struct()} to fetch the raw data of the entries.
+ * This method relies on {@link as_struct()} to fetch the raw data of the
+ * entries.
*
* Please note that attribute names are case sensitive!
*
* Usage example:
* <code>
- * // to sort entries first by location, then by surename, but descending:
- * $entries = $search->sorted_as_struct(array('locality','sn'), SORT_DESC);
+ * // To sort entries first by location, then by surname, but descending:
+ * $entries = $search->sorted_as_struct(array('locality', 'sn'), SORT_DESC);
* </code>
*
- * @param array $attrs Array of attribute names to sort; order from left to right.
- * @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC
+ * @todo what about server side sorting as specified in
+ * http://www.ietf.org/rfc/rfc2891.txt?
+ * @todo Nuke evil eval().
*
- * @return array|Horde_Ldap_Error Array with sorted entries or error
- * @todo what about server side sorting as specified in http://www.ietf.org/rfc/rfc2891.txt?
+ * @param array $attrs Attribute names as sort criteria.
+ * @param integer $order Ordering direction, either constant SORT_ASC or
+ * SORT_DESC
+ *
+ * @return array Sorted entries.
+ * @throws Horde_Ldap_Exception
*/
- public function sorted_as_struct($attrs = array('cn'), $order = SORT_ASC)
+ public function sorted_as_struct(array $attrs = array('cn'),
+ $order = SORT_ASC)
{
- /*
- * Old Code, suitable and fast for single valued sorting
- * This code should be used if we know that single valued sorting is desired,
- * but we need some method to get that knowledge...
- */
+ /* Old Code, suitable and fast for single valued sorting. This code
+ * should be used if we know that single valued sorting is desired, but
+ * we need some method to get that knowledge... */
/*
$attrs = array_reverse($attrs);
foreach ($attrs as $attribute) {
- if (!ldap_sort($this->_link, $this->_search, $attribute)){
- $this->raiseError("Sorting failed for Attribute " . $attribute);
+ if (!ldap_sort($this->_link, $this->_search, $attribute)) {
+ throw new Horde_Ldap_Exception('Sorting failed for attribute ' . $attribute);
}
}
$results = ldap_get_entries($this->_link, $this->_search);
- unset($results['count']); //for tidier output
+ unset($results['count']);
if ($order) {
return array_reverse($results);
- } else {
- return $results;
- }*/
-
- /*
- * New code: complete "client side" sorting
- */
- // first some parameterchecks
- if (!is_array($attrs)) {
- return PEAR::raiseError("Sorting failed: Parameterlist must be an array!");
}
+ return $results;
+ */
+
+ /* New code: complete "client side" sorting */
+ // First some parameterchecks.
if ($order != SORT_ASC && $order != SORT_DESC) {
- return PEAR::raiseError("Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)");
+ throw new Horde_Ldap_Exception('Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)');
}
- // fetch the entries data
+ // Fetch the entries data.
$entries = $this->as_struct();
- // now sort each entries attribute values
- // this is neccessary because later we can only sort by one value,
- // so we need the highest or lowest attribute now, depending on the
- // selected ordering for that specific attribute
+ // Now sort each entries attribute values.
+ // This is neccessary because later we can only sort by one value, so
+ // we need the highest or lowest attribute now, depending on the
+ // selected ordering for that specific attribute.
foreach ($entries as $dn => $entry) {
foreach ($entry as $attr_name => $attr_values) {
sort($entries[$dn][$attr_name]);
}
}
- // reformat entrys array for later use with array_multisort()
- $to_sort = array(); // <- will be a numeric array similar to ldap_get_entries
+ // Reformat entries array for later use with
+ // array_multisort(). $to_sort will be a numeric array similar to
+ // ldap_get_entries().
+ $to_sort = array();
foreach ($entries as $dn => $entry_attr) {
- $row = array();
- $row['dn'] = $dn;
+ $row = array('dn' => $dn);
foreach ($entry_attr as $attr_name => $attr_values) {
$row[$attr_name] = $attr_values;
}
$to_sort[] = $row;
}
- // Build columns for array_multisort()
- // each requested attribute is one row
+ // Build columns for array_multisort(). Each requested attribute is one
+ // row.
$columns = array();
foreach ($attrs as $attr_name) {
foreach ($to_sort as $key => $row) {
}
}
- // sort the colums with array_multisort, if there is something
- // to sort and if we have requested sort columns
+ // Sort the colums with array_multisort() if there is something to sort
+ // and if we have requested sort columns.
if (!empty($to_sort) && !empty($columns)) {
$sort_params = '';
foreach ($attrs as $attr_name) {
- $sort_params .= '$columns[\''.$attr_name.'\'], '.$order.', ';
+ $sort_params .= '$columns[\'' . $attr_name . '\'], ' . $order . ', ';
}
- eval("array_multisort($sort_params \$to_sort);"); // perform sorting
+ eval("array_multisort($sort_params \$to_sort);");
}
return $to_sort;
}
/**
- * Return entries sorted as objects
+ * Returns entries sorted as objects.
*
- * This returns a array with sorted Horde_Ldap_Entry objects.
- * The sorting is actually done with {@link sorted_as_struct()}.
+ * This returns a array with sorted Horde_Ldap_Entry objects. The sorting
+ * is actually done with {@link sorted_as_struct()}.
*
* Please note that attribute names are case sensitive!
- * Also note, that it is (depending on server capabilitys) possible to let
- * the server sort your results. This happens through search controls
- * and is described in detail at {@link http://www.ietf.org/rfc/rfc2891.txt}
+ *
+ * Also note that it is (depending on server capabilities) possible to let
+ * the server sort your results. This happens through search controls and
+ * is described in detail at {@link http://www.ietf.org/rfc/rfc2891.txt}
*
* Usage example:
* <code>
- * // to sort entries first by location, then by surename, but descending:
- * $entries = $search->sorted(array('locality','sn'), SORT_DESC);
+ * // To sort entries first by location, then by surname, but descending:
+ * $entries = $search->sorted(array('locality', 'sn'), SORT_DESC);
* </code>
*
- * @param array $attrs Array of sort attributes to sort; order from left to right.
- * @param int $order Ordering direction, either constant SORT_ASC or SORT_DESC
+ * @todo Entry object construction could be faster. Maybe we could use one
+ * of the factories instead of fetching the entry again.
+ *
+ * @param array $attrs Attribute names as sort criteria.
+ * @param integer $order Ordering direction, either constant SORT_ASC or
+ * SORT_DESC
*
- * @return array|Horde_Ldap_Error Array with sorted Horde_Ldap_Entries or error
- * @todo Entry object construction could be faster. Maybe we could use one of the factorys instead of fetching the entry again
+ * @return array Sorted entries.
+ * @throws Horde_Ldap_Exception
*/
public function sorted($attrs = array('cn'), $order = SORT_ASC)
{
$return = array();
$sorted = $this->sorted_as_struct($attrs, $order);
- if (PEAR::isError($sorted)) {
- return $sorted;
- }
foreach ($sorted as $key => $row) {
- $entry = $this->_ldap->getEntry($row['dn'], $this->searchedAttrs());
- if (!PEAR::isError($entry)) {
- array_push($return, $entry);
- } else {
- return $entry;
- }
+ $entry = $this->_ldap->getEntry($row['dn'], $this->searchedAttributes());
+ array_push($return, $entry);
}
return $return;
}
/**
- * Return entries as array
+ * Returns entries as array.
*
- * This method returns the entries and the selected attributes values as
- * array.
* The first array level contains all found entries where the keys are the
- * DNs of the entries. The second level arrays contian the entries attributes
- * such that the keys is the lowercased name of the attribute and the values
- * are stored in another indexed array. Note that the attribute values are stored
- * in an array even if there is no or just one value.
+ * DNs of the entries. The second level arrays contian the entries
+ * attributes such that the keys is the lowercased name of the attribute
+ * and the values are stored in another indexed array. Note that the
+ * attribute values are stored in an array even if there is no or just one
+ * value.
*
* The array has the following structure:
* <code>
- * $return = array(
- * 'cn=foo,dc=example,dc=com' => array(
- * 'sn' => array('foo'),
- * 'multival' => array('val1', 'val2', 'valN')
- * )
- * 'cn=bar,dc=example,dc=com' => array(
- * 'sn' => array('bar'),
- * 'multival' => array('val1', 'valN')
- * )
- * )
+ * array(
+ * 'cn=foo,dc=example,dc=com' => array(
+ * 'sn' => array('foo'),
+ * 'multival' => array('val1', 'val2', 'valN')),
+ * 'cn=bar,dc=example,dc=com' => array(
+ * 'sn' => array('bar'),
+ * 'multival' => array('val1', 'valN')))
* </code>
*
- * @return array associative result array as described above
+ * @return array Associative result array as described above.
+ * @throws Horde_Ldap_Exception
*/
public function as_struct()
{
}
/**
- * Set the search objects resource link
+ * Sets the search objects resource link
*
- * @param resource &$search Search result identifier
- *
- * @access public
- * @return void
+ * @param resource $search Search result identifier.
*/
- public function setSearch(&$search)
+ public function setSearch($search)
{
$this->_search = $search;
}
/**
- * Set the ldap ressource link
- *
- * @param resource &$link Link identifier
+ * Sets the LDAP resource link.
*
- * @access public
- * @return void
+ * @param resource $link LDAP link identifier.
*/
- public function setLink(&$link)
+ public function setLink($link)
{
$this->_link = $link;
}
/**
- * Returns the number of entries in the searchresult
+ * Returns the number of entries in the search result.
*
- * @return int Number of entries in search.
+ * @return integer Number of found entries.
*/
public function count()
{
- // this catches the situation where OL returned errno 32 = no such object!
+ // This catches the situation where OL returned errno 32 = no such
+ // object!
if (!$this->_search) {
return 0;
}
}
/**
- * Get the errorcode the object got in its search.
+ * Returns the errorcode from the search.
*
- * @return int The ldap error number.
+ * @return integer The LDAP error number.
*/
public function getErrorCode()
{
}
/**
- * Destructor
+ * Returns the attribute names this search selected.
*
- * @access protected
- */
- public function _Horde_Ldap_Search()
- {
- @ldap_free_result($this->_search);
- }
-
- /**
- * Closes search result
- *
- * @return void
- */
- public function done()
- {
- $this->_Horde_Ldap_Search();
- }
-
- /**
- * Return the attribute names this search selected
+ * @see $_searchedAttrs
*
* @return array
- * @see $_searchedAttrs
- * @access protected
*/
- protected function searchedAttrs()
+ protected function searchedAttributes()
{
return $this->_searchedAttrs;
}
/**
- * Tells if this search exceeds a sizelimit
+ * Returns wheter this search exceeded a sizelimit.
*
- * @return boolean
+ * @return boolean True if the size limit was exceeded.
*/
public function sizeLimitExceeded()
{
- return ($this->getErrorCode() == 4);
+ return $this->getErrorCode() == 4;
}
+ /* SPL Iterator interface methods. This interface allows to use
+ * Horde_Ldap_Search objects directly inside a foreach loop. */
- /*
- * SPL Iterator interface methods.
- * This interface allows to use Horde_Ldap_Search
- * objects directly inside a foreach loop!
- */
/**
- * SPL Iterator interface: Return the current element.
+ * SPL Iterator interface: Returns the current element.
*
* The SPL Iterator interface allows you to fetch entries inside
* a foreach() loop: <code>foreach ($search as $dn => $entry) { ...</code>
* Of course, you may call {@link current()}, {@link key()}, {@link next()},
* {@link rewind()} and {@link valid()} yourself.
*
- * If the search throwed an error, it returns false.
- * False is also returned, if the end is reached
- * In case no call to next() was made, we will issue one,
- * thus returning the first entry.
+ * If the search throwed an error, it returns false. False is also
+ * returned, if the end is reached.
+ *
+ * In case no call to next() was made, we will issue one, thus returning
+ * the first entry.
*
* @return Horde_Ldap_Entry|false
+ * @throws Horde_Ldap_Exception
*/
public function current()
{
reset($this->_iteratorCache);
}
$entry = current($this->_iteratorCache);
- return ($entry instanceof Horde_Ldap_Entry)? $entry : false;
+ return $entry instanceof Horde_Ldap_Entry ? $entry : false;
}
/**
- * SPL Iterator interface: Return the identifying key (DN) of the current entry.
+ * SPL Iterator interface: Returns the identifying key (DN) of the current
+ * entry.
*
* @see current()
- * @return string|false DN of the current entry; false in case no entry is returned by current()
+ * @return string|false DN of the current entry; false in case no entry is
+ * returned by current().
*/
public function key()
{
$entry = $this->current();
- return ($entry instanceof Horde_Ldap_Entry)? $entry->dn() :false;
+ return $entry instanceof Horde_Ldap_Entry ? $entry->dn() :false;
}
/**
- * SPL Iterator interface: Move forward to next entry.
+ * SPL Iterator interface: Moves forward to next entry.
*
- * After a call to {@link next()}, {@link current()} will return
- * the next entry in the result set.
+ * After a call to {@link next()}, {@link current()} will return the next
+ * entry in the result set.
*
* @see current()
- * @return void
+ * @throws Horde_Ldap_Exception
*/
public function next()
{
- // fetch next entry.
- // if we have no entrys anymore, we add false (which is
- // returned by shiftEntry()) so current() will complain.
+ // Fetch next entry. If we have no entries anymore, we add false (which
+ // is returned by shiftEntry()) so current() will complain.
if (count($this->_iteratorCache) - 1 <= $this->count()) {
$this->_iteratorCache[] = $this->shiftEntry();
}
- // move on array pointer to current element.
- // even if we have added all entries, this will
- // ensure proper operation in case we rewind()
+ // Move array pointer to current element. Even if we have added all
+ // entries, this will ensure proper operation in case we rewind().
next($this->_iteratorCache);
}
/**
- * SPL Iterator interface: Check if there is a current element after calls to {@link rewind()} or {@link next()}.
+ * SPL Iterator interface: Checks if there is a current element after calls
+ * to {@link rewind()} or {@link next()}.
*
* Used to check if we've iterated to the end of the collection.
*
* @see current()
- * @return boolean FALSE if there's nothing more to iterate over
+ * @return boolean False if there's nothing more to iterate over.
*/
public function valid()
{
- return ($this->current() instanceof Horde_Ldap_Entry);
+ return $this->current() instanceof Horde_Ldap_Entry;
}
/**
- * SPL Iterator interface: Rewind the Iterator to the first element.
+ * SPL Iterator interface: Rewinds the Iterator to the first element.
*
- * After rewinding, {@link current()} will return the first entry in the result set.
+ * After rewinding, {@link current()} will return the first entry in the
+ * result set.
*
* @see current()
- * @return void
*/
public function rewind()
{
reset($this->_iteratorCache);
}
}
-
-?>
<pearinstaller>
<min>1.5.0</min>
</pearinstaller>
+ <package>
+ <name>Exception</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Util</name>
+ <channel>pear.horde.org</channel>
+ </package>
</required>
</dependencies>
<phprelease>