* @version $Revision: 4831 $ */ class Net_LDAP_Schema extends PEAR { /** * Map of entry types to ldap attributes of subschema entry * * @access public * @var array */ var $types = array('attribute' => 'attributeTypes', 'ditcontentrule' => 'dITContentRules', 'ditstructurerule' => 'dITStructureRules', 'matchingrule' => 'matchingRules', 'matchingruleuse' => 'matchingRuleUse', 'nameform' => 'nameForms', 'objectclass' => 'objectClasses', 'syntax' => 'ldapSyntaxes'); /**#@+ * Array of entries belonging to this type * * @access private * @var array */ var $_attributeTypes = array(); var $_matchingRules = array(); var $_matchingRuleUse = array(); var $_ldapSyntaxes = array(); var $_objectClasses = array(); var $_dITContentRules = array(); var $_dITStructureRules = array(); var $_nameForms = array(); /**#@-*/ /** * hash of all fetched oids * * @access private * @var array */ var $_oids = array(); /** * constructor of the class * * @access protected */ function Net_LDAP_Schema() { $this->PEAR('Net_LDAP_Error'); // default error class } /** * Return a hash of entries for the given type * * Returns a hash of entry for th givene type. Types may be: * objectclasses, attributes, ditcontentrules, ditstructurerules, matchingrules, * matchingruleuses, nameforms, syntaxes * * @access public * @param string Type to fetch * @return mixed Array or Net_LDAP_Error */ function &getAll($type) { $map = array('objectclasses' => &$this->_objectClasses, 'attributes' => &$this->_attributeTypes, 'ditcontentrules' => &$this->_dITContentRules, 'ditstructurerules' => &$this->_dITStructureRules, 'matchingrules' => &$this->_matchingRules, 'matchingruleuses' => &$this->_matchingRuleUse, 'nameforms' => &$this->_nameForms, 'syntaxes' => &$this->_ldapSyntaxes ); $key = strtolower($type); return ((key_exists($key, $map)) ? $map[$key] : $this->raiseError("Unknown type $type")); } /** * Return a specific entry * * @access public * @param string Type of name * @param string Name or OID to fetch * @return mixed Entry or Net_LDAP_Error */ function &get($type, $name) { $type = strtolower($type); if (false == key_exists($type, $this->types)) { return $this->raiseError("No such type $type"); } $name = strtolower($name); $type_var = &$this->{'_' . $this->types[$type]}; if( key_exists($name, $type_var)) { return $type_var[$name]; } elseif(key_exists($name, $this->_oids) && $this->_oids[$name]['type'] == $type) { return $this->_oids[$name]; } else { return $this->raiseError("Could not find $type $name"); } } /** * Fetches attributes that MAY be present in the given objectclass * * @access public * @param string Name or OID of objectclass * @return mixed Array with attributes or Net_LDAP_Error */ function may($oc) { return $this->_getAttr($oc, 'may'); } /** * Fetches attributes that MUST be present in the given objectclass * * @access public * @param string Name or OID of objectclass * @return mixed Array with attributes or Net_LDAP_Error */ function must($oc) { return $this->_getAttr($oc, 'must'); } /** * Fetches the given attribute from the given objectclass * * @access private * @param string Name or OID of objectclass * @param string Name of attribute to fetch * @return mixed The attribute or Net_LDAP_Error */ function _getAttr($oc, $attr) { $oc = strtolower($oc); if (key_exists($oc, $this->_objectClasses) && key_exists($attr, $this->_objectClasses[$oc])) { return $this->_objectClasses[$oc][$attr]; } elseif (key_exists($oc, $this->_oids) && $this->_oids[$oc]['type'] == 'objectclass' && key_exists($attr, $this->_oids[$oc])) { return $this->_oids[$oc][$attr]; } else { return $this->raiseError("Could not find $attr attributes for $oc "); } } /** * Returns the name(s) of the immediate superclass(es) * * @param string Name or OID of objectclass * @return mixed Array of names or Net_LDAP_Error */ function superclass($oc) { $o = $this->get('objectclass', $oc); if (Net_LDAP::isError($o)) { return $o; } return (key_exists('sup', $o) ? $o['sup'] : array()); } /** * Parses the schema of the given Subschema entry * * @access public * @param object Net_LDAP_Entry Subschema entry */ function parse(&$entry) { foreach ($this->types as $type => $attr) { // initialize map type to entry $type_var = '_' . $attr; $this->{$type_var} = array(); // get values for this type $values = $entry->get_value($attr); if (is_array($values)) { foreach ($values as $value) { unset($schema_entry); // this was a real mess without it // get the schema entry $schema_entry = $this->_parse_entry($value); // set the type $schema_entry['type'] = $type; // save a ref in $_oids $this->_oids[$schema_entry['oid']] =& $schema_entry; // save refs for all names in type map $names = $schema_entry['aliases']; array_push($names, $schema_entry['name']); foreach ($names as $name) { $this->{$type_var}[strtolower($name)] =& $schema_entry; } } } } } /** * parses an attribute value into a schema entry * * @access private * @param string Attribute value * @return mixed Schema entry array or false */ function &_parse_entry($value) { // tokens that have no value associated $noValue = array('single-value', 'obsolete', 'collective', 'no-user-modification', 'abstract', 'structural', 'auxiliary'); // tokens that can have multiple values $multiValue = array('must', 'may', 'sup'); $schema_entry = array('aliases' => array()); // initilization $tokens = $this->_tokenize($value); // get an array of tokens // remove surrounding brackets if ($tokens[0] == '(') array_shift($tokens); if ($tokens[count($tokens) - 1] == ')') array_pop($tokens); // -1 doesnt work on arrays :-( $schema_entry['oid'] = array_shift($tokens); // first token is the oid // cycle over the tokens until none are left while (count($tokens) > 0) { $token = strtolower(array_shift($tokens)); if (in_array($token, $noValue)) { $schema_entry[$token] = 1; // single value token } else { // this one follows a string or a list if it is multivalued if (($schema_entry[$token] = array_shift($tokens)) == '(') { // this creates the list of values and cycles through the tokens // until the end of the list is reached ')' $schema_entry[$token] = array(); while ($tmp = array_shift($tokens)) { if ($tmp == ')') break; if ($tmp != '$') array_push($schema_entry[$token], $tmp); } } // create a array if the value should be multivalued but was not if (in_array($token, $multiValue ) && !is_array($schema_entry[$token])) { $schema_entry[$token] = array($schema_entry[$token]); } } } // get max length from syntax if (key_exists('syntax', $schema_entry)) { if (preg_match('/{(\d+)}/', $schema_entry['syntax'], $matches)) { $schema_entry['max_length'] = $matches[1]; } } // force a name if (empty($schema_entry['name'])) { $schema_entry['name'] = $schema_entry['oid']; } // make one name the default and put the other ones into aliases if (is_array($schema_entry['name'])) { $aliases = $schema_entry['name']; $schema_entry['name'] = array_shift($aliases); $schema_entry['aliases'] = $aliases; } return $schema_entry; } /** * tokenizes the given value into an array of tokens * * @access private * @param string String to parse * @return array Array of tokens */ function _tokenize($value) { $tokens = array(); // array of tokens $matches = array(); // matches[0] full pattern match, [1,2,3] subpatterns // this one is taken from perl-ldap, modified for php $pattern = "/\s* (?:([()]) | ([^'\s()]+) | '((?:[^']+|'[^\s)])*)') \s*/x"; /** * This one matches one big pattern wherin only one of the three subpatterns matched * We are interested in the subpatterns that matched. If it matched its value will be * non-empty and so it is a token. Tokens may be round brackets, a string, or a string * enclosed by ' */ preg_match_all($pattern, $value, $matches); for ($i = 0; $i < count($matches[0]); $i++) { // number of tokens (full pattern match) for ($j = 1; $j < 4; $j++) { // each subpattern if (null != trim($matches[$j][$i])) { // pattern match in this subpattern $tokens[$i] = trim($matches[$j][$i]); // this is the token } } } return $tokens; } } ?>