Source for file LDAP.php

Documentation is available at LDAP.php

  1. <?php
  2. /**
  3.  * This file contains the class XML_Query2XML_Driver_LDAP.
  4.  *
  5.  * PHP version 5
  6.  *
  7.  * @category  XML
  8.  * @package   XML_Query2XML
  9.  * @author    Lukas Feiler <lukas.feiler@lukasfeiler.com>
  10.  * @copyright 2007 Lukas Feiler
  11.  * @license   http://www.gnu.org/copyleft/lesser.html  LGPL Version 2.1
  12.  * @version   CVS: $Id: LDAP.php 259451 2008-05-09 20:54:26Z lukasfeiler $
  13.  * @link      http://pear.php.net/package/XML_Query2XML
  14.  */
  15.  
  16. /**
  17.  * XML_Query2XML_Driver_LDAP extends XML_Query2XML_Driver.
  18.  */
  19. require_once 'XML/Query2XML.php';
  20.  
  21. /**
  22.  * XML_Query2XML_Driver_LDAP uses Net_LDAP.
  23.  */
  24. require_once 'Net/LDAP.php';
  25.  
  26. /**
  27.  * Net_LDAP_Util is required for its escape_filter_value() method.
  28.  */
  29. require_once 'Net/LDAP/Util.php';
  30.  
  31. /**
  32.  * PEAR is required for its isError() method.
  33.  */
  34. require_once 'PEAR.php';
  35.  
  36. /**
  37.  * Driver for Net_LDAP.
  38.  *
  39.  * usage:
  40.  * <code>
  41.  * $driver = XML_Query2XML_Driver::factory(new Net_LDAP(...));
  42.  * </code>
  43.  *
  44.  * This LDAP driver provides three features:
  45.  * - prepare & execute like usage of placeholders in "base" and "filter"
  46.  * - handling missing attributes
  47.  *   in LDAP an entity does not have to use all available attributes,
  48.  *   while XML_Query2XML expects every record to have the same columns;
  49.  *   this driver solves the problem by setting all missing columns to null.
  50.  * - handling multi-value attributes
  51.  *   XML_Query2XML expects every record to be a one-dimensional associative
  52.  *   array. In order to achieve this result this driver creates as many
  53.  *   records for each LDAP entry as are necassary to accomodate all values
  54.  *   of an attribute.
  55.  *
  56.  * @category  XML
  57.  * @package   XML_Query2XML
  58.  * @author    Lukas Feiler <lukas.feiler@lukasfeiler.com>
  59.  * @copyright 2006 Lukas Feiler
  60.  * @license   http://www.gnu.org/copyleft/lesser.html  LGPL Version 2.1
  61.  * @version   Release: 1.7.2
  62.  * @link      http://pear.php.net/package/XML_Query2XML
  63.  * @since     Release 1.6.0RC1
  64.  */
  65. {
  66.     /**
  67.      * In instance of Net_LDAP
  68.      * @var Net_LDAP 
  69.      */
  70.     private $_ldap null;
  71.     
  72.     /**
  73.      * Constructor
  74.      *
  75.      * @param Net_LDAP $ldap An instance of PEAR Net_LDAP.
  76.      */
  77.     public function __construct(Net_LDAP $ldap)
  78.     {
  79.         $this->_ldap $ldap;
  80.     }
  81.     
  82.     /**
  83.      * Pre-processes LDAP query specifications.
  84.      *
  85.      * @param array  &$query     An array optionally containing the elements
  86.      *                            'base', 'filter', 'options' and 'data'.
  87.      * @param string $configPath The config path; used for exception messages.
  88.      *
  89.      * @return string A string representation of $query
  90.      */
  91.     public function preprocessQuery(&$query$configPath)
  92.     {
  93.         if (!is_array($query)) {
  94.             /*
  95.              * unit test: XML_Query2XML_Driver_LDAP-preprocessQuery/
  96.              *  throwConfigException_queryNotAnArray.phpt
  97.              */
  98.             throw new XML_Query2XML_ConfigException(
  99.                 $configPath ': array expected, ' gettype($query' given.'
  100.             );
  101.         }
  102.         $queryStatement 'basedn:';
  103.         if (isset($query['base'])) {
  104.             $queryStatement .= $query['base'];
  105.         else {
  106.             $queryStatement .= 'default';
  107.         }
  108.         if (isset($query['filter'])) {
  109.             if (class_exists('Net_LDAP_Filter'&&
  110.                 $query['filter'instanceof Net_LDAP_Filter
  111.             {
  112.                 $queryStatement .= '; filter:' $query['filter']->asString();
  113.             else {
  114.                 $queryStatement .= '; filter:' $query['filter'];
  115.             }
  116.         }
  117.         if (isset($query['options'])) {
  118.             $queryStatement .= '; options:' print_r($query['options']1);
  119.         }
  120.         return $queryStatement;
  121.     }
  122.     
  123.     /**
  124.      * Execute a LDAP query stement and fetch all results.
  125.      *
  126.      * @param mixed  $query      The SQL query as a string or an array.
  127.      * @param string $configPath The config path; used for exception messages.
  128.      *
  129.      * @return array An array of records.
  130.      * @throws XML_Query2XML_LDAPException If Net_LDAP::search() returns an error.
  131.      * @see XML_Query2XML_Driver::getAllRecords()
  132.      */
  133.     public function getAllRecords($query$configPath)
  134.     {
  135.         $base    null;
  136.         $filter  null;
  137.         $options array();
  138.         if (isset($query['base'])) {
  139.             $base $query['base'];
  140.         }
  141.         if (isset($query['filter'])) {
  142.             $filter $query['filter'];
  143.         }
  144.         if (isset($query['options'])) {
  145.             $options $query['options'];
  146.         }
  147.         
  148.         if (isset($options['query2xml_placeholder'])) {
  149.             $placeholder $options['query2xml_placeholder'];
  150.         else {
  151.             $placeholder '?';
  152.         }
  153.         unset($options['query2xml_placeholder']);
  154.         
  155.         if (isset($query['data']&& is_array($query['data'])) {
  156.             $data Net_LDAP_Util::escape_filter_value($query['data']);
  157.             $base self::_replacePlaceholders($base$data$placeholder);
  158.             if (is_string($filter)) {
  159.                 $filter self::_replacePlaceholders($filter$data$placeholder);
  160.             }
  161.         }
  162.         $search $this->_ldap->search($base$filter$options);
  163.         
  164.         if (PEAR::isError($search)) {
  165.             /*
  166.              * unit test: getXML/throwLDAPException_queryError.phpt
  167.              */
  168.             throw new XML_Query2XML_LDAPException(
  169.                 $configPath ': Could not run LDAP search query: '
  170.                 . $search->toString()
  171.             );
  172.         }
  173.         
  174.         $records array();
  175.         $entries $search->entries();
  176.         foreach ($entries as $key => $entry{
  177.             $records[$entry->getValues();
  178.         }
  179.         $search->done();
  180.         
  181.         $records self::_processMultiValueAttributes($records);
  182.         
  183.         // set missing attriubtes to null
  184.         if (isset($options['attributes']&& is_array($options['attributes'])) {
  185.             foreach ($options['attributes'as $attribute{
  186.                 for ($i 0$i count($records)$i++{
  187.                     if (!array_key_exists($attribute$records[$i])) {
  188.                         $records[$i][$attributenull;
  189.                     }
  190.                 }
  191.             }
  192.         }
  193.         return $records;
  194.     }
  195.     
  196.     /**
  197.      * Creates multiple records for each entry that has mult-value attributes.
  198.      * XML_Query2XML can only handle records represented by a one-dimensional
  199.      * associative array. An entry like
  200.      * <pre>
  201.      * dn: cn=John Doe,ou=people,dc=example,dc=com
  202.      * cn: John Doe
  203.      * mail: john.doe@example.com
  204.      * mail: jdoe@example.com
  205.      * mail: jd@example.com
  206.      * mobile: 555-666-777
  207.      * mobile: 666-777-888
  208.      * </pre>
  209.      * therefore has to be converted into multiple one-dimensional associative
  210.      * arrays (i.e. records):
  211.      * <pre>
  212.      * cn        mail                  mobile
  213.      * -------------------------------------------------------
  214.      * John Doe  john.doe@example.com  555-666-777
  215.      * John Doe  jdoe@example.com      666-777-888
  216.      * John Doe  jd@example.com        555-666-777
  217.      * </pre>
  218.      * Note that no cartasian product of the mail-values and the mobile-values
  219.      * is produced. The number of records returned is equal to the number
  220.      * values assigned to the attribute that has the most values (here
  221.      * it's the mail attribute that has 3 values). To make sure that every
  222.      * record has valid values for all attributes/columns, we start with
  223.      * the first value after reaching the last one (e.g. the last record
  224.      * for jd@example.com has a mobile of 555-666-777).
  225.      *
  226.      * @param array $entries A multi-dimensional associative array.
  227.      *
  228.      * @return void 
  229.      */
  230.     private static function _processMultiValueAttributes($entries)
  231.     {
  232.         $records array();
  233.         foreach ($entries as $entry{
  234.             $multiValueAttributes array();
  235.             
  236.             // will hold the name of the attribute with the most values
  237.             $maxValuesAttribute null;
  238.             $maxValues          0;
  239.             
  240.             // loop over all attributes
  241.             foreach ($entry as $attributeName => $attribute{
  242.                 if (is_array($attribute)) {
  243.                     $multiValueAttributes[$attributeNamearray($attribute0);
  244.                     if ($maxValues count($attribute)) {
  245.                         $maxValues          count($attribute);
  246.                         $maxValuesAttribute $attributeName;
  247.                     }
  248.                     $multiValueAttributesMap[$attributeNamecount($attribute);
  249.                 }
  250.             }
  251.             
  252.             if (count($multiValueAttributes0{
  253.                 /*
  254.                  * $multiValueAttributes is something like:
  255.                  * array(
  256.                  *   ['email'] => array(
  257.                  *     array(
  258.                  *       'john.doe@example.com'
  259.                  *     ),
  260.                  *     0  // index used to keep track of where we are
  261.                  *   ['telephoneNumber'] => array(
  262.                  *     array(
  263.                  *       '555-111-222',
  264.                  *       '555-222-333'
  265.                  *     ),
  266.                  *     0  // index used to keep track of where we are
  267.                  *   )
  268.                  * )
  269.                  */
  270.                 $combinations array();
  271.                 
  272.                 $maxValuesAttributeValues =
  273.                     $multiValueAttributes[$maxValuesAttribute][0];
  274.                 unset($multiValueAttributes[$maxValuesAttribute]);
  275.                 
  276.                 foreach ($maxValuesAttributeValues as $value{
  277.                     $combination                      array();
  278.                     $combination[$maxValuesAttribute$value;
  279.                     
  280.                     /*
  281.                      * Get the next value for each multi-value attribute.
  282.                      * When the last value has been reached start again at
  283.                      * the first one.
  284.                      */
  285.                     foreach (array_keys($multiValueAttributesas $attributeName{
  286.                         $values =$multiValueAttributes[$attributeName][0];
  287.                         $index  =$multiValueAttributes[$attributeName][1];
  288.                         $count  =count($values);
  289.                         
  290.                         if ($index == $count{
  291.                             $index 0;
  292.                         }
  293.                         $combination[$attributeName$values[$index++];
  294.                     }
  295.                     $combinations[$combination;
  296.                 }
  297.                 foreach ($combinations as $combination{
  298.                     $records[array_merge($entry$combination);
  299.                 }
  300.             else {
  301.                 $records[$entry;
  302.             }
  303.         }
  304.         return $records;
  305.     }
  306.     
  307.     /**
  308.      * Replaces all placeholder strings (e.g. '?') with replacement strings.
  309.      *
  310.      * @param string $string        The string in which to replace the placeholder
  311.      *                               strings.
  312.      * @param array  &$replacements An array of replacement strings.
  313.      * @param string $placeholder   The placeholder string.
  314.      *
  315.      * @return string The modified version of $string.
  316.      */
  317.     private static function _replacePlaceholders($string,
  318.                                                  &$replacements,
  319.                                                  $placeholder)
  320.     {
  321.         while (($pos strpos($string$placeholder)) !== false{
  322.             if (count($replacements0{
  323.                 $string substr($string0$pos.
  324.                           array_shift($replacements.
  325.                           substr($string$pos+strlen($placeholder));
  326.             else {
  327.                 break;
  328.             }
  329.         }
  330.         return $string;
  331.     }
  332. }
  333.  
  334. /**
  335.  * Exception for LDAP errors
  336.  *
  337.  * @category XML
  338.  * @package  XML_Query2XML
  339.  * @author   Lukas Feiler <lukas.feiler@lukasfeiler.com>
  340.  * @license  http://www.gnu.org/copyleft/lesser.html  LGPL Version 2.1
  341.  * @link     http://pear.php.net/package/XML_Query2XML
  342.  */
  343. {
  344.     /**
  345.      * Constructor
  346.      *
  347.      * @param string $message The error message.
  348.      */
  349.     public function __construct($message)
  350.     {
  351.         parent::__construct($message);
  352.     }
  353. }
  354. ?>

Documentation generated on Sun, 03 Apr 2011 13:13:08 +0200 by phpDocumentor 1.4.1