Net_LDAP2
[ class tree: Net_LDAP2 ] [ index: Net_LDAP2 ] [ all elements ]

Source for file Search.php

Documentation is available at Search.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3.  
  4. require_once 'PEAR.php';
  5.  
  6. /**
  7. * Result set of an LDAP search
  8. *
  9. @category Net
  10. @package  Net_LDAP2
  11. @author   Tarjej Huse <tarjei@bergfald.no>
  12. @author   Benedikt Hallinger <beni@php.net>
  13. @license  http://www.gnu.org/copyleft/lesser.html LGPL
  14. @version  CVS $Id: Search.php,v 1.3 2008/06/27 06:16:16 beni Exp $
  15. @link     http://pear.php.net/package/Net_LDAP22/
  16. */
  17. class Net_LDAP2_Search extends PEAR implements Iterator
  18. {
  19.     /**
  20.     * Search result identifier
  21.     *
  22.     * @access protected
  23.     * @var resource 
  24.     */
  25.     protected $_search;
  26.  
  27.     /**
  28.     * LDAP resource link
  29.     *
  30.     * @access protected
  31.     * @var resource 
  32.     */
  33.     protected $_link;
  34.  
  35.     /**
  36.     * Net_LDAP2 object
  37.     *
  38.     * A reference of the Net_LDAP2 object for passing to Net_LDAP2_Entry
  39.     *
  40.     * @access protected
  41.     * @var object Net_LDAP2 
  42.     */
  43.     protected $_ldap;
  44.  
  45.     /**
  46.     * Result entry identifier
  47.     *
  48.     * @access protected
  49.     * @var resource 
  50.     */
  51.     protected $_entry = null;
  52.  
  53.     /**
  54.     * The errorcode the search got
  55.     *
  56.     * Some errorcodes might be of interest, but might not be best handled as errors.
  57.     * examples: 4 - LDAP_SIZELIMIT_EXCEEDED - indicates a huge search.
  58.     *               Incomplete results are returned. If you just want to check if there's anything in the search.
  59.     *               than this is a point to handle.
  60.     *           32 - no such object - search here returns a count of 0.
  61.     *
  62.     * @access protected
  63.     * @var int 
  64.     */
  65.     protected $_errorCode = 0; // if not set - sucess!
  66.  
  67.     /**
  68.     * Cache for all entries already fetched from iterator interface
  69.     *
  70.     * @access protected
  71.     * @var array 
  72.     */
  73.     protected $_iteratorCache = array();
  74.  
  75.     /**
  76.     * What attributes we searched for
  77.     *
  78.     * The $attributes array contains the names of the searched attributes and gets
  79.     * passed from $Net_LDAP2->search() so the Net_LDAP2_Search object can tell
  80.     * what attributes was searched for ({@link _searchedAttrs())}
  81.     *
  82.     * This variable gets set from the constructor and returned
  83.     * from {@link _searchedAttrs()}
  84.     *
  85.     * @access protected
  86.     * @var array 
  87.     */
  88.     protected $_searchedAttrs = array();
  89.  
  90.     /**
  91.     * Cache variable for storing entries fetched internally
  92.     *
  93.     * This currently is only used by {@link pop_entry()}
  94.     *
  95.     * @access protected
  96.     * @var array 
  97.     */
  98.     protected $_entry_cache = false;
  99.  
  100.     /**
  101.     * Constructor
  102.     *
  103.     * @param resource          &$search    Search result identifier
  104.     * @param Net_LDAP2|resource&$ldap      Net_LDAP2 object or just a LDAP-Link resource
  105.     * @param array             $attributes (optional) Array with searched attribute names. (see {@link $_searchedAttrs})
  106.     *
  107.     * @access public
  108.     */
  109.     public function __construct(&$search&$ldap$attributes = array())
  110.     {
  111.         $this->PEAR('Net_LDAP2_Error');
  112.  
  113.         $this->setSearch($search);
  114.  
  115.         if ($ldap instanceof Net_LDAP2{
  116.             $this->_ldap =$ldap;
  117.             $this->setLink($this->_ldap->getLink());
  118.         else {
  119.             $this->setLink($ldap);
  120.         }
  121.  
  122.         $this->_errorCode = @ldap_errno($this->_link);
  123.  
  124.         if (is_array($attributes&& !empty($attributes)) {
  125.             $this->_searchedAttrs = $attributes;
  126.         }
  127.     }
  128.  
  129.     /**
  130.     * Returns an array of entry objects
  131.     *
  132.     * @return array Array of entry objects.
  133.     */
  134.     public function entries()
  135.     {
  136.         $entries = array();
  137.  
  138.         while ($entry $this->shiftEntry()) {
  139.             $entries[$entry;
  140.         }
  141.  
  142.         return $entries;
  143.     }
  144.  
  145.     /**
  146.     * Get the next entry in the searchresult.
  147.     *
  148.     * This will return a valid Net_LDAP2_Entry object or false, so
  149.     * you can use this method to easily iterate over the entries inside
  150.     * a while loop.
  151.     *
  152.     * @return Net_LDAP2_Entry|false Reference to Net_LDAP2_Entry object or false
  153.     */
  154.     public function &shiftEntry()
  155.     {
  156.         if ($this->count(== 0 {
  157.             $false = false;
  158.             return $false;
  159.         }
  160.  
  161.         if (is_null($this->_entry)) {
  162.             $this->_entry = @ldap_first_entry($this->_link$this->_search);
  163.             $entry Net_LDAP2_Entry::createConnected($this->_ldap$this->_entry);
  164.             if ($entry instanceof Net_LDAP2_Error$entry = false;
  165.         else {
  166.             if (!$this->_entry = @ldap_next_entry($this->_link$this->_entry)) {
  167.                 $false = false;
  168.                 return $false;
  169.             }
  170.             $entry Net_LDAP2_Entry::createConnected($this->_ldap$this->_entry);
  171.             if ($entry instanceof Net_LDAP2_Error$entry = false;
  172.         }
  173.         return $entry;
  174.     }
  175.  
  176.     /**
  177.     * Alias function of shiftEntry() for perl-ldap interface
  178.     *
  179.     * @see shiftEntry()
  180.     */
  181.     public function shift_entry()
  182.     {
  183.         $args func_get_args();
  184.         return call_user_func_array(array&$this'shiftEntry' )$args);
  185.     }
  186.  
  187.     /**
  188.     * Retrieve the next entry in the searchresult, but starting from last entry
  189.     *
  190.     * This is the opposite to {@link shiftEntry()} and is also very useful
  191.     * to be used inside a while loop.
  192.     *
  193.     * @return Net_LDAP2_Entry|false
  194.     */
  195.     public function popEntry()
  196.     {
  197.         if (false === $this->_entry_cache{
  198.             // fetch entries into cache if not done so far
  199.             $this->_entry_cache = $this->entries();
  200.         }
  201.  
  202.         $return array_pop($this->_entry_cache);
  203.         return (null === $return)? false : $return;
  204.     }
  205.  
  206.     /**
  207.     * Alias function of popEntry() for perl-ldap interface
  208.     *
  209.     * @see popEntry()
  210.     */
  211.     public function pop_entry()
  212.     {
  213.         $args func_get_args();
  214.         return call_user_func_array(array&$this'popEntry' )$args);
  215.     }
  216.  
  217.     /**
  218.     * Return entries sorted as array
  219.     *
  220.     * This returns a array with sorted entries and the values.
  221.     * Sorting is done with PHPs {@link array_multisort()}.
  222.     * This method relies on {@link as_struct()} to fetch the raw data of the entries.
  223.     *
  224.     * Please note that attribute names are case sensitive!
  225.     *
  226.     * Usage example:
  227.     * <code>
  228.     *   // to sort entries first by location, then by surename, but descending:
  229.     *   $entries = $search->sorted_as_struct(array('locality','sn'), SORT_DESC);
  230.     * </code>
  231.     *
  232.     * @param array $attrs Array of attribute names to sort; order from left to right.
  233.     * @param int   $order Ordering direction, either constant SORT_ASC or SORT_DESC
  234.     *
  235.     * @return array|Net_LDAP2_Error  Array with sorted entries or error
  236.     * @todo what about server side sorting as specified in http://www.ietf.org/rfc/rfc2891.txt?
  237.     */
  238.     public function sorted_as_struct($attrs = array('cn')$order = SORT_ASC)
  239.     {
  240.         /*
  241.         * Old Code, suitable and fast for single valued sorting
  242.         * This code should be used if we know that single valued sorting is desired,
  243.         * but we need some method to get that knowledge...
  244.         */
  245.         /*
  246.         $attrs = array_reverse($attrs);
  247.         foreach ($attrs as $attribute) {
  248.             if (!ldap_sort($this->_link, $this->_search, $attribute)){
  249.                 $this->raiseError("Sorting failed for Attribute " . $attribute);
  250.             }
  251.         }
  252.  
  253.         $results = ldap_get_entries($this->_link, $this->_search);
  254.  
  255.         unset($results['count']); //for tidier output
  256.         if ($order) {
  257.             return array_reverse($results);
  258.         } else {
  259.             return $results;
  260.         }*/
  261.  
  262.         /*
  263.         * New code: complete "client side" sorting
  264.         */
  265.         // first some parameterchecks
  266.         if (!is_array($attrs)) {
  267.             return PEAR::raiseError("Sorting failed: Parameterlist must be an array!");
  268.         }
  269.         if ($order != SORT_ASC && $order != SORT_DESC{
  270.             return PEAR::raiseError("Sorting failed: sorting direction not understood! (neither constant SORT_ASC nor SORT_DESC)");
  271.         }
  272.  
  273.         // fetch the entries data
  274.         $entries $this->as_struct();
  275.  
  276.         // now sort each entries attribute values
  277.         // this is neccessary because later we can only sort by one value,
  278.         // so we need the highest or lowest attribute now, depending on the
  279.         // selected ordering for that specific attribute
  280.         foreach ($entries as $dn => $entry{
  281.             foreach ($entry as $attr_name => $attr_values{
  282.                 sort($entries[$dn][$attr_name]);
  283.                 if ($order == SORT_DESC{
  284.                     array_reverse($entries[$dn][$attr_name]);
  285.                 }
  286.             }
  287.         }
  288.  
  289.         // reformat entrys array for later use with array_multisort()
  290.         $to_sort = array()// <- will be a numeric array similar to ldap_get_entries
  291.         foreach ($entries as $dn => $entry_attr{
  292.             $row       = array();
  293.             $row['dn'$dn;
  294.             foreach ($entry_attr as $attr_name => $attr_values{
  295.                 $row[$attr_name$attr_values;
  296.             }
  297.             $to_sort[$row;
  298.         }
  299.  
  300.         // Build columns for array_multisort()
  301.         // each requested attribute is one row
  302.         $columns = array();
  303.         foreach ($attrs as $attr_name{
  304.             foreach ($to_sort as $key => $row{
  305.                 $columns[$attr_name][$key=$to_sort[$key][$attr_name][0];
  306.             }
  307.         }
  308.  
  309.         // sort the colums with array_multisort, if there is something
  310.         // to sort and if we have requested sort columns
  311.         if (!empty($to_sort&& !empty($columns)) {
  312.             $sort_params '';
  313.             foreach ($attrs as $attr_name{
  314.                 $sort_params .= '$columns[\''.$attr_name.'\'], '.$order.', ';
  315.             }
  316.             eval("array_multisort($sort_params \$to_sort);")// perform sorting
  317.         }
  318.  
  319.         return $to_sort;
  320.     }
  321.  
  322.     /**
  323.     * Return entries sorted as objects
  324.     *
  325.     * This returns a array with sorted Net_LDAP2_Entry objects.
  326.     * The sorting is actually done with {@link sorted_as_struct()}.
  327.     *
  328.     * Please note that attribute names are case sensitive!
  329.     * Also note, that it is (depending on server capabilitys) possible to let
  330.     * the server sort your results. This happens through search controls
  331.     * and is described in detail at {@link http://www.ietf.org/rfc/rfc2891.txt}
  332.     *
  333.     * Usage example:
  334.     * <code>
  335.     *   // to sort entries first by location, then by surename, but descending:
  336.     *   $entries = $search->sorted(array('locality','sn'), SORT_DESC);
  337.     * </code>
  338.     *
  339.     * @param array $attrs Array of sort attributes to sort; order from left to right.
  340.     * @param int   $order Ordering direction, either constant SORT_ASC or SORT_DESC
  341.     *
  342.     * @return array|Net_LDAP2_Error  Array with sorted Net_LDAP2_Entries or error
  343.     * @todo Entry object construction could be faster. Maybe we could use one of the factorys instead of fetching the entry again
  344.     */
  345.     public function sorted($attrs = array('cn')$order = SORT_ASC)
  346.     {
  347.         $return = array();
  348.         $sorted $this->sorted_as_struct($attrs$order);
  349.         if (PEAR::isError($sorted)) {
  350.             return $sorted;
  351.         }
  352.         foreach ($sorted as $key => $row{
  353.             $entry $this->_ldap->getEntry($row['dn']$this->_searchedAttrs());
  354.             if (!PEAR::isError($entry)) {
  355.                 array_push($return$entry);
  356.             else {
  357.                 return $entry;
  358.             }
  359.         }
  360.         return $return;
  361.     }
  362.  
  363.     /**
  364.     * Return entries as array
  365.     *
  366.     * This method returns the entries and the selected attributes values as
  367.     * array.
  368.     * The first array level contains all found entries where the keys are the
  369.     * DNs of the entries. The second level arrays contian the entries attributes
  370.     * such that the keys is the lowercased name of the attribute and the values
  371.     * are stored in another indexed array. Note that the attribute values are stored
  372.     * in an array even if there is no or just one value.
  373.     *
  374.     * The array has the following structure:
  375.     * <code>
  376.     * $return = array(
  377.     *           'cn=foo,dc=example,dc=com' => array(
  378.     *                                                'sn'       => array('foo'),
  379.     *                                                'multival' => array('val1', 'val2', 'valN')
  380.     *                                             )
  381.     *           'cn=bar,dc=example,dc=com' => array(
  382.     *                                                'sn'       => array('bar'),
  383.     *                                                'multival' => array('val1', 'valN')
  384.     *                                             )
  385.     *           )
  386.     * </code>
  387.     *
  388.     * @return array      associative result array as described above
  389.     */
  390.     public function as_struct()
  391.     {
  392.         $return  = array();
  393.         $entries $this->entries();
  394.         foreach ($entries as $entry{
  395.             $attrs            = array();
  396.             $entry_attributes $entry->attributes();
  397.             foreach ($entry_attributes as $attr_name{
  398.                 $attr_values $entry->getValue($attr_name'all');
  399.                 if (!is_array($attr_values)) {
  400.                     $attr_values = array($attr_values);
  401.                 }
  402.                 $attrs[$attr_name$attr_values;
  403.             }
  404.             $return[$entry->dn()$attrs;
  405.         }
  406.         return $return;
  407.     }
  408.  
  409.     /**
  410.     * Set the search objects resource link
  411.     *
  412.     * @param resource &$search Search result identifier
  413.     *
  414.     * @access public
  415.     * @return void 
  416.     */
  417.     public function setSearch(&$search)
  418.     {
  419.         $this->_search = $search;
  420.     }
  421.  
  422.     /**
  423.     * Set the ldap ressource link
  424.     *
  425.     * @param resource &$link Link identifier
  426.     *
  427.     * @access public
  428.     * @return void 
  429.     */
  430.     public function setLink(&$link)
  431.     {
  432.         $this->_link = $link;
  433.     }
  434.  
  435.     /**
  436.     * Returns the number of entries in the searchresult
  437.     *
  438.     * @return int Number of entries in search.
  439.     */
  440.     public function count()
  441.     {
  442.         // this catches the situation where OL returned errno 32 = no such object!
  443.         if (!$this->_search{
  444.             return 0;
  445.         }
  446.         return @ldap_count_entries($this->_link$this->_search);
  447.     }
  448.  
  449.     /**
  450.     * Get the errorcode the object got in its search.
  451.     *
  452.     * @return int The ldap error number.
  453.     */
  454.     public function getErrorCode()
  455.     {
  456.         return $this->_errorCode;
  457.     }
  458.  
  459.     /**
  460.     * Destructor
  461.     *
  462.     * @access protected
  463.     */
  464.     public function _Net_LDAP2_Search()
  465.     {
  466.         @ldap_free_result($this->_search);
  467.     }
  468.  
  469.     /**
  470.     * Closes search result
  471.     *
  472.     * @return void 
  473.     */
  474.     public function done()
  475.     {
  476.         $this->_Net_LDAP2_Search();
  477.     }
  478.  
  479.     /**
  480.     * Return the attribute names this search selected
  481.     *
  482.     * @return array 
  483.     * @see $_searchedAttrs
  484.     * @access protected
  485.     */
  486.     public function _searchedAttrs()
  487.     {
  488.         return $this->_searchedAttrs;
  489.     }
  490.  
  491.     /**
  492.     * Tells if this search exceeds a sizelimit
  493.     *
  494.     * @return boolean 
  495.     */
  496.     public function sizeLimitExceeded()
  497.     {
  498.         return ($this->getErrorCode(== 4);
  499.     }
  500.  
  501.  
  502.     /*
  503.     * SPL Iterator interface methods.
  504.     * This interface allows to use Net_LDAP2_Search
  505.     * objects directly inside a foreach loop!
  506.     */
  507.     /**
  508.     * SPL Iterator interface: Return the current element.
  509.     *
  510.     * The SPL Iterator interface allows you to fetch entries inside
  511.     * a foreach() loop: <code>foreach ($search as $dn => $entry) { ...</code>
  512.     *
  513.     * Of course, you may call {@link current()}{@link key()}{@link next()},
  514.     * {@link rewind()} and {@link valid()} yourself.
  515.     *
  516.     * If the search throwed an error, it returns false.
  517.     * False is also returned, if the end is reached
  518.     * In case no call to next() was made, we will issue one,
  519.     * thus returning the first entry.
  520.     *
  521.     * @return Net_LDAP2_Entry|false
  522.     */
  523.     public function current()
  524.     {
  525.         if (count($this->_iteratorCache== 0{
  526.             $this->next();
  527.             reset($this->_iteratorCache);
  528.         }
  529.         $entry current($this->_iteratorCache);
  530.         return ($entry instanceof Net_LDAP2_Entry)$entry : false;
  531.     }
  532.  
  533.     /**
  534.     * SPL Iterator interface: Return the identifying key (DN) of the current entry.
  535.     *
  536.     * @see current()
  537.     * @return string|falseDN of the current entry; false in case no entry is returned by current()
  538.     */
  539.     public function key()
  540.     {
  541.         $entry $this->current();
  542.         return ($entry instanceof Net_LDAP2_Entry)$entry->dn(:false;
  543.     }
  544.  
  545.     /**
  546.     * SPL Iterator interface: Move forward to next entry.
  547.     *
  548.     * After a call to {@link next()}{@link current()} will return
  549.     * the next entry in the result set.
  550.     *
  551.     * @see current()
  552.     * @return void 
  553.     */
  554.     public function next()
  555.     {
  556.         // fetch next entry.
  557.         // if we have no entrys anymore, we add false (which is
  558.         // returned by shiftEntry()) so current() will complain.
  559.         if (count($this->_iteratorCache- 1 <= $this->count()) {
  560.             $this->_iteratorCache[$this->shiftEntry();
  561.         }
  562.  
  563.         // move on array pointer to current element.
  564.         // even if we have added all entries, this will
  565.         // ensure proper operation in case we rewind()
  566.         next($this->_iteratorCache);
  567.     }
  568.  
  569.     /**
  570.     * SPL Iterator interface:  Check if there is a current element after calls to {@link rewind()} or {@link next()}.
  571.     *
  572.     * Used to check if we've iterated to the end of the collection.
  573.     *
  574.     * @see current()
  575.     * @return boolean FALSE if there's nothing more to iterate over
  576.     */
  577.     public function valid()
  578.     {
  579.         return ($this->current(instanceof Net_LDAP2_Entry);
  580.     }
  581.  
  582.     /**
  583.     * SPL Iterator interface: Rewind the Iterator to the first element.
  584.     *
  585.     * After rewinding, {@link current()} will return the first entry in the result set.
  586.     *
  587.     * @see current()
  588.     * @return void 
  589.     */
  590.     public function rewind()
  591.     {
  592.         reset($this->_iteratorCache);
  593.     }
  594. }
  595.  
  596. ?>

Documentation generated on Mon, 11 Mar 2019 15:25:55 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.