/** * Gets an associative array of AttributeType objects for the specified * server. Each array entry's key is the name of the attributeType * in lower-case and the value is an AttributeType object. * * @param string $dn (optional) It is easier to fetch schema if a DN is provided * which defines the subschemaSubEntry attribute (all entries should). * * @return array An array of AttributeType objects. */ public function SchemaAttributes($method = null, $dn = '') { if (DEBUG_ENABLED && (($fargs = func_get_args()) || ($fargs = 'NOARGS'))) { debug_log('Entered (%%)', 25, 0, __FILE__, __LINE__, __METHOD__, $fargs); } # Set default return $return = null; if ($return = get_cached_item($this->index, 'schema', 'attributes')) { if (DEBUG_ENABLED) { debug_log('(): Returning CACHED [%s] (%s)', 25, 0, __FILE__, __LINE__, __METHOD__, $this->index, 'attributes'); } return $return; } $raw = $this->getRawSchema($method, 'attributeTypes', $dn); if ($raw) { # build the array of attribueTypes $syntaxes = $this->SchemaSyntaxes($method, $dn); $attrs = array(); /** * bug 856832: create two arrays - one indexed by name (the standard * $attrs array above) and one indexed by oid (the new $attrs_oid array * below). This will help for directory servers, like IBM's, that use OIDs * in their attribute definitions of SUP, etc */ $attrs_oid = array(); foreach ($raw as $line) { if (is_null($line) || !strlen($line)) { continue; } $attr = new AttributeType($line); if (isset($syntaxes[$attr->getSyntaxOID()])) { $syntax = $syntaxes[$attr->getSyntaxOID()]; $attr->setType($syntax->getDescription()); } $attrs[$attr->getName()] = $attr; /** * bug 856832: create an entry in the $attrs_oid array too. This * will be a ref to the $attrs entry for maintenance and performance * reasons */ $attrs_oid[$attr->getOID()] =& $attrs[$attr->getName()]; } # go back and add data from aliased attributeTypes foreach ($attrs as $name => $attr) { $aliases = $attr->getAliases(); if (is_array($aliases) && count($aliases) > 0) { /* foreach of the attribute's aliases, create a new entry in the attrs array * with its name set to the alias name, and all other data copied.*/ foreach ($aliases as $alias_attr_name) { $new_attr = clone $attr; $new_attr->setName($alias_attr_name); $new_attr->addAlias($attr->getName(false)); $new_attr->removeAlias($alias_attr_name); $new_attr_key = strtolower($alias_attr_name); $attrs[$new_attr_key] = $new_attr; } } } # go back and add any inherited descriptions from parent attributes (ie, cn inherits name) foreach ($attrs as $key => $attr) { $sup_attr_name = $attr->getSupAttribute(); $sup_attr = null; if (trim($sup_attr_name)) { /* This loop really should traverse infinite levels of inheritance (SUP) for attributeTypes, * but just in case we get carried away, stop at 100. This shouldn't happen, but for * some weird reason, we have had someone report that it has happened. Oh well.*/ $i = 0; while ($i++ < 100) { if (isset($attrs_oid[$sup_attr_name])) { $attr->setSupAttribute($attrs_oid[$sup_attr_name]->getName()); $sup_attr_name = $attr->getSupAttribute(); } if (!isset($attrs[strtolower($sup_attr_name)])) { error(sprintf('Schema error: attributeType "%s" inherits from "%s", but attributeType "%s" does not exist.', $attr->getName(), $sup_attr_name, $sup_attr_name), 'error', 'index.php'); return; } $sup_attr = $attrs[strtolower($sup_attr_name)]; $sup_attr_name = $sup_attr->getSupAttribute(); # Does this superior attributeType not have a superior attributeType? if (is_null($sup_attr_name) || strlen(trim($sup_attr_name)) == 0) { /* Since this attribute's superior attribute does not have another superior * attribute, clone its properties for this attribute. Then, replace * those cloned values with those that can be explicitly set by the child * attribute attr). Save those few properties which the child can set here:*/ $tmp_name = $attr->getName(false); $tmp_oid = $attr->getOID(); $tmp_sup = $attr->getSupAttribute(); $tmp_aliases = $attr->getAliases(); $tmp_single_val = $attr->getIsSingleValue(); $tmp_desc = $attr->getDescription(); /* clone the SUP attributeType and populate those values * that were set by the child attributeType */ $attr = clone $sup_attr; $attr->setOID($tmp_oid); $attr->setName($tmp_name); $attr->setSupAttribute($tmp_sup); $attr->setAliases($tmp_aliases); $attr->setDescription($tmp_desc); /* only overwrite the SINGLE-VALUE property if the child explicitly sets it * (note: All LDAP attributes default to multi-value if not explicitly set SINGLE-VALUE) */ if ($tmp_single_val) { $attr->setIsSingleValue(true); } /* replace this attribute in the attrs array now that we have populated new values therein */ $attrs[$key] = $attr; # very important: break out after we are done with this attribute $sup_attr_name = null; $sup_attr = null; break; } } } } ksort($attrs); # Add the used in and required_by values. $socs = $this->SchemaObjectClasses($method); if (!is_array($socs)) { return array(); } foreach ($socs as $object_class) { $must_attrs = $object_class->getMustAttrNames(); $may_attrs = $object_class->getMayAttrNames(); $oclass_attrs = array_unique(array_merge($must_attrs, $may_attrs)); # Add Used In. foreach ($oclass_attrs as $attr_name) { if (isset($attrs[strtolower($attr_name)])) { $attrs[strtolower($attr_name)]->addUsedInObjectClass($object_class->getName(false)); } } # Add Required By. foreach ($must_attrs as $attr_name) { if (isset($attrs[strtolower($attr_name)])) { $attrs[strtolower($attr_name)]->addRequiredByObjectClass($object_class->getName(false)); } } # Force May foreach ($object_class->getForceMayAttrs() as $attr_name) { if (isset($attrs[strtolower($attr_name->name)])) { $attrs[strtolower($attr_name->name)]->setForceMay(); } } } $return = $attrs; # cache the schema to prevent multiple schema fetches from LDAP server set_cached_item($this->index, 'schema', 'attributes', $return); } if (DEBUG_ENABLED) { debug_log('Returning (%s)', 25, 0, __FILE__, __LINE__, __METHOD__, $return); } return $return; }