/**
* Method to load the form description from an XML string or object.
*
* The replace option works per field. If a field being loaded already exists in the current
* form definition then the behavior or load will vary depending upon the replace flag. If it
* is set to true, then the existing field will be replaced in its exact location by the new
* field being loaded. If it is false, then the new field being loaded will be ignored and the
* method will move on to the next field to load.
*
* @param string $data The name of an XML string or object.
* @param boolean $replace Flag to toggle whether form fields should be replaced if a field
* already exists with the same group/name.
* @param string $xpath An optional xpath to search for the fields.
*
* @return boolean True on success, false otherwise.
*
* @since 1.7.0
*/
public function load($data, $replace = true, $xpath = null)
{
// If the data to load isn't already an XML element or string return false.
if (!$data instanceof \SimpleXMLElement && !\is_string($data)) {
return false;
}
// Attempt to load the XML if a string.
if (\is_string($data)) {
try {
$data = new \SimpleXMLElement($data);
} catch (\Exception $e) {
return false;
}
}
// If we have no XML definition at this point let's make sure we get one.
if (empty($this->xml)) {
// If no XPath query is set to search for fields, and we have a <form />, set it and return.
if (!$xpath && $data->getName() === 'form') {
$this->xml = $data;
// Synchronize any paths found in the load.
$this->syncPaths();
return true;
} else {
$this->xml = new \SimpleXMLElement('<form></form>');
}
}
// Get the XML elements to load.
$elements = [];
if ($xpath) {
$elements = $data->xpath($xpath);
} elseif ($data->getName() === 'form') {
$elements = $data->children();
}
// If there is nothing to load return true.
if (empty($elements)) {
return true;
}
// Load the found form elements.
foreach ($elements as $element) {
// Get an array of fields with the correct name.
$fields = $element->xpath('descendant-or-self::field');
foreach ($fields as $field) {
// Get the group names as strings for ancestor fields elements.
$attrs = $field->xpath('ancestor::fields[@name]/@name');
$groups = array_map('strval', $attrs ?: []);
// Check to see if the field exists in the current form.
if ($current = $this->findField((string) $field['name'], implode('.', $groups))) {
// If set to replace found fields, replace the data and remove the field so we don't add it twice.
if ($replace) {
$olddom = dom_import_simplexml($current);
$loadeddom = dom_import_simplexml($field);
$addeddom = $olddom->ownerDocument->importNode($loadeddom, true);
$olddom->parentNode->replaceChild($addeddom, $olddom);
$loadeddom->parentNode->removeChild($loadeddom);
} else {
unset($field);
}
}
}
// Merge the new field data into the existing XML document.
self::addNode($this->xml, $element);
}
// Synchronize any paths found in the load.
$this->syncPaths();
return true;
}