. * */ namespace Vvveb\System\Import; #[\AllowDynamicProperties] class Xml { private $db; private $dom; private $xpath; private $sqlPath; private $prefix; private $idsMap; private $importXMLOptions = LIBXML_NOBLANKS | LIBXML_COMPACT | LIBXML_NOCDATA | LIBXML_PARSEHUGE | LIBXML_NOWARNING | LIBXML_BIGLINES; private $tableOrder = []; private $cDataColumns = ['content', 'excerpt', 'name', 'title', 'description', 'meta_title', 'meta_description', 'slug']; function __construct($driver = DB_ENGINE, $host = DB_HOST, $dbname = DB_NAME, $user = DB_USER, $pass = DB_PASS, $port = DB_PORT, $prefix = DB_PREFIX) { $this->sqlPath = DIR_ROOT . "install/sql/$driver/"; $engine = '\Vvveb\System\Db\\' . ucfirst($driver); $this->prefix = $prefix; $this->db = new $engine($host, $dbname, $user, $pass, $port, $prefix); $this->tableOrder = include 'table-order.php'; } /* * Get all table names for the database */ function getTableNames($db = DB_NAME) { return $this->db->getTableNames(); } function export($tables = []) { $this->dom = new \DOMDocument('1.0', 'utf-8'); $this->dom->formatOutput = true; $this->dom->preserveWhiteSpace = false; if (empty($tables)) { $tables = $this->getTableNames(); } $root = $this->dom->createElement('root'); $database = $this->dom->createElement('database'); foreach ($tables as $tableName) { $table = $this->dom->createElement($tableName); $q = '`'; if (DB_ENGINE == 'pgsql') { $q = '"'; } $stmt = $this->db->query("SELECT * FROM $q$tableName$q"); //$result = $stmt->get_result(); while ($row = $this->db->fetchArray($stmt)) { $item = $this->dom->createElement('item'); foreach ($row as $key => $value) { $isString = is_string($value); if ($value === NULL) { $value = 'NULL'; } if ($value === 0) { $value = '0'; } if (! $value) { $value = ''; } if (in_array($key,$this->cDataColumns) || $isString) { //$element = $this->dom->createElement($key, $value); $cdata = $this->dom->createCDataSection($value); $element = $this->dom->createElement($key); $element->appendChild($cdata); } else { $element = $this->dom->createElement($key, $value); } $item->appendChild($element); } $table->appendChild($item); } if ($table->hasChildNodes()) { $database->appendChild($table); } } $root->appendChild($database); $this->dom->appendChild($root); return $this->dom->saveXML(); } function display_xml_error($error, $xml) { $return = $xml[$error->line - 1] . "\n"; $return .= str_repeat('-', $error->column) . "^\n"; switch ($error->level) { case LIBXML_ERR_WARNING: $return .= "Warning $error->code: "; break; case LIBXML_ERR_ERROR: $return .= "Error $error->code: "; break; case LIBXML_ERR_FATAL: $return .= "Fatal Error $error->code: "; break; } $return .= trim($error->message) . "\n Line: $error->line" . "\n Column: $error->column"; if ($error->file) { $return .= "\n File: $error->file"; } return "$return\n\n--------------------------------------------\n\n"; } function import($xml, $tables = []) { if ($tables) { //sort tables by table order $tableOrder = array_flip($this->tableOrder); usort($tables, function ($left, $right) use ($tableOrder) { return $tableOrder[$left] <=> $tableOrder[$right]; }); } else { $tables = $this->tableOrder; } $this->dom = new \DOMDocument('1.0', 'utf-8'); $this->dom->formatOutput = false; $this->dom->preserveWhiteSpace = false; $this->dom->strictErrorChecking = false; libxml_use_internal_errors(true); $result = $this->dom->loadXML($xml, $this->importXMLOptions); if (! $result) { $error = libxml_get_last_error(); $message = $this->display_xml_error($error, explode("\n", $xml)); throw new \Exception(sprintf("Unable to load XML content, check if valid XML!\n\t%s", $message), 1); return; } $this->dom->normalize(); $this->xpath = new \DOMXpath($this->dom); $domTables = $this->xpath->query('//root/database/*'); //map ids foreach ($domTables as $table) { $tableName = $table->nodeName; //continue; $rows = $table->childNodes; if (! is_null($rows)) { foreach ($rows as $row) { $columns = $row->childNodes; if ($columns) { foreach ($columns as $column) { //if ($column->nodeName == '#text') continue; $columnName = $column->nodeName; $columnValue = $column->nodeValue; $data[$columnName] = $columnValue; $idColumn = $tableName . '_id'; if ($columnName == $idColumn) { //$this->idsMap[$idColumn][$columnValue] = 0; $this->idsMap[$idColumn][$columnValue] = $columnValue; } } } } } } if (DB_ENGINE == 'mysqli') { $stmt = $this->db->execute('SET sql_mode = "";'); } $q = '`'; if (DB_ENGINE == 'pgsql') { $q = '"'; } foreach ($tables as $tableName) { $rows = $this->xpath->query("//root/database/$tableName/item"); if (! is_null($rows)) { foreach ($rows as $row) { $columns = $row->childNodes; $update = ''; $data = []; $idColumn = $tableName . '_id'; foreach ($columns as $column) { //if ($column->nodeName == '#text') continue; $columnName = $column->nodeName; $columnValue = $column->nodeValue; if ($columnName == 'parent_id' && $columnValue > 0) { //self refrence table id $oldId = $columnValue; $columnValue = $this->idsMap[$idColumn][$columnValue]; } if ($columnName == $idColumn) { $idValue = $columnValue; $this->idsMap[$idColumn][$idValue] = $idValue; } else { if (isset($this->idsMap[$columnName]) && $columnValue > 0) { $oldId = $columnValue; if (isset($this->idsMap[$columnName][$columnValue])) { $columnValue = $this->idsMap[$columnName][$columnValue]; } else { error_log("$tableName - $columnName with value `$columnValue` does not map, probably missing data"); } } $data[$columnName] = $columnValue; if ($update) { $update .= ','; } $update .= "$q$columnName$q = :$columnName"; } } $cols = implode("$q, $q",array_keys($data)); $values = implode(', :',array_keys($data)); $duplicate = "ON DUPLICATE KEY UPDATE $update\n\n"; if (DB_ENGINE == 'sqlite' || DB_ENGINE == 'pgsql') { //$duplicate = "ON CONFLICT($q$cols$q) DO UPDATE SET $update\n\n"; $duplicate = ''; } $sql = "INSERT INTO $q$tableName$q ($q$cols$q) VALUES (:$values)" . $duplicate; $stmt = $this->db->execute($sql, $data); $lastId = $this->db->insert_id; if ($idColumn != 'site_id' && $idColumn != 'language_id') { $this->idsMap[$idColumn][$idValue] = $lastId; } //$result = $stmt->get_result(); } } } return true; } }