/** * This helper method is the default instruction attribute handler in OPT. * It allows to parse the list of attributes using the specified rules. * The attribute configuration is passed as a second argument by reference, * and OPT returns the compiled attribute values in the same way. * * If the attribute specification contains "__UNKNOWN__" element, the node * may contain an undefined number of attributes. The undefined attributes * must match to the rules in "__UNKNOWN__" element and are returned by the * method as a separate array. For details, see the OPT user manual. * * @final * @param Opt_Xml_Element $subitem The scanned XML element. * @param Array &$config The reference to the attribute configuration * @return Array|Null The list of undefined attributes, if "__UNKNOWN__" is set. */ protected final function _extractAttributes(Opt_Xml_Element $subitem, array &$config) { $required = array(); $optional = array(); $unknown = null; // Decide, what is what. foreach ($config as $name => &$data) { if (!isset($data[3])) { $data[3] = null; } if ($name == '__UNKNOWN__') { $unknown =& $data; } elseif ($data[0] == self::REQUIRED) { $required[$name] =& $data; } elseif ($data[0] == self::OPTIONAL) { $optional[$name] =& $data; } } $config = array(); $return = array(); $exprEngines = $this->_compiler->getExpressionEngines(); // Parse required attributes $attrList = $subitem->getAttributes(false); foreach ($required as $name => &$data) { $ok = false; if (isset($attrList[$name])) { $aname = $name; $ok = true; } elseif (($data[1] == self::EXPRESSION || $data[1] == self::ASSIGN_EXPR) && $this->_tpl->backwardCompatibility == true) { // DEPRECATED: Legacy code for compatibility with OPT 2.0 foreach ($exprEngines as $eeName => $eeValue) { if (isset($attrList[$eeName . ':' . $name])) { $aname = $eeName . ':' . $name; $data[3] = $eeName; $ok = true; break; } } } if (!$ok) { throw new Opt_AttributeNotDefined_Exception($name, $subitem->getXmlName()); } $config[$name] = $this->_extractAttribute($subitem, $attrList[$aname], $data[1], $data[3]); unset($attrList[$aname]); } // Parse optional attributes foreach ($optional as $name => &$data) { $ok = false; if (isset($attrList[$name])) { $aname = $name; $ok = true; } elseif (($data[1] == self::EXPRESSION || $data[1] == self::ASSIGN_EXPR) && $this->_tpl->backwardCompatibility == true) { // DEPRECATED: Legacy code for compatibility with OPT 2.0 foreach ($exprEngines as $eeName => $eeValue) { if (isset($attrList[$eeName . ':' . $name])) { $aname = $eeName . ':' . $name; $data[3] = $eeName; $ok = true; break; } } } if (!$ok) { // We can't use isset() because the default data might be "NULL" if (!array_key_exists(2, $data)) { throw new Opt_APIMissingDefaultValue_Exception($name, $subitem->getXmlName()); } $config[$name] = $data[2]; continue; } $config[$name] = $this->_extractAttribute($subitem, $attrList[$aname], $data[1], $data[3]); unset($attrList[$aname]); } // The remaining tags must be processed using $unknown rule, however it // must be defined. if (!is_null($unknown)) { $type = $unknown[1]; foreach ($attrList as $name => $attr) { try { if (($type == self::EXPRESSION || $type == self::ASSIGN_EXPR) && $this->_tpl->backwardCompatibility == true) { // DEPRECATED: Legacy code for compatibility with OPT 2.0 $exprType = $unknown[3]; foreach ($exprEngines as $eeName => $eeValue) { if (isset($attrList[$eeName . ':' . $name])) { $name = substr($name, strlen($eeName), strlen($name) - strlen($eeName)); $exprType = $eeName; // Skip the tag, if this is a special OPT namespace. if ($this->_compiler->isNamespace($exprType)) { throw new Exception(); } break; } } } $return[$name] = $this->_extractAttribute($subitem, $attr, $type, $exprType); } catch (Exception $e) { /* null */ } } } return $return; }