/** * parse model and config xml to object model using types in FieldTypes * @param SimpleXMLElement $xml model xml data (from items section) * @param SimpleXMLElement $config_data (current) config data * @param BaseField $internal_data output structure using FieldTypes,rootnode is internalData * @throws ModelException parse error */ private function parseXml($xml, &$config_data, &$internal_data) { // copy xml tag attributes to Field if ($config_data != null) { foreach ($config_data->attributes() as $AttrKey => $AttrValue) { $internal_data->setAttributeValue($AttrKey, $AttrValue->__toString()); } } // iterate model children foreach ($xml->children() as $xmlNode) { $tagName = $xmlNode->getName(); // every item results in a Field type object, the first step is to determine which object to create // based on the input model spec $fieldObject = null; $classname = "OPNsense\\Base\\FieldTypes\\" . $xmlNode->attributes()["type"]; if (class_exists($classname)) { // construct field type object $field_rfcls = new \ReflectionClass($classname); if (!$field_rfcls->getParentClass()->name == 'OPNsense\\Base\\FieldTypes\\BaseField') { // class found, but of wrong type. raise an exception. throw new ModelException("class " . $field_rfcls->name . " of wrong type in model definition"); } } else { // no type defined, so this must be a standard container (without content) $field_rfcls = new \ReflectionClass('OPNsense\\Base\\FieldTypes\\ContainerField'); } // generate full object name ( section.section.field syntax ) and create new Field if ($internal_data->__reference == "") { $new_ref = $tagName; } else { $new_ref = $internal_data->__reference . "." . $tagName; } $fieldObject = $field_rfcls->newInstance($new_ref, $tagName); // now add content to this model (recursive) if ($fieldObject->isContainer() == false) { $internal_data->addChildNode($tagName, $fieldObject); if ($xmlNode->count() > 0) { // if fieldtype contains properties, try to call the setters foreach ($xmlNode->children() as $fieldMethod) { $method_name = "set" . $fieldMethod->getName(); if ($field_rfcls->hasMethod($method_name)) { $fieldObject->{$method_name}($this->parseOptionData($fieldMethod)); } } } if ($config_data != null && isset($config_data->{$tagName})) { // set field content from config (if available) $fieldObject->setValue($config_data->{$tagName}->__toString()); } } else { // add new child node container, always try to pass config data if ($config_data != null && isset($config_data->{$tagName})) { $config_section_data = $config_data->{$tagName}; } else { $config_section_data = null; } if ($fieldObject instanceof ArrayField) { // handle Array types, recurring items if ($config_section_data != null) { foreach ($config_section_data as $conf_section) { // Array items are identified by a UUID, read from attribute or create a new one if (isset($conf_section->attributes()->uuid)) { $tagUUID = $conf_section->attributes()['uuid']->__toString(); } else { $tagUUID = $internal_data->generateUUID(); } // iterate array items from config data $child_node = new ContainerField($fieldObject->__reference . "." . $tagUUID, $tagName); $this->parseXml($xmlNode, $conf_section, $child_node); if (!isset($conf_section->attributes()->uuid)) { // if the node misses a uuid, copy it to this nodes attributes $child_node->setAttributeValue('uuid', $tagUUID); } $fieldObject->addChildNode($tagUUID, $child_node); } } else { // There's no content in config.xml for this array node. $tagUUID = $internal_data->generateUUID(); $child_node = new ContainerField($fieldObject->__reference . "." . $tagUUID, $tagName); $child_node->setInternalIsVirtual(); $this->parseXml($xmlNode, $config_section_data, $child_node); $fieldObject->addChildNode($tagUUID, $child_node); } } else { // All other node types (Text,Email,...) $this->parseXml($xmlNode, $config_section_data, $fieldObject); } // add object as child to this node $internal_data->addChildNode($xmlNode->getName(), $fieldObject); } } }