first commit
This commit is contained in:
452
class/class.AutomaticImportTransform.php
Normal file
452
class/class.AutomaticImportTransform.php
Normal file
@@ -0,0 +1,452 @@
|
||||
<?php
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
|
||||
/**
|
||||
* AutomaticImportTransform.
|
||||
*
|
||||
* @author Petr Knap <knap@wpj.cz>
|
||||
*
|
||||
* @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í:
|
||||
* <xml>
|
||||
* <head>
|
||||
* <col>...</col>
|
||||
* ...
|
||||
* <col>...</col>
|
||||
* </head>
|
||||
* <item>
|
||||
* <col>...</col>
|
||||
* ...
|
||||
* <col>...</col>
|
||||
* </item>
|
||||
* ...
|
||||
* <item>...</item>
|
||||
* </xml>
|
||||
* 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("<?xml version='1.0' encoding='UTF-8'?><{$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));
|
||||
}
|
||||
Reference in New Issue
Block a user