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

Source for file Schema.php

Documentation is available at Schema.php

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3.  
  4. require_once 'PEAR.php';
  5.  
  6. /**
  7. * Syntax definitions
  8. *
  9. * Please don't forget to add binary attributes to isBinary() below
  10. * to support proper value fetching from Net_LDAP2_Entry
  11. */
  12. define('NET_LDAP_SYNTAX_BOOLEAN',            '1.3.6.1.4.1.1466.115.121.1.7');
  13. define('NET_LDAP_SYNTAX_DIRECTORY_STRING',   '1.3.6.1.4.1.1466.115.121.1.15');
  14. define('NET_LDAP_SYNTAX_DISTINGUISHED_NAME''1.3.6.1.4.1.1466.115.121.1.12');
  15. define('NET_LDAP_SYNTAX_INTEGER',            '1.3.6.1.4.1.1466.115.121.1.27');
  16. define('NET_LDAP_SYNTAX_JPEG',               '1.3.6.1.4.1.1466.115.121.1.28');
  17. define('NET_LDAP_SYNTAX_NUMERIC_STRING',     '1.3.6.1.4.1.1466.115.121.1.36');
  18. define('NET_LDAP_SYNTAX_OID',                '1.3.6.1.4.1.1466.115.121.1.38');
  19. define('NET_LDAP_SYNTAX_OCTET_STRING',       '1.3.6.1.4.1.1466.115.121.1.40');
  20.  
  21. /**
  22. * Load an LDAP Schema and provide information
  23. *
  24. * This class takes a Subschema entry, parses this information
  25. * and makes it available in an array. Most of the code has been
  26. * inspired by perl-ldap( http://perl-ldap.sourceforge.net).
  27. * You will find portions of their implementation in here.
  28. *
  29. @category Net
  30. @package  Net_LDAP2
  31. @author   Jan Wagner <wagner@netsols.de>
  32. @author   Benedikt Hallinger <beni@php.net>
  33. @license  http://www.gnu.org/copyleft/lesser.html LGPL
  34. @version  CVS: $Id: Schema.php,v 1.2 2008/03/20 09:32:39 beni Exp $
  35. @link     http://pear.php.net/package/Net_LDAP22/
  36. */
  37. class Net_LDAP2_Schema extends PEAR
  38. {
  39.     /**
  40.     * Map of entry types to ldap attributes of subschema entry
  41.     *
  42.     * @access public
  43.     * @var array 
  44.     */
  45.     public $types = array(
  46.             'attribute'        => 'attributeTypes',
  47.             'ditcontentrule'   => 'dITContentRules',
  48.             'ditstructurerule' => 'dITStructureRules',
  49.             'matchingrule'     => 'matchingRules',
  50.             'matchingruleuse'  => 'matchingRuleUse',
  51.             'nameform'         => 'nameForms',
  52.             'objectclass'      => 'objectClasses',
  53.             'syntax'           => 'ldapSyntaxes'
  54.         );
  55.  
  56.     /**
  57.     * Array of entries belonging to this type
  58.     *
  59.     * @access protected
  60.     * @var array 
  61.     */
  62.     protected $_attributeTypes    = array();
  63.     protected $_matchingRules     = array();
  64.     protected $_matchingRuleUse   = array();
  65.     protected $_ldapSyntaxes      = array();
  66.     protected $_objectClasses     = array();
  67.     protected $_dITContentRules   = array();
  68.     protected $_dITStructureRules = array();
  69.     protected $_nameForms         = array();
  70.  
  71.  
  72.     /**
  73.     * hash of all fetched oids
  74.     *
  75.     * @access protected
  76.     * @var array 
  77.     */
  78.     protected $_oids = array();
  79.  
  80.     /**
  81.     * Tells if the schema is initialized
  82.     *
  83.     * @access protected
  84.     * @var boolean 
  85.     * @see parse(), get()
  86.     */
  87.     protected $_initialized = false;
  88.  
  89.  
  90.     /**
  91.     * Constructor of the class
  92.     *
  93.     * @access protected
  94.     */
  95.     protected function __construct()
  96.     {
  97.         $this->PEAR('Net_LDAP2_Error')// default error class
  98.     }
  99.  
  100.     /**
  101.     * Fetch the Schema from an LDAP connection
  102.     *
  103.     * @param Net_LDAP2 LDAP connection
  104.     * @param string $dn (optional) Subschema entry dn
  105.     *
  106.     * @author Jan Wagner <wagner@netsols.de>
  107.     * @access public
  108.     * @return Net_LDAP2_Schema|Net_LDAP_Error
  109.     */
  110.     public function fetch(&$ldap$dn = null{
  111.         if (!$ldap instanceof Net_LDAP2{
  112.             return PEAR::raiseError("Unable to fetch Schema: Parameter \$ldap must be a Net_LDAP2 object!");
  113.         }
  114.  
  115.         $schema_o = new Net_LDAP2_Schema();
  116.  
  117.         if (is_null($dn)) {
  118.             // get the subschema entry via root dse
  119.             $dse $ldap->rootDSE(array('subschemaSubentry'));
  120.             if (false == Net_LDAP2::isError($dse)) {
  121.                 $base $dse->getValue('subschemaSubentry''single');
  122.                 if (!Net_LDAP2::isError($base)) {
  123.                     $dn $base;
  124.                 }
  125.             }
  126.         }
  127.  
  128.         //
  129.         // Support for buggy LDAP servers (e.g. Siemens DirX 6.x) that incorrectly
  130.         // call this entry subSchemaSubentry instead of subschemaSubentry.
  131.         // Note the correct case/spelling as per RFC 2251.
  132.         //
  133.         if (is_null($dn)) {
  134.             // get the subschema entry via root dse
  135.             $dse $ldap->rootDSE(array('subSchemaSubentry'));
  136.             if (false == Net_LDAP2::isError($dse)) {
  137.                 $base $dse->getValue('subSchemaSubentry''single');
  138.                 if (!Net_LDAP2::isError($base)) {
  139.                     $dn $base;
  140.                 }
  141.             }
  142.         }
  143.  
  144.         //
  145.         // Final fallback case where there is no subschemaSubentry attribute
  146.         // in the root DSE (this is a bug for an LDAP v3 server so report this
  147.         // to your LDAP vendor if you get this far).
  148.         //
  149.         if (is_null($dn)) {
  150.             $dn 'cn=Subschema';
  151.         }
  152.  
  153.         // fetch the subschema entry
  154.         $result $ldap->search($dn'(objectClass=*)',
  155.                                 array('attributes' => array_values($schema_o->types),
  156.                                         'scope' => 'base'));
  157.         if (Net_LDAP2::isError($result)) {
  158.             return $result;
  159.         }
  160.  
  161.         $entry $result->shiftEntry();
  162.         if (!$entry instanceof Net_LDAP2_Entry{
  163.             return PEAR::raiseError('Could not fetch Subschema entry');
  164.         }
  165.  
  166.         $schema_o->parse($entry);
  167.         return $schema_o;
  168.     }
  169.  
  170.     /**
  171.     * Return a hash of entries for the given type
  172.     *
  173.     * Returns a hash of entry for th givene type. Types may be:
  174.     * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules,
  175.     * matchingruleuses, nameforms, syntaxes
  176.     *
  177.     * @param string $type Type to fetch
  178.     *
  179.     * @access public
  180.     * @return array|Net_LDAP2_ErrorArray or Net_LDAP2_Error
  181.     */
  182.     public function &getAll($type)
  183.     {
  184.         $map = array('objectclasses'     => &$this->_objectClasses,
  185.                      'attributes'        => &$this->_attributeTypes,
  186.                      'ditcontentrules'   => &$this->_dITContentRules,
  187.                      'ditstructurerules' => &$this->_dITStructureRules,
  188.                      'matchingrules'     => &$this->_matchingRules,
  189.                      'matchingruleuses'  => &$this->_matchingRuleUse,
  190.                      'nameforms'         => &$this->_nameForms,
  191.                      'syntaxes'          => &$this->_ldapSyntaxes );
  192.  
  193.         $key strtolower($type);
  194.         $ret ((key_exists($key$map)) $map[$key: PEAR::raiseError("Unknown type $type"));
  195.         return $ret;
  196.     }
  197.  
  198.     /**
  199.     * Return a specific entry
  200.     *
  201.     * @param string $type Type of name
  202.     * @param string $name Name or OID to fetch
  203.     *
  204.     * @access public
  205.     * @return mixed Entry or Net_LDAP2_Error
  206.     */
  207.     public function &get($type$name)
  208.     {
  209.         if ($this->_initialized{
  210.             $type strtolower($type);
  211.             if (false == key_exists($type$this->types)) {
  212.                 return PEAR::raiseError("No such type $type");
  213.             }
  214.  
  215.             $name     strtolower($name);
  216.             $type_var &$this->{'_' $this->types[$type]};
  217.  
  218.             if (key_exists($name$type_var)) {
  219.                 return $type_var[$name];
  220.             elseif (key_exists($name$this->_oids&& $this->_oids[$name]['type'== $type{
  221.                 return $this->_oids[$name];
  222.             else {
  223.                 return PEAR::raiseError("Could not find $type $name");
  224.             }
  225.         else {
  226.             $return = null;
  227.             return $return;
  228.         }
  229.     }
  230.  
  231.  
  232.     /**
  233.     * Fetches attributes that MAY be present in the given objectclass
  234.     *
  235.     * @param string $oc Name or OID of objectclass
  236.     *
  237.     * @access public
  238.     * @return array|Net_LDAP2_ErrorArray with attributes or Net_LDAP2_Error
  239.     */
  240.     public function may($oc)
  241.     {
  242.         return $this->_getAttr($oc'may');
  243.     }
  244.  
  245.     /**
  246.     * Fetches attributes that MUST be present in the given objectclass
  247.     *
  248.     * @param string $oc Name or OID of objectclass
  249.     *
  250.     * @access public
  251.     * @return array|Net_LDAP2_ErrorArray with attributes or Net_LDAP2_Error
  252.     */
  253.     public function must($oc)
  254.     {
  255.         return $this->_getAttr($oc'must');
  256.     }
  257.  
  258.     /**
  259.     * Fetches the given attribute from the given objectclass
  260.     *
  261.     * @param string $oc   Name or OID of objectclass
  262.     * @param string $attr Name of attribute to fetch
  263.     *
  264.     * @access protected
  265.     * @return array|Net_LDAP2_ErrorThe attribute or Net_LDAP2_Error
  266.     */
  267.     protected function _getAttr($oc$attr)
  268.     {
  269.         $oc strtolower($oc);
  270.         if (key_exists($oc$this->_objectClasses&& key_exists($attr$this->_objectClasses[$oc])) {
  271.             return $this->_objectClasses[$oc][$attr];
  272.         elseif (key_exists($oc$this->_oids&&
  273.                 $this->_oids[$oc]['type'== 'objectclass' &&
  274.                 key_exists($attr$this->_oids[$oc])) {
  275.             return $this->_oids[$oc][$attr];
  276.         else {
  277.             return PEAR::raiseError("Could not find $attr attributes for $oc ");
  278.         }
  279.     }
  280.  
  281.     /**
  282.     * Returns the name(s) of the immediate superclass(es)
  283.     *
  284.     * @param string $oc Name or OID of objectclass
  285.     *
  286.     * @access public
  287.     * @return array|Net_LDAP2_Error Array of names or Net_LDAP2_Error
  288.     */
  289.     public function superclass($oc)
  290.     {
  291.         $o $this->get('objectclass'$oc);
  292.         if (Net_LDAP2::isError($o)) {
  293.             return $o;
  294.         }
  295.         return (key_exists('sup'$o$o['sup': array());
  296.     }
  297.  
  298.     /**
  299.     * Parses the schema of the given Subschema entry
  300.     *
  301.     * @param Net_LDAP2_Entry &$entry Subschema entry
  302.     *
  303.     * @access public
  304.     */
  305.     public function parse(&$entry)
  306.     {
  307.         foreach ($this->types as $type => $attr{
  308.             // initialize map type to entry
  309.             $type_var          '_' $attr;
  310.             $this->{$type_var= array();
  311.  
  312.             // get values for this type
  313.             if ($entry->exists($attr)) {
  314.                 $values $entry->getValue($attr);
  315.                 if (is_array($values)) {
  316.                     foreach ($values as $value{
  317.  
  318.                         unset($schema_entry)// this was a real mess without it
  319.  
  320.                         // get the schema entry
  321.                         $schema_entry $this->_parse_entry($value);
  322.  
  323.                         // set the type
  324.                         $schema_entry['type'$type;
  325.  
  326.                         // save a ref in $_oids
  327.                         $this->_oids[$schema_entry['oid']] &$schema_entry;
  328.  
  329.                         // save refs for all names in type map
  330.                         $names $schema_entry['aliases'];
  331.                         array_push($names$schema_entry['name']);
  332.                         foreach ($names as $name{
  333.                             $this->{$type_var}[strtolower($name)&$schema_entry;
  334.                         }
  335.                     }
  336.                 }
  337.             }
  338.         }
  339.         $this->_initialized = true;
  340.     }
  341.  
  342.     /**
  343.     * Parses an attribute value into a schema entry
  344.     *
  345.     * @param string $value Attribute value
  346.     *
  347.     * @access protected
  348.     * @return array|falseSchema entry array or false
  349.     */
  350.     protected function &_parse_entry($value)
  351.     {
  352.         // tokens that have no value associated
  353.         $noValue = array('single-value',
  354.                          'obsolete',
  355.                          'collective',
  356.                          'no-user-modification',
  357.                          'abstract',
  358.                          'structural',
  359.                          'auxiliary');
  360.  
  361.         // tokens that can have multiple values
  362.         $multiValue = array('must''may''sup');
  363.  
  364.         $schema_entry = array('aliases' => array())// initilization
  365.  
  366.         $tokens $this->_tokenize($value)// get an array of tokens
  367.  
  368.         // remove surrounding brackets
  369.         if ($tokens[0== '('array_shift($tokens);
  370.         if ($tokens[count($tokens- 1== ')'array_pop($tokens)// -1 doesnt work on arrays :-(
  371.  
  372.         $schema_entry['oid'array_shift($tokens)// first token is the oid
  373.  
  374.         // cycle over the tokens until none are left
  375.         while (count($tokens> 0{
  376.             $token strtolower(array_shift($tokens));
  377.             if (in_array($token$noValue)) {
  378.                 $schema_entry[$token= 1; // single value token
  379.             else {
  380.                 // this one follows a string or a list if it is multivalued
  381.                 if (($schema_entry[$tokenarray_shift($tokens)) == '('{
  382.                     // this creates the list of values and cycles through the tokens
  383.                     // until the end of the list is reached ')'
  384.                     $schema_entry[$token= array();
  385.                     while ($tmp array_shift($tokens)) {
  386.                         if ($tmp == ')'break;
  387.                         if ($tmp != '$'array_push($schema_entry[$token]$tmp);
  388.                     }
  389.                 }
  390.                 // create a array if the value should be multivalued but was not
  391.                 if (in_array($token$multiValue&& !is_array($schema_entry[$token])) {
  392.                     $schema_entry[$token= array($schema_entry[$token]);
  393.                 }
  394.             }
  395.         }
  396.         // get max length from syntax
  397.         if (key_exists('syntax'$schema_entry)) {
  398.             if (preg_match('/{(\d+)}/'$schema_entry['syntax']$matches)) {
  399.                 $schema_entry['max_length'$matches[1];
  400.             }
  401.         }
  402.         // force a name
  403.         if (empty($schema_entry['name'])) {
  404.             $schema_entry['name'$schema_entry['oid'];
  405.         }
  406.         // make one name the default and put the other ones into aliases
  407.         if (is_array($schema_entry['name'])) {
  408.             $aliases                 $schema_entry['name'];
  409.             $schema_entry['name']    array_shift($aliases);
  410.             $schema_entry['aliases'$aliases;
  411.         }
  412.         return $schema_entry;
  413.     }
  414.  
  415.     /**
  416.     * Tokenizes the given value into an array of tokens
  417.     *
  418.     * @param string $value String to parse
  419.     *
  420.     * @access protected
  421.     * @return array Array of tokens
  422.     */
  423.     protected function _tokenize($value)
  424.     {
  425.         $tokens  = array();       // array of tokens
  426.         $matches = array();       // matches[0] full pattern match, [1,2,3] subpatterns
  427.  
  428.         // this one is taken from perl-ldap, modified for php
  429.         $pattern "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x";
  430.  
  431.         /**
  432.          * This one matches one big pattern wherin only one of the three subpatterns matched
  433.          * We are interested in the subpatterns that matched. If it matched its value will be
  434.          * non-empty and so it is a token. Tokens may be round brackets, a string, or a string
  435.          * enclosed by '
  436.          */
  437.         preg_match_all($pattern$value$matches);
  438.  
  439.         for ($i = 0; $i count($matches[0])$i++{     // number of tokens (full pattern match)
  440.             for ($j = 1; $j < 4; $j++{                  // each subpattern
  441.                 if (null != trim($matches[$j][$i])) {     // pattern match in this subpattern
  442.                     $tokens[$itrim($matches[$j][$i])// this is the token
  443.                 }
  444.             }
  445.         }
  446.         return $tokens;
  447.     }
  448.  
  449.     /**
  450.     * Returns wether a attribute syntax is binary or not
  451.     *
  452.     * This method gets used by Net_LDAP2_Entry to decide which
  453.     * PHP function needs to be used to fetch the value in the
  454.     * proper format (e.g. binary or string)
  455.     *
  456.     * @param string $attribute The name of the attribute (eg.: 'sn')
  457.     *
  458.     * @access public
  459.     * @return boolean 
  460.     */
  461.     public function isBinary($attribute)
  462.     {
  463.         // This list contains all syntax that should be treaten as
  464.         // containing binary values
  465.         // The Syntax Definitons go into constants at the top of this page
  466.         $syntax_binary = array(
  467.                            NET_LDAP_SYNTAX_OCTET_STRING,
  468.                            NET_LDAP_SYNTAX_JPEG
  469.                          );
  470.  
  471.         // Check Syntax
  472.         $attr_s $this->get('attribute'$attribute);
  473.         if (false === Net_LDAP2::isError($attr_s&& isset($attr_s['syntax']&& in_array($attr_s['syntax']$syntax_binary)) {
  474.             $return = true;
  475.             return $return;
  476.         else {
  477.             $return = false;
  478.             return $return;
  479.         }
  480.     }
  481. }
  482. ?>

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