* * @category Import */ class AutomaticImportTransform { private $pathToFile; private $typeOfFile; private $rootElementName = 'xml'; private $headElementName = 'head'; private $itemElementName = 'item'; private $colNames = []; private $encoding; /** * Zkouška existence souboru na disku. * * @param string $iPathToFile cesta k souboru na pevném disku * * @return bool */ private static function TestFile($iPathToFile) { return file_exists($iPathToFile); } /** * Konstruktor třídy. * * @param string $iPathToFile cesta k souboru na pevném disku * @param string|null $iTypeOfFile typ souboru (null = automatická detekce) */ public function __construct($iPathToFile, $iTypeOfFile = null) { if (self::TestFile($iPathToFile)) { $this->pathToFile = $iPathToFile; if ($iTypeOfFile === null) { $this->typeOfFile = IOFactory::identify($this->pathToFile); } else { $this->typeOfFile = $iTypeOfFile; } } } /** * Setter pro $rootElementName. * * @param string $iName název XML root tagu * * @return bool */ public function setRootElementName($iName) { if (is_string($iName) && !preg_match('/[^A-Za-z0-9]/', $iName)) { $this->rootElementName = $iName; return true; } return false; } /** * Setter pro $headElementName. * * @param string $iName název XML head tagu * * @return bool */ public function setHeadElementName($iName) { if (is_string($iName) && !preg_match('/[^A-Za-z0-9]/', $iName)) { $this->headElementName = $iName; return true; } return false; } /** * Setter pro $itemElementName. * * @param string $iName název XML item tagu * * @return bool */ public function setItemElementName($iName) { if (is_string($iName) && !preg_match('/[^A-Za-z0-9]/', $iName)) { $this->itemElementName = $iName; return true; } return false; } /** * Setter pro $colNames. * * @param array(string) $iNames Pole stringů obsahující názvy XML subitem tagy. Pole je ve tvaru $index => $nazev, např. array(0 => 'id', 1 => 'name',...). * * @return bool */ public function setColNames($iNames) { if (is_array($iNames)) { foreach ($iNames as $name) { if (!is_string($name) || preg_match('/[^A-Za-z0-9_]/', $name)) { return false; } } $this->colNames = $iNames; return true; } return false; } public function setEncoding($encoding) { if (empty($encoding)) { return; } switch ($this->typeOfFile) { case 'HTML': case 'Csv': ob_start(); // start output buffering readfile($this->pathToFile); // save buffer in a file $buffer = ob_get_clean(); @ob_end_clean(); $buffer = iconv($encoding, 'UTF-8', $buffer); if ($buffer !== false) { $this->pathToFile = "{$this->pathToFile}.utf8"; file_put_contents($this->pathToFile, $buffer); } break; case 'dbf': $this->encoding = $encoding; break; } } /** * Metoda pro načtení binárního tabulkového souboru (XLS, XLSX, ODS, atd.) do 3D pole. * * @return array|null */ public function LoadBinfileAsArray() { if (!self::TestFile($this->pathToFile)) { return null; } $output = []; $objReader = IOFactory::createReader($this->typeOfFile); $objReader->setLoadAllSheets(); $objPHPExcel = $objReader->load($this->pathToFile); $loadedSheetNames = $objPHPExcel->getSheetNames(); foreach ($loadedSheetNames as $sheetIndex => $loadedSheetName) { $objPHPExcel->setActiveSheetIndex($sheetIndex); $output[$loadedSheetName] = $objPHPExcel->getActiveSheet()->rangeToArray($objPHPExcel->getActiveSheet()->calculateWorksheetDataDimension(), null, true, true, true); } return $output; } /** * Metoda pro načtení CSV tabulkového souboru do 2D pole. * * @param string|null $iItemSeparator oddělovač položek v řádku * @param string|null $iLineSeparator oddělovač řádků * * @return array|null */ public function LoadCsvfileAsArray($iItemSeparator = null, $iLineSeparator = null) { if (!self::TestFile($this->pathToFile)) { return null; } $output = []; if ($iItemSeparator === null) { $ItemSeparator = ';'; } else { $ItemSeparator = $iItemSeparator; } if ($iLineSeparator === null) { $LineSeparator = "\n"; } else { $LineSeparator = $iLineSeparator; } $handle = fopen($this->pathToFile, 'r'); while (($row = fgetcsv($handle, null, $ItemSeparator, '"')) != false) { $output[] = $row; } fclose($handle); return $output; } /** * Metoda pro převod tabulkového souboru na XML. * * Základní XML struktura je následující: * * * ... * ... * ... * * * ... * ... * ... * * ... * ... * * Tuto strukturu lze modifikovat pomocí metod setRootElementName, setHeadElementName, setItemElementName a setColNames. * * @param int $iFirstDataLine index prvního datového řádku (číslováno od 1) * @param string|null $iSheetName_iItemSeparator název sešitu nebo znak oddělující položky na řádku * @param array $params další parametry s případnou konfigurací pro načtení souboru * * @return string|null */ public function GetXml($iHeaderLine = 0, $iFirstDataLine = 1, $iSheetName_iItemSeparator = null, array $params = []) { if (!self::TestFile($this->pathToFile)) { return null; } $output = new SimpleXMLElement("<{$this->rootElementName}/>"); switch ($this->typeOfFile) { case 'Xls': case 'Xml': case 'Xlsx': case 'Ods': $data = self::LoadBinfileAsArray(); if ($iSheetName_iItemSeparator === null) { $values = array_values($data); $data = array_shift($values); } else { $data = $data[$iSheetName_iItemSeparator]; } break; case 'json': $data = json_decode(file_get_contents($this->pathToFile), true); break; case 'dbf': $data = $this->LoadDBFDatabase(); break; default: // CSV $data = self::LoadCsvfileAsArray($iSheetName_iItemSeparator); $iFirstDataLine--; $iHeaderLine--; break; } if ($data === null) { return null; } // vložení hlavičky if ($iHeaderLine > 0 || ($params['forceHeaderLine'] ?? null) !== null) { $i = 0; $heads = $data[$params['forceHeaderLine'] ?? $iHeaderLine]; $head = $output->addChild($this->headElementName); if (count($heads) == 1 && strpos($heads[0], 'columns:') === 0) { // Úprava pro WinShop $heads = explode( ';', trim( str_replace('columns:', '', $heads[0]) ) ); } foreach ($heads as $name) { if (empty($this->colNames[$i])) { $this->colNames[$i] = empty($name) ? 'col' : createScriptURL_Text($name); } $head->addChild($this->colNames[$i], $name); $i++; } } // odtržení úvodní části for ($i = 1; $i < $iFirstDataLine; $i++) { array_shift($data); } // sestavení datové části foreach ($data as $A => $B) { $item = $output->addChild($this->itemElementName); $this->addItemChildren($item, $B); } return $output; } public function addItemChildren($parent, $data) { $i = 0; foreach ($data as $key => $item) { if (empty($this->colNames[$i])) { $this->colNames[$i] = 'col'; } $colName = $this->colNames[$i]; if ($this->typeOfFile == 'json') { $colName = $key; if (is_numeric($key)) { $colName = $this->itemElementName; } } if (is_array($item)) { $newParent = @$parent->addChild($colName); $this->addItemChildren($newParent, $item); } else { @$parent->addChild($colName, strtr($item, ['&' => '&', '>' => '>', '<' => '<'])); } $i++; } } /** * Metoda pro převod XML souboru za využití XSL. * * @param DomDocument $xsl xSL šablona * @param DomDocument $xml_doc XML dokument * * @return DomDocument|void|null */ public static function TransformXml($xsl, $xml_doc) { $xp = new XSLTProcessor(); $xp->registerPHPFunctions(['xsl_replace', 'count_same']); $xp->importStylesheet($xsl); if ($output = $xp->transformToDoc($xml_doc)) { return $output; } else { trigger_error('XSL transformation failed.', E_USER_ERROR); } } public static function LoadXMLFromFile($fileName) { if (!self::TestFile($fileName)) { trigger_error('Cannot load XML file {$fileName}.', E_USER_ERROR); return null; } $xml = new DOMDocument(); @$xml->load($fileName); return $xml; } public function LoadDBFDatabase() { $table = new XBase\Table($this->pathToFile); $columns = []; foreach ($table->getColumns() as $name => $column) { $columns[] = preg_replace('/[^[:print:]\p{L}]/u', '', $name); } if (count($columns) <= 0) { throw new Exception('Empty DBF file'); } $this->setColNames($columns); $data = []; while ($record = $table->nextRecord()/* && $i++ < 1000 */) { $row = []; foreach ($table->getColumns() as $name => $column) { if ($this->encoding && $column->getType() == XBase\Record::DBFFIELD_TYPE_CHAR) { $row[] = preg_replace('/[^[:print:]\p{L}]/u', '', iconv($this->encoding, 'UTF-8', $record->getString($name) ?? '')); } else { $row[] = $record->getString($name); } } $data[] = $row; } return $data; } } /** * Replace all occurrences of the search string with the replacement string. * * @param mixed $search The value being searched for, otherwise known as the needle. An array may be used to designate multiple needles. * @param mixed $replace The replacement value that replaces found search values. An array may be used to designate multiple replacements. * @param mixed $subject the string, DOM element or array being searched and replaced on, otherwise known as the haystack * @param int $count if passed, this will be set to the number of replacements performed * * @return mixed this function returns a string with the replaced values */ function xsl_replace($search, $replace, $subject, &$count = null) { if (is_array($subject)) { $subject = $subject[0]; } if (!is_string($subject)) { $subject = $subject->textContent; } return str_replace($search, $replace, $subject, $count); } /** * @param DOMElement $node */ function count_same($node, $attr) { if (is_array($node)) { $node = $node[0]; } var_dump($node->firstChild->textContent); exit; $next = $node; $value = $node->getAttribute($attr); var_dump([$value, $attr]); $count = 1; while ($next && $next->getAttribute($attr) == $value) { var_dump([$count, $next->getAttribute($attr)]); $count++; $next = $next->nextSibling; } exit(var_dump($count)); }