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

Source for file Filter.php

Documentation is available at Filter.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3.  
  4. require_once 'PEAR.php';
  5. require_once 'Util.php';
  6.  
  7. /**
  8. * Object representation of a part of a LDAP filter.
  9. *
  10. * This Class is not completely compatible to the PERL interface!
  11. *
  12. * The purpose of this class is, that users can easily build LDAP filters
  13. * without having to worry about right escaping etc.
  14. * A Filter is built using several independent filter objects
  15. * which are combined afterwards. This object works in two
  16. * modes, depending how the object is created.
  17. * If the object is created using the {@link create()} method, then this is a leaf-object.
  18. * If the object is created using the {@link combine()} method, then this is a container object.
  19. *
  20. * LDAP filters are defined in RFC-2254 and can be found under
  21. {@link http://www.ietf.org/rfc/rfc2254.txt}
  22. *
  23. * Here a quick copy&paste example:
  24. * <code>
  25. * $filter0 = Net_LDAP_Filter::create('stars', 'equals', '***');
  26. * $filter_not0 = Net_LDAP_Filter::combine('not', $filter0);
  27. *
  28. * $filter1 = Net_LDAP_Filter::create('gn', 'begins', 'bar');
  29. * $filter2 = Net_LDAP_Filter::create('gn', 'ends', 'baz');
  30. * $filter_comp = Net_LDAP_Filter::combine('or',array($filter_not0, $filter1, $filter2));
  31. *
  32. * echo $filter_comp->asString();
  33. * // This will output: (|(!(stars=\0x5c0x2a\0x5c0x2a\0x5c0x2a))(gn=bar*)(gn=*baz))
  34. * // The stars in $filter0 are treaten as real stars unless you disable escaping.
  35. * </code>
  36. *
  37. @category Net
  38. @package  Net_LDAP
  39. @author   Benedikt Hallinger <beni@php.net>
  40. @license  http://www.gnu.org/copyleft/lesser.html LGPL
  41. @version  CVS: $Id: Filter.php,v 1.27 2008/06/04 06:12:04 beni Exp $
  42. @link     http://pear.php.net/package/Net_LDAP/
  43. */
  44. class Net_LDAP_Filter extends PEAR
  45. {
  46.     /**
  47.     * Storage for combination of filters
  48.     *
  49.     * This variable holds a array of filter objects
  50.     * that should be combined by this filter object.
  51.     *
  52.     * @access private
  53.     * @var array 
  54.     */
  55.     var $_subfilters = array();
  56.  
  57.     /**
  58.     * Match of this filter
  59.     *
  60.     * If this is a leaf filter, then a matching rule is stored,
  61.     * if it is a container, then it is a logical operator
  62.     *
  63.     * @access private
  64.     * @var string 
  65.     */
  66.     var $_match;
  67.  
  68.     /**
  69.     * Single filter
  70.     *
  71.     * If we operate in leaf filter mode,
  72.     * then the constructing method stores
  73.     * the filter representation here
  74.     *
  75.     * @acces private
  76.     * @var string 
  77.     */
  78.     var $_filter;
  79.  
  80.     /**
  81.     * Create a new Net_LDAP_Filter object and parse $filter.
  82.     *
  83.     * This is for PERL Net::LDAP interface.
  84.     * Construction of Net_LDAP_Filter objects should happen through either
  85.     * {@link create()} or {@link combine()} which give you more control.
  86.     * However, you may use the perl iterface if you already have generated filters.
  87.     *
  88.     * @param string $filter LDAP filter string
  89.     *
  90.     * @see parse()
  91.     */
  92.     function Net_LDAP_Filter($filter = false)
  93.     {
  94.         // The optional parameter must remain here, because otherwise create() crashes
  95.         if (false !== $filter{
  96.             $filter_o Net_LDAP_Filter::parse($filter);
  97.             if (PEAR::isError($filter_o)) {
  98.                 $this->_filter $filter_o// assign error, so asString() can report it
  99.             else {
  100.                 $this->_filter $filter_o->asString();
  101.             }
  102.         }
  103.     }
  104.  
  105.     /**
  106.     * Constructor of a new part of a LDAP filter.
  107.     *
  108.     * The following matching rules exists:
  109.     *    - equals:         One of the attributes values is exactly $value
  110.     *                      Please note that case sensitiviness is depends on the
  111.     *                      attributes syntax configured in the server.
  112.     *    - begins:         One of the attributes values must begin with $value
  113.     *    - ends:           One of the attributes values must end with $value
  114.     *    - contains:       One of the attributes values must contain $value
  115.     *    - any:            The attribute can contain any value but must be existent
  116.     *    - greater:        The attributes value is greater than $value
  117.     *    - less:           The attributes value is less than $value
  118.     *    - greaterOrEqual: The attributes value is greater or equal than $value
  119.     *    - lessOrEqual:    The attributes value is less or equal than $value
  120.     *    - approx:         One of the attributes values is similar to $value
  121.     *
  122.     * If $escape is set to true (default) then $value will be escaped
  123.     * properly. If it is set to false then $value will be treaten as raw value.
  124.     *
  125.     * Examples:
  126.     * <code>
  127.     *   // This will find entries that contain an attribute "sn" that ends with "foobar":
  128.     *   $filter = new Net_LDAP_Filter('sn', 'ends', 'foobar');
  129.     *
  130.     *   // This will find entries that contain an attribute "sn" that has any value set:
  131.     *   $filter = new Net_LDAP_Filter('sn', 'any');
  132.     * </code>
  133.     *
  134.     * @param string  $attr_name Name of the attribute the filter should apply to
  135.     * @param string  $match     Matching rule (equals, begins, ends, contains, greater, less, greaterOrEqual, lessOrEqual, approx, any)
  136.     * @param string  $value     (optional) if given, then this is used as a filter
  137.     * @param boolean $escape    Should $value be escaped? (default: yes, see {@link Net_LDAP_Util::escape_filter_value()} for detailed information)
  138.     *
  139.     * @return Net_LDAP_Filter|Net_LDAP_Error
  140.     */
  141.     function &create($attr_name$match$value ''$escape = true)
  142.     {
  143.         $leaf_filter = new Net_LDAP_Filter();
  144.         if ($escape{
  145.             $array Net_LDAP_Util::escape_filter_value(array($value));
  146.             $value $array[0];
  147.         }
  148.         switch (strtolower($match)) {
  149.         case 'equals':
  150.             $leaf_filter->_filter = '(' $attr_name '=' $value ')';
  151.             break;
  152.         case 'begins':
  153.             $leaf_filter->_filter = '(' $attr_name '=' $value '*)';
  154.             break;
  155.         case 'ends':
  156.             $leaf_filter->_filter = '(' $attr_name '=*' $value ')';
  157.             break;
  158.         case 'contains':
  159.             $leaf_filter->_filter = '(' $attr_name '=*' $value '*)';
  160.             break;
  161.         case 'greater':
  162.             $leaf_filter->_filter = '(' $attr_name '>' $value ')';
  163.             break;
  164.         case 'less':
  165.             $leaf_filter->_filter = '(' $attr_name '<' $value ')';
  166.             break;
  167.         case 'greaterorequal':
  168.             $leaf_filter->_filter = '(' $attr_name '>=' $value ')';
  169.             break;
  170.         case 'lessorequal':
  171.             $leaf_filter->_filter = '(' $attr_name '<=' $value ')';
  172.             break;
  173.         case 'approx':
  174.             $leaf_filter->_filter = '(' $attr_name '~=' $value ')';
  175.             break;
  176.         case 'any':
  177.             $leaf_filter->_filter = '(' $attr_name '=*)';
  178.             break;
  179.         default:
  180.             return PEAR::raiseError('Net_LDAP_Filter create error: matching rule "' $match '" not known!');
  181.         }
  182.         return $leaf_filter;
  183.     }
  184.  
  185.     /**
  186.     * Combine two or more filter objects using a logical operator
  187.     *
  188.     * This static method combines two or more filter objects and returns one single
  189.     * filter object that contains all the others.
  190.     * Call this method statically: $filter =& Net_LDAP_Filter('or', array($filter1, $filter2))
  191.     * If the array contains filter strings instead of filter objects, we will try to parse them.
  192.     *
  193.     * @param string                $log_op  The locicall operator. May be "and", "or", "not" or the subsequent logical equivalents "&", "|", "!"
  194.     * @param array|Net_LDAP_Filter$filters array with Net_LDAP_Filter objects
  195.     *
  196.     * @return Net_LDAP_Filter|Net_LDAP_Error
  197.     * @static
  198.     */
  199.     function &combine($log_op$filters)
  200.     {
  201.         if (PEAR::isError($filters)) {
  202.             return $filters;
  203.         }
  204.  
  205.         // substitude named operators to logical operators
  206.         if ($log_op == 'and'$log_op '&';
  207.         if ($log_op == 'or')  $log_op '|';
  208.         if ($log_op == 'not'$log_op '!';
  209.  
  210.         // tests for sane operation
  211.         if ($log_op == '!'{
  212.             // Not-combination, here we also accept one filter object or filter string
  213.             if (!is_array($filters&& is_a($filters'Net_LDAP_Filter')) {
  214.                 $filters = array($filters)// force array
  215.             elseif (is_string($filters)) {
  216.                 $filter_o Net_LDAP_Filter::parse($filters);
  217.                 if (PEAR::isError($filter_o)) {
  218.                     $err = PEAR::raiseError('Net_LDAP_Filter combine error: '.$filter_o->getMessage());
  219.                     return $err;
  220.                 else {
  221.                     $filters = array($filter_o);
  222.                 }
  223.             else {
  224.                 $err = PEAR::raiseError('Net_LDAP_Filter combine error: operator is "not" but $filter is not a valid Net_LDAP_Filter nor an array nor a filter string!');
  225.                 return $err;
  226.             }
  227.         elseif ($log_op == '&' || $log_op == '|'{
  228.             if (!is_array($filters|| count($filters< 2{
  229.                 $err = PEAR::raiseError('Net_LDAP_Filter combine error: parameter $filters is not an array or contains less than two Net_LDAP_Filter objects!');
  230.                 return $err;
  231.             }
  232.         else {
  233.             $err = PEAR::raiseError('Net_LDAP_Filter combine error: logical operator is not known!');
  234.             return $err;
  235.         }
  236.  
  237.         $combined_filter = new Net_LDAP_Filter();
  238.         foreach ($filters as $key => $testfilter{     // check for errors
  239.             if (PEAR::isError($testfilter)) {
  240.                 return $testfilter;
  241.             elseif (is_string($testfilter)) {
  242.                 // string found, try to parse into an filter object
  243.                 $filter_o Net_LDAP_Filter::parse($testfilter);
  244.                 if (PEAR::isError($filter_o)) {
  245.                     return $filter_o;
  246.                 else {
  247.                     $filters[$key$filter_o;
  248.                 }
  249.             elseif (!is_a($testfilter'Net_LDAP_Filter')) {
  250.                 $err = PEAR::raiseError('Net_LDAP_Filter combine error: invalid object passed in array $filters!');
  251.                 return $err;
  252.             }
  253.         }
  254.  
  255.         $combined_filter->_subfilters = $filters;
  256.         $combined_filter->_match      = $log_op;
  257.         return $combined_filter;
  258.     }
  259.  
  260.     /**
  261.     * Parse FILTER into a Net_LDAP_Filter object
  262.     *
  263.     * This parses an filter string into Net_LDAP_Filter objects.
  264.     *
  265.     * @param string $FILTER The filter string
  266.     *
  267.     * @access static
  268.     * @return Net_LDAP_Filter|Net_LDAP_Error
  269.     * @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)
  270.     */
  271.     function parse($FILTER)
  272.     {
  273.         if (preg_match('/^\((.+?)\)$/'$FILTER$matches)) {
  274.             if (in_array(substr($matches[1]01)array('!''|''&'))) {
  275.                 // Subfilter processing: pass subfilters to parse() and combine
  276.                 // the objects using the logical operator detected
  277.                 // we have now something like "(...)(...)(...)" but at least one part ("(...)").
  278.  
  279.                 // extract logical operator and subfilters
  280.                 $log_op              substr($matches[1]01);
  281.                 $remaining_component substr($matches[1]1);
  282.  
  283.                 // bite off the next filter part and parse
  284.                 $subfilters = array();
  285.                 while (preg_match('/^(\(.+?\))(.*)/'$remaining_component$matches)) {
  286.                     $remaining_component $matches[2];
  287.                     $filter_o Net_LDAP_Filter::parse($matches[1]);
  288.                     if (PEAR::isError($filter_o)) {
  289.                         return $filter_o;
  290.                     }
  291.                     array_push($subfilters$filter_o);
  292.                 }
  293.  
  294.                 // combine subfilters using the logical operator
  295.                 $filter_o Net_LDAP_Filter::combine($log_op$subfilters);
  296.                 return $filter_o;
  297.             else {
  298.                 // This is one leaf filter component, do some syntax checks, then escape and build filter_o
  299.                 // $matches[1] should be now something like "foo=bar"
  300.  
  301.                 // detect multiple leaf components
  302.                 // [TODO] Maybe this will make problems with filters containing brackets inside the value
  303.                 if (stristr($matches[1]')(')) {
  304.                     return PEAR::raiseError("Filter parsing error: invalid filter syntax - multiple leaf components detected!");
  305.                 else {
  306.                     $filter_parts preg_split('/(?<!\\\\)(=|=~|>|<|>=|<=)/'$matches[1]2PREG_SPLIT_DELIM_CAPTURE);
  307.                     if (count($filter_parts!= 3{
  308.                         return PEAR::raiseError("Filter parsing error: invalid filter syntax - unknown matching rule used");
  309.                     else {
  310.                         $filter_o          = new Net_LDAP_Filter();
  311.                         // [TODO]: Do we need to escape at all? what about *-chars user provide and that should remain special?
  312.                         //         I think, those prevent escaping! We need to check against PERL Net::LDAP!
  313.                         // $value_arr         = Net_LDAP_Util::escape_filter_value(array($filter_parts[2]));
  314.                         // $value             = $value_arr[0];
  315.                         $value             $filter_parts[2];
  316.                         $filter_o->_filter = '('.$filter_parts[0].$filter_parts[1].$value.')';
  317.                         return $filter_o;
  318.                     }
  319.                 }
  320.             }
  321.         else {
  322.                // ERROR: Filter components must be enclosed in round brackets
  323.                return PEAR::raiseError("Filter parsing error: invalid filter syntax - filter components must be enclosed in round brackets");
  324.         }
  325.     }
  326.  
  327.     /**
  328.     * Get the string representation of this filter
  329.     *
  330.     * This method runs through all filter objects and creates
  331.     * the string representation of the filter. If this
  332.     * filter object is a leaf filter, then it will return
  333.     * the string representation of this filter.
  334.     *
  335.     * @return string|Net_LDAP_Error
  336.     */
  337.     function asString()
  338.     {
  339.         if ($this->_isLeaf()) {
  340.             $return $this->_filter;
  341.         else {
  342.             $return '';
  343.             foreach ($this->_subfilters as $filter{
  344.                 $return $return.$filter->asString();
  345.             }
  346.             $return '(' $this->_match $return ')';
  347.         }
  348.         return $return;
  349.     }
  350.  
  351.     /**
  352.     * Alias for perl interface as_string()
  353.     *
  354.     * @see asString()
  355.     */
  356.     function as_string()
  357.     {
  358.         return $this->asString();
  359.     }
  360.  
  361.     /**
  362.     * Print the text representation of the filter to FH, or the currently selected output handle if FH is not given
  363.     *
  364.     * This method is only for compatibility to the perl interface.
  365.     * However, the original method was called "print" but due to PHP language restrictions,
  366.     * we can't have a print() method.
  367.     *
  368.     * @param resource $FH (optional) A filehandle resource
  369.     *
  370.     * @return true|Net_LDAP_Error
  371.     */
  372.     function printMe($FH = false)
  373.     {
  374.         if (!is_resource($FH)) {
  375.             if (PEAR::isError($FH)) {
  376.                 return $FH;
  377.             }
  378.             $filter_str $this->asString();
  379.             if (PEAR::isError($filter_str)) {
  380.                 return $filter_str;
  381.             else {
  382.                 print($filter_str);
  383.             }
  384.         else {
  385.             $filter_str $this->asString();
  386.             if (PEAR::isError($filter_str)) {
  387.                 return $filter_str;
  388.             else {
  389.                 $res @fwrite($FH$this->asString());
  390.                 if ($res == false{
  391.                     return PEAR::raiseError("Unable to write filter string to filehandle \$FH!");
  392.                 }
  393.             }
  394.         }
  395.         return true;
  396.     }
  397.  
  398.     /**
  399.     * This can be used to escape a string to provide a valid LDAP-Filter.
  400.     *
  401.     * LDAP will only recognise certain characters as the
  402.     * character istself if they are properly escaped. This is
  403.     * what this method does.
  404.     * The method can be called statically, so you can use it outside
  405.     * for your own purposes (eg for escaping only parts of strings)
  406.     *
  407.     * In fact, this is just a shorthand to {@link Net_LDAP_Util::escape_filter_value()}.
  408.     * For upward compatibiliy reasons you are strongly encouraged to use the escape
  409.     * methods provided by the Net_LDAP_Util class.
  410.     *
  411.     * @param string $value Any string who should be escaped
  412.     *
  413.     * @static
  414.     * @return string         The string $string, but escaped
  415.     * @deprecated  Do not use this method anymore, instead use Net_LDAP_Util::escape_filter_value() directly
  416.     */
  417.     function escape($value)
  418.     {
  419.         $return Net_LDAP_Util::escape_filter_value(array($value));
  420.         return $return[0];
  421.     }
  422.  
  423.     /**
  424.     * Is this a container or a leaf filter object?
  425.     *
  426.     * @access private
  427.     * @return boolean 
  428.     */
  429.     function _isLeaf()
  430.     {
  431.         if (count($this->_subfilters> 0{
  432.             return false; // Container!
  433.         else {
  434.             return true; // Leaf!
  435.         }
  436.     }
  437. }
  438. ?>

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