Current File : //home/strato/chroot/opt/RZphp80/includes/XML/Query2XML/Driver/LDAP2.php
<?php
/**
 * This file contains the class XML_Query2XML_Driver_LDAP2.
 *
 * PHP version 5
 *
 * @category  XML
 * @package   XML_Query2XML
 * @author    Lukas Feiler <lukas.feiler@lukasfeiler.com>
 * @copyright 2007 Lukas Feiler
 * @license   http://www.gnu.org/copyleft/lesser.html  LGPL Version 2.1
 * @version   CVS: $Id: LDAP2.php 259451 2008-05-09 20:54:26Z lukasfeiler $
 * @link      http://pear.php.net/package/XML_Query2XML
 */

/**
 * XML_Query2XML_Driver_LDAP2 extends XML_Query2XML_Driver.
 */
require_once 'XML/Query2XML.php';

/**
 * XML_Query2XML_Driver_LDAP2 uses Net_LDAP2.
 */
require_once 'Net/LDAP2.php';

/**
 * Net_LDAP2_Util is required for its escape_filter_value() method.
 */
require_once 'Net/LDAP2/Util.php';

/**
 * PEAR is required for its isError() method.
 */
require_once 'PEAR.php';

/**
 * Driver for Net_LDAP2.
 *
 * usage:
 * <code>
 * $driver = XML_Query2XML_Driver::factory(new Net_LDAP2(...));
 * </code>
 *
 * This LDAP driver is built upon PEAR Net_LDAP2 and provides three features:
 * - prepare & execute like usage of placeholders in "base" and "filter"
 * - handling missing attributes
 *   in LDAP an entity does not have to use all available attributes,
 *   while XML_Query2XML expects every record to have the same columns;
 *   this driver solves the problem by setting all missing columns to null.
 * - handling multi-value attributes
 *   XML_Query2XML expects every record to be a one-dimensional associative
 *   array. In order to achieve this result this driver creates as many
 *   records for each LDAP entry as are necassary to accomodate all values
 *   of an attribute.
 *
 * @category  XML
 * @package   XML_Query2XML
 * @author    Lukas Feiler <lukas.feiler@lukasfeiler.com>
 * @copyright 2006 Lukas Feiler
 * @license   http://www.gnu.org/copyleft/lesser.html  LGPL Version 2.1
 * @version   Release: 1.7.2
 * @link      http://pear.php.net/package/XML_Query2XML
 * @since     Release 1.7.0RC1
 */
class XML_Query2XML_Driver_LDAP2 extends XML_Query2XML_Driver
{
    /**
     * In instance of Net_LDAP2
     * @var Net_LDAP2
     */
    private $_ldap = null;
    
    /**
     * Constructor
     *
     * @param Net_LDAP2 $ldap An instance of PEAR Net_LDAP2.
     */
    public function __construct(Net_LDAP2 $ldap)
    {
        $this->_ldap = $ldap;
    }
    
    /**
     * Pre-processes LDAP query specifications.
     *
     * @param array  &$query     An array optionally containing the elements
     *                           'base', 'filter', 'options' and 'data'.
     * @param string $configPath The config path; used for exception messages.
     *
     * @return string A string representation of $query
     */
    public function preprocessQuery(&$query, $configPath)
    {
        if (!is_array($query)) {
            /*
             * unit test: XML_Query2XML_Driver_LDAP-preprocessQuery/
             *  throwConfigException_queryNotAnArray.phpt
             */
            throw new XML_Query2XML_ConfigException(
                $configPath . ': array expected, ' . gettype($query) . ' given.'
            );
        }
        $queryStatement = 'basedn:';
        if (isset($query['base'])) {
            $queryStatement .= $query['base'];
        } else {
            $queryStatement .= 'default';
        }
        if (isset($query['filter'])) {
            if (class_exists('Net_LDAP2_Filter') &&
                $query['filter'] instanceof Net_LDAP2_Filter
            ) {
                $queryStatement .= '; filter:' . $query['filter']->asString();
            } else {
                $queryStatement .= '; filter:' . $query['filter'];
            }
        }
        if (isset($query['options'])) {
            $queryStatement .= '; options:' . print_r($query['options'], 1);
        }
        return $queryStatement;
    }
    
    /**
     * Execute a LDAP query stement and fetch all results.
     *
     * @param mixed  $query      The SQL query as a string or an array.
     * @param string $configPath The config path; used for exception messages.
     *
     * @return array An array of records.
     * @throws XML_Query2XML_LDAP2Exception If Net_LDAP2::search() returns an error.
     * @see XML_Query2XML_Driver::getAllRecords()
     */
    public function getAllRecords($query, $configPath)
    {
        $base    = null;
        $filter  = null;
        $options = array();
        if (isset($query['base'])) {
            $base = $query['base'];
        }
        if (isset($query['filter'])) {
            $filter = $query['filter'];
        }
        if (isset($query['options'])) {
            $options = $query['options'];
        }
        
        if (isset($options['query2xml_placeholder'])) {
            $placeholder = $options['query2xml_placeholder'];
        } else {
            $placeholder = '?';
        }
        unset($options['query2xml_placeholder']);
        
        if (isset($query['data']) && is_array($query['data'])) {
            $data = Net_LDAP2_Util::escape_filter_value($query['data']);
            $base = self::_replacePlaceholders($base, $data, $placeholder);
            if (is_string($filter)) {
                $filter = self::_replacePlaceholders($filter, $data, $placeholder);
            }
        }
        $search = $this->_ldap->search($base, $filter, $options);
        
        if (PEAR::isError($search)) {
            /*
             * unit test: getXML/throwLDAPException_queryError.phpt
             */
            throw new XML_Query2XML_LDAP2Exception(
                $configPath . ': Could not run LDAP search query: '
                . $search->toString()
            );
        }
        
        $records = array();
        $entries = $search->entries();
        foreach ($entries as $key => $entry) {
            $records[] = $entry->getValues();
        }
        $search->done();
        
        $records = self::_processMultiValueAttributes($records);
        
        // set missing attriubtes to null
        if (isset($options['attributes']) && is_array($options['attributes'])) {
            foreach ($options['attributes'] as $attribute) {
                for ($i = 0; $i < count($records); $i++) {
                    if (!array_key_exists($attribute, $records[$i])) {
                        $records[$i][$attribute] = null;
                    }
                }
            }
        }
        return $records;
    }
    
    /**
     * Creates multiple records for each entry that has mult-value attributes.
     * XML_Query2XML can only handle records represented by a one-dimensional
     * associative array. An entry like
     * <pre>
     * dn: cn=John Doe,ou=people,dc=example,dc=com
     * cn: John Doe
     * mail: john.doe@example.com
     * mail: jdoe@example.com
     * mail: jd@example.com
     * mobile: 555-666-777
     * mobile: 666-777-888
     * </pre>
     * therefore has to be converted into multiple one-dimensional associative
     * arrays (i.e. records):
     * <pre>
     * cn        mail                  mobile
     * -------------------------------------------------------
     * John Doe  john.doe@example.com  555-666-777
     * John Doe  jdoe@example.com      666-777-888
     * John Doe  jd@example.com        555-666-777
     * </pre>
     * Note that no cartasian product of the mail-values and the mobile-values
     * is produced. The number of records returned is equal to the number
     * values assigned to the attribute that has the most values (here
     * it's the mail attribute that has 3 values). To make sure that every
     * record has valid values for all attributes/columns, we start with
     * the first value after reaching the last one (e.g. the last record
     * for jd@example.com has a mobile of 555-666-777).
     *
     * @param array $entries A multi-dimensional associative array.
     *
     * @return void
     */
    private static function _processMultiValueAttributes($entries)
    {
        $records = array();
        foreach ($entries as $entry) {
            $multiValueAttributes = array();
            
            // will hold the name of the attribute with the most values
            $maxValuesAttribute = null;
            $maxValues          = 0;
            
            // loop over all attributes
            foreach ($entry as $attributeName => $attribute) {
                if (is_array($attribute)) {
                    $multiValueAttributes[$attributeName] = array($attribute, 0);
                    if ($maxValues < count($attribute)) {
                        $maxValues          = count($attribute);
                        $maxValuesAttribute = $attributeName;
                    }
                    $multiValueAttributesMap[$attributeName] = count($attribute);
                }
            }
            
            if (count($multiValueAttributes) > 0) {
                /*
                 * $multiValueAttributes is something like:
                 * array(
                 *   ['email'] => array(
                 *     array(
                 *       'john.doe@example.com'
                 *     ),
                 *     0  // index used to keep track of where we are
                 *   ['telephoneNumber'] => array(
                 *     array(
                 *       '555-111-222',
                 *       '555-222-333'
                 *     ),
                 *     0  // index used to keep track of where we are
                 *   )
                 * )
                 */
                $combinations = array();
                
                $maxValuesAttributeValues =
                    $multiValueAttributes[$maxValuesAttribute][0];
                unset($multiValueAttributes[$maxValuesAttribute]);
                
                foreach ($maxValuesAttributeValues as $value) {
                    $combination                      = array();
                    $combination[$maxValuesAttribute] = $value;
                    
                    /*
                     * Get the next value for each multi-value attribute.
                     * When the last value has been reached start again at
                     * the first one.
                     */
                    foreach (array_keys($multiValueAttributes) as $attributeName) {
                        $values =& $multiValueAttributes[$attributeName][0];
                        $index  =& $multiValueAttributes[$attributeName][1];
                        $count  =& count($values);
                        
                        if ($index == $count) {
                            $index = 0;
                        }
                        $combination[$attributeName] = $values[$index++];
                    }
                    $combinations[] = $combination;
                }
                foreach ($combinations as $combination) {
                    $records[] = array_merge($entry, $combination);
                }
            } else {
                $records[] = $entry;
            }
        }
        return $records;
    }
    
    /**
     * Replaces all placeholder strings (e.g. '?') with replacement strings.
     *
     * @param string $string        The string in which to replace the placeholder
     *                              strings.
     * @param array  &$replacements An array of replacement strings.
     * @param string $placeholder   The placeholder string.
     *
     * @return string The modified version of $string.
     */
    private static function _replacePlaceholders($string,
                                                 &$replacements,
                                                 $placeholder)
    {
        while (($pos = strpos($string, $placeholder)) !== false) {
            if (count($replacements) > 0) {
                $string = substr($string, 0, $pos) .
                          array_shift($replacements) .
                          substr($string, $pos+strlen($placeholder));
            } else {
                break;
            }
        }
        return $string;
    }
}

/**
 * Exception for LDAP errors
 *
 * @category XML
 * @package  XML_Query2XML
 * @author   Lukas Feiler <lukas.feiler@lukasfeiler.com>
 * @license  http://www.gnu.org/copyleft/lesser.html  LGPL Version 2.1
 * @link     http://pear.php.net/package/XML_Query2XML
 */
class XML_Query2XML_LDAP2Exception extends XML_Query2XML_DriverException
{
    /**
     * Constructor
     *
     * @param string $message The error message.
     */
    public function __construct($message)
    {
        parent::__construct($message);
    }
}
?>