Commit 8e1285263d87896e66472bf4008b4ced2adc02d7

Authored by Mihail
1 parent c8b9fb48

add xlsx parser

composer.json
... ... @@ -4,23 +4,21 @@
4 4 "type": "library",
5 5 "description": "This extension provides a Multiparser solution for Yii framework 2.0.",
6 6 "keywords": [ "yii", "parser", "csv", "xml" ],
7   - "homepage": "https://github.com/tsurkanovm/multiparser.git",
  7 + "homepage": "https://github.com/tsurkanovm/yii-multiparser.git",
8 8 "license": "MIT",
9 9 "authors": [
10 10 {
11 11 "name": "Mihail Tsurkanov",
12 12 "email": "tsurkanovm@gmail.com",
13   - "homepage": "http://samoinvestor.ru",
14 13 "role": "Developer"
15 14 }
16 15 ],
17 16 "require": {
18   - "yiisoft/yii2": "*",
19   - "artweb/multiparser": "*"
  17 + "yiisoft/yii2": "*"
20 18 },
21 19 "autoload": {
22 20 "psr-4": {
23   - "yii\\multiparser\": ""
  21 + "artweb\\yii_multiparser\": ""
24 22 }
25 23 }
26 24 }
... ...
lib/Converter.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 31.08.2015
  6 + * Time: 12:50
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +// класс который содержит преобразователи значений (фильтры) используемые при парсинге
  12 +class Converter implements ConverterInterface
  13 +{
  14 +
  15 + const METHOD_PREFIX = 'convertTo';
  16 +
  17 + //public $configuration = [];
  18 +
  19 + public static function convertToFloat($value)
  20 + {
  21 + if ($value == '') {
  22 + $value = 0;
  23 + }
  24 + $value = trim(str_replace(",", ".", $value));
  25 + $value = preg_replace("/[^0-9.]+/", "", strtoupper($value));
  26 +
  27 + if ($value == '') {
  28 + return '';
  29 + }
  30 + $value = round((float)$value, 2);
  31 +
  32 + return $value;
  33 + }
  34 +
  35 + public static function convertToInteger($value)
  36 + {
  37 + if ($value == '') {
  38 + $value = 0;
  39 + }
  40 + $value = trim(str_replace(",", ".", $value));
  41 + $value = preg_replace("/[^0-9.]+/", "", strtoupper($value));
  42 + if ($value == '') {
  43 + return '';
  44 + }
  45 + $value = round((int)$value, 2);
  46 +
  47 + return $value;
  48 + }
  49 +
  50 + public static function convertToEncode($value)
  51 + {
  52 + $res = $value;
  53 + if (is_array($value)) {
  54 +
  55 + $res = Encoder::encodeArray($value);
  56 +
  57 + } elseif (is_string($value)) {
  58 +
  59 + $res = Encoder::encodeString($value);
  60 +
  61 + }
  62 + return $res;
  63 + }
  64 +
  65 + public static function convertToString($value)
  66 + {
  67 + $convert_func = function ($value_to_convert) {
  68 + return str_replace(array('!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '=', '-', '~', '`', '"', "'", ' ', '№', '%', ';', ':', '[', ']', '{', '}', '*', '?', '/', '\'', '|', '.', ',', '<', '>', '\\'), '', $value_to_convert);
  69 + };
  70 +
  71 + if( is_string( $value ) ){
  72 + $value = $convert_func( $value );
  73 + }
  74 +
  75 + if( is_array( $value ) ){
  76 + array_walk( $value, $convert_func );
  77 + }
  78 +
  79 + return $value;
  80 + }
  81 +
  82 + /**
  83 + * @param $name - имя метода конвертации
  84 + * @param $value - значение на конвертацию
  85 + * @return mixed
  86 + */
  87 + public static function __callStatic($name, $value)
  88 + {
  89 + $method_name = self::METHOD_PREFIX . $name;
  90 +
  91 + if (method_exists(static::class, $method_name)) {
  92 + return static::$method_name($value[0]);
  93 +
  94 + } else {
  95 + // если такого метода конвертации не предусмотрено, то возвращаем не конвертируя
  96 + return $value[0];
  97 +
  98 + }
  99 + }
  100 +
  101 + public function __call($name, $params)
  102 + {
  103 + return self::__callStatic($name, $params);
  104 + }
  105 +
  106 +
  107 + /**
  108 + * @param $arr - массив для конвертирования
  109 + * @param $configuration - массив конфигурация конвертирования
  110 + * @return mixed
  111 + * конвертирует массив по полученным настройкам, вызывая последовательно функции конвертации (указанные в конфигурации)
  112 + */
  113 + public static function convertByConfiguration($arr, $configuration)
  114 + {
  115 + if ($hasKey = isset($configuration['hasKey']))
  116 + unset($configuration['hasKey']);
  117 +
  118 + if (isset($configuration['configuration'])) {
  119 + $arr_config = $configuration['configuration'];
  120 + unset($configuration['configuration']);
  121 + } else {
  122 + throw new \Exception('Не указан обязательный параметр конфигурационного файла - converter_conf[configuration]');
  123 + }
  124 +
  125 + // проставим аттрибуты из конфига{}{}
  126 + self::setAttributes($configuration);
  127 +
  128 + foreach ($arr_config as $key => $value) {
  129 + if ($hasKey) {
  130 + // у нас ассоциативный массив, и мы можем конвертировать каждое значение в отдельности
  131 + if (is_array($value)) {
  132 + //если пустой массив то конвертируем всю строку
  133 + if (count($value) === 0) {
  134 +
  135 + $arr = self::$key($arr);
  136 + continue;
  137 + }
  138 + // иначе конвертируем каждую ячейку в отдельности
  139 + foreach ($value as $sub_value) {
  140 + if (isset($arr[$sub_value])) {
  141 + // конвертируем только те ячейки которые сопоставлены в прочитанном массиве с колонками в конфигурационном файле
  142 + $arr[$sub_value] = self::$key($arr[$sub_value]);
  143 + }
  144 +
  145 + }
  146 + } else {
  147 +
  148 + if (isset($arr[$value])) {
  149 + // конвертируем только те ячейки которые сопоставлены в прочитанном массиве с колонками в конфигурационном файле
  150 + $arr[$value] = self::$key($arr[$value]);
  151 + // CustomVarDamp::dump($result);
  152 + }
  153 +
  154 + }
  155 +
  156 + } else {
  157 + // нет заголовка - мы можем конвертировать только строку в целом
  158 + $arr = self::$key($arr);
  159 + }
  160 +
  161 + }
  162 +
  163 + return $arr;
  164 + }
  165 +
  166 + public static function setAttributes($configuration)
  167 + {
  168 + foreach ($configuration as $key_setting => $setting) {
  169 + if (property_exists(static::class, $key_setting))
  170 + static::$$key_setting = $setting;
  171 + }
  172 +
  173 + }
  174 +
  175 +
  176 +}
0 177 \ No newline at end of file
... ...
lib/ConverterInterface.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 20.10.2015
  6 + * Time: 13:38
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +
  12 +interface ConverterInterface {
  13 +
  14 + public static function convertByConfiguration( $arr_values_to_convert, $configuration );
  15 +
  16 +}
0 17 \ No newline at end of file
... ...
lib/CsvParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 +
  4 + */
  5 +namespace artweb\yii_multiparser;
  6 +use common\components\CustomVarDamp;
  7 +
  8 +
  9 +/**
  10 + * Class CsvParser
  11 + * @package artweb\yii_multiparser
  12 + * @todo - перевести на анг. яз.
  13 + */
  14 +class CsvParser extends TableParser
  15 +{
  16 + /** @var string - разделитель csv */
  17 + public $delimiter = ';';
  18 +
  19 +
  20 +
  21 + /**
  22 + * метод устанвливает нужные настройки объекта SplFileObject, для работы с csv
  23 + */
  24 + public function setup()
  25 + {
  26 +
  27 + $this->file->setCsvControl($this->delimiter);
  28 + $this->file->setFlags(\SplFileObject::READ_CSV);
  29 + $this->file->setFlags(\SplFileObject::SKIP_EMPTY);
  30 +
  31 + parent::setup();
  32 +
  33 + }
  34 +
  35 + public function read()
  36 + {
  37 + parent::read();
  38 +
  39 + return $this->result;
  40 + }
  41 +
  42 +
  43 + protected function readRow( )
  44 + {
  45 + $this->row = $this->file->fgetcsv();
  46 + }
  47 +
  48 + protected function isEmptyRow(){
  49 +
  50 + $is_empty = false;
  51 +
  52 + if ($this->row === false || $this->row === NULL ) {
  53 + return true;
  54 + }
  55 +
  56 + $j = 0;
  57 + for ($i = 1; $i <= count( $this->row ); $i++) {
  58 +
  59 + if ( $this->isEmptyColumn( $this->row[$i - 1] ) ) {
  60 + $j++;
  61 + }
  62 +
  63 + if ( $j >= $this->min_column_quantity ) {
  64 + $is_empty = true;
  65 + break;
  66 + }
  67 + }
  68 +
  69 + return $is_empty;
  70 + }
  71 +
  72 + protected function isEmptyColumn( $val ){
  73 + return $val == '';
  74 + }
  75 +}
0 76 \ No newline at end of file
... ...
lib/DynamicFormHelper.php
... ... @@ -6,7 +6,7 @@
6 6 * Time: 14:50
7 7 */
8 8  
9   -namespace yii\multiparser;
  9 +namespace artweb\yii_multiparser;
10 10  
11 11 use yii\base\DynamicModel;
12 12 use yii\grid\GridView;
... ...
lib/Encoder.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 27.08.2015
  6 + * Time: 13:36
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +// @todo add comments
  12 +class Encoder
  13 +{
  14 + /** @var out encoding charset */
  15 + public static $out_charset = 'UTF-8';
  16 + /** @var out encoding charset */
  17 + public static $in_charset = 'windows-1251';
  18 +
  19 + public static function encodeFile($in_charset, $out_charset, $filePath)
  20 + {
  21 +
  22 + $old_content = file_get_contents($filePath);
  23 + $encode_content = self::encodeString( $old_content, $in_charset, $out_charset );
  24 + $file = @fopen($filePath, "w");
  25 + fwrite($file, $encode_content);
  26 + @fclose($file);
  27 + }
  28 +
  29 + public static function encodeArray( $array, $in_charset = '', $out_charset = '')
  30 + {
  31 + if ($in_charset)
  32 + self::$in_charset = $in_charset;
  33 +
  34 + if ($out_charset)
  35 + self::$out_charset = $out_charset;
  36 +
  37 + $result = array_map(
  38 + function ($value) {
  39 +
  40 + return self::encodeString( $value, self::$in_charset, self::$out_charset );
  41 +
  42 + },
  43 + $array);
  44 +
  45 + return $result;
  46 + }
  47 +
  48 + public static function encodeString( $source, $in_charset = '', $out_charset = '' ){
  49 +
  50 + if ($in_charset)
  51 + self::$in_charset = $in_charset;
  52 +
  53 + if ($out_charset)
  54 + self::$out_charset = $out_charset;
  55 +
  56 + return iconv( self::$in_charset, self::$out_charset, $source );
  57 +
  58 + }
  59 +}
0 60 \ No newline at end of file
... ...
lib/ObjectCreator.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 20.10.2015
  6 + * Time: 16:24
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +
  12 +use common\components\CustomVarDamp;
  13 +
  14 +class ObjectCreator {
  15 + public static function build( array $configuration ){
  16 + if ( isset( $configuration['class'] ) ) {
  17 + $class = trim( $configuration['class'] );
  18 + unset( $configuration['class'] );
  19 + } else{
  20 + throw new \ErrorException('Error configuration - undefined class');
  21 + }
  22 +
  23 + $object = new $class();
  24 + foreach ($configuration as $name => $value) {
  25 + $object->$name = $value;
  26 + }
  27 +
  28 + return $object;
  29 + }
  30 +}
0 31 \ No newline at end of file
... ...
lib/Parser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 04.09.2015
  6 + * Time: 18:25
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +//@todo - заменить read на parse
  12 +//@todo - xml - убрать из названий функций xml и array - это и так понятно
  13 +
  14 +
  15 +use common\components\CustomVarDamp;
  16 +
  17 +abstract class Parser
  18 +{
  19 + public $converter_conf = [];
  20 + protected $converter = NULL;
  21 +
  22 + /**
  23 + * @var array - результирующий массив с отпарсенными значениями
  24 + */
  25 + protected $result = [];
  26 +
  27 + /** @var array - массив с заголовком,
  28 + * */
  29 + public $keys = NULL;
  30 + /** @var bool
  31 + имеет ли файл заголовок который будет установлен ключами возвращемого массива*/
  32 + public $has_header_row = false;
  33 +
  34 + /** @var экземляр SplFileObject читаемого файла */
  35 + public $file;
  36 +
  37 +
  38 +
  39 + public function setup()
  40 + {
  41 + $this->setupConverter();
  42 + }
  43 +
  44 + protected function setupConverter()
  45 + {
  46 + if ( $this->has_header_row || $this->keys !== NULL ) {
  47 + // если у файла есть заголовок, то в результате имеем ассоциативный массив
  48 + $this->converter_conf['hasKey'] = 1;
  49 + }
  50 +
  51 + if ( $this->converter_conf ) {
  52 + $converter = ObjectCreator::build( $this->converter_conf );
  53 + if ( $converter instanceof ConverterInterface ) {
  54 +
  55 + $this->converter = $converter;
  56 +
  57 + }
  58 + }
  59 +
  60 +
  61 + }
  62 +
  63 + public abstract function read();
  64 +
  65 + /**
  66 + * @param $arr
  67 + * @return mixed
  68 + * преобразовует значения прочитанного массива в нужные типы, согласно конфигурации конвертера
  69 + */
  70 + protected function convert( $arr )
  71 + {
  72 +
  73 + if ($this->converter !== NULL) {
  74 +
  75 + $arr = $this->converter->convertByConfiguration( $arr, $this->converter_conf );
  76 +
  77 + }
  78 +
  79 +
  80 + return $arr;
  81 +
  82 + }
  83 +}
0 84 \ No newline at end of file
... ...
lib/ParserHandler.php 0 → 100644
  1 +<?php
  2 +
  3 +namespace artweb\yii_multiparser;
  4 +
  5 +use common\components\CustomVarDamp;
  6 +
  7 +class ParserHandler
  8 +{
  9 + //@todo - добавить комменты на анг язе (ошибки выкидывать тоже на англ яз.)
  10 + //@todo - сделать универсальную обработку ошибок
  11 + //@todo - возможно отказаться от YiiParserHandler
  12 + const DEFAULT_MODE = 'web';
  13 + /** @var string */
  14 + protected $filePath;
  15 +
  16 + /** @var string */
  17 + protected $configuration = [];
  18 + /** @var string */
  19 + protected $custom_configuration = [];
  20 +
  21 + /** @var instance of SplFileObject */
  22 + protected $fileObject;
  23 +
  24 + /** @var string - extension of file $filePath */
  25 + protected $extension;
  26 +
  27 + /** @var string - */
  28 + protected $mode;
  29 +
  30 + /** @var string - */
  31 + protected $options;
  32 +
  33 + /**
  34 + * @param string first line in file for parsing
  35 + */
  36 + public function setup($filePath, $options = [])
  37 + {
  38 + $this->filePath = $filePath;
  39 + if (isset($options['mode'])) {
  40 +
  41 + $this->mode = $options['mode'];
  42 + unset($options['mode']);
  43 +
  44 + } else {
  45 +
  46 + $this->mode = self::DEFAULT_MODE;
  47 +
  48 + }
  49 +
  50 + $this->options = $options;
  51 +
  52 + $this->fileObject = new \SplFileObject($this->filePath, 'r');
  53 +
  54 + $options['file'] = $this->fileObject;
  55 + $this->extension = $this->fileObject->getExtension();
  56 +
  57 + $this->custom_configuration = $this->getCustomConfiguration($this->extension, $this->mode);
  58 + $this->custom_configuration = array_merge_recursive($this->custom_configuration, $options);
  59 +
  60 + }
  61 +
  62 + public function run()
  63 + {
  64 + $result = [];
  65 + if (count($this->custom_configuration)) {
  66 +
  67 + $parser = $this->createObjectByConfiguration($this->custom_configuration);
  68 +
  69 + try {
  70 +
  71 + $parser->setup();
  72 + $result = $parser->read();
  73 +
  74 + } catch (\ErrorException $e) {
  75 +
  76 + echo $e->getMessage();
  77 +
  78 + }
  79 +
  80 + }
  81 +
  82 + return $result;
  83 + }
  84 +
  85 + public function getCustomConfiguration($extension, $parameter)
  86 + {
  87 + if (!count($this->configuration)) {
  88 + $this->setConfiguration(require(__DIR__ . '/config.php'));
  89 + }
  90 +
  91 + if (!isset($this->configuration[$extension])) {
  92 + throw new \ErrorException("Parser do not maintain file with extension {$extension}");
  93 + }
  94 + if (!isset($this->configuration[$extension][$parameter])) {
  95 + throw new \ErrorException("Parser configurator do not have settings for {$parameter} parameter");
  96 + }
  97 +
  98 + return $this->configuration[$extension][$parameter];
  99 + }
  100 +
  101 + public function setConfiguration($configuration)
  102 + {
  103 + $this->configuration = $configuration;
  104 + }
  105 +
  106 + protected function createObjectByConfiguration($configuration)
  107 + {
  108 + return ObjectCreator::build($configuration);
  109 + }
  110 +}
  111 +
  112 +
... ...
lib/TableParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 22.10.2015
  6 + * Time: 15:53
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +
  12 +use common\components\CustomVarDamp;
  13 +
  14 +abstract class TableParser extends Parser {
  15 +
  16 +
  17 + /**
  18 + * @var array - текущий отпарсенный ряд
  19 + */
  20 + protected $row = [];
  21 +
  22 + /** @var int - первая строка с которой начинать парсить */
  23 + public $first_line = 0;
  24 +
  25 + /** @var int - последняя строка до которой парсить
  26 + * если не указана, то парсинг происходит до конца файла*/
  27 + public $last_line = 0;
  28 +
  29 + /** @var int - первая колонка файла с которой начнется парсинг */
  30 + public $first_column = 0;
  31 +
  32 +
  33 + /** @var bool
  34 + нужно ли искать автоматически первоую значисмую строку (не пустая строка)
  35 + * иначе первая строка будет взята из аттрибута $first_line */
  36 + public $auto_detect_first_line = false;
  37 +
  38 + /** @var int - количество значимых колонок, что бы определить первую значимую строку
  39 + * используется при автоопределении первой строки*/
  40 + public $min_column_quantity = 5;
  41 + /** @var int - количество пустых строк, что бы определить конец файла,
  42 + такое количеество подряд пустых строк считается концом файла*/
  43 + public $empty_lines_quantity = 3;
  44 +
  45 +
  46 + /** @var int - номер текущей строки парсера */
  47 + protected $current_row_number = 0;
  48 +
  49 +
  50 + protected abstract function isEmptyRow();
  51 +
  52 + protected abstract function isEmptyColumn($column_value);
  53 +
  54 + protected abstract function readRow();
  55 +
  56 +
  57 + public function read()
  58 + {
  59 + if ($this->auto_detect_first_line) {
  60 + $this->shiftToFirstValuableLine();
  61 + }
  62 +
  63 + // будем считать количество пустых строк подряд - при достижении $empty_lines_quantity - считаем что это конец файла и выходим
  64 + $empty_lines = 0;
  65 + while ( $empty_lines < $this->empty_lines_quantity ) {
  66 + // прочтем строку из файла
  67 + $this->readRow();
  68 +
  69 + if ( $this->isEmptyRow() ) {
  70 + //счетчик пустых строк
  71 + $empty_lines++;
  72 + continue;
  73 + }
  74 +
  75 + // уберем пустые колонки из ряда
  76 + $this->filterRow();
  77 +
  78 +
  79 + $this->adjustRowToSettings( );
  80 +
  81 + // строка не пустая, имеем прочитанный массив значений
  82 + $this->current_row_number++;
  83 +
  84 + // для первой строки утановим ключи из заголовка
  85 + $this->setKeysFromHeader();
  86 +
  87 + // если у нас установлен лимит, при его достижении прекращаем парсинг
  88 + if ( $this->isLastLine() )
  89 + break;
  90 +
  91 + // обнуляем счетчик, так как считаюся пустые строки ПОДРЯД
  92 + $empty_lines = 0;
  93 +
  94 + $this->result[] = $this->row;
  95 + $this->row = [];
  96 +
  97 + }
  98 +
  99 +
  100 + }
  101 + /**
  102 + * определяет первую значимую строку,
  103 + * считывается файл пока в нем не встретится строка с непустыми колонками
  104 + * в количестве указанном в атрибуте min_column_quantity
  105 + * в результате выполнения $current_row_number будет находится на последней незначимой строке
  106 + */
  107 + protected function shiftToFirstValuableLine()
  108 + {
  109 + do {
  110 +
  111 + $this->current_row_number ++;
  112 + $this->readRow();
  113 +
  114 + } while( $this->isEmptyRow() );
  115 +
  116 + // @todo - сделать опционально
  117 + // код для того что бы парсить первую строку, закомментировано как предполагается что первая значимая строка это заголовок
  118 + // $this->current_row_number --;
  119 +// $this->file->seek( $this->current_row_number );
  120 + }
  121 +
  122 + /**
  123 + * @return array - одномерный массив результата парсинга строки
  124 + */
  125 + protected function adjustRowToSettings( )
  126 + {
  127 +
  128 + // если есть заголовок, то перед конвертацией его нужно назначить
  129 + if ( $this->keys !== NULL ) {
  130 +
  131 + if (count($this->keys) !== count($this->row)) {
  132 + throw new \Exception("Ошибка парсинга файла в строке # {$this->current_row_number}. Не соответсвие числа ключевых колонок (заголовка) - числу колонок с данными", 0, 1, $this->file->getBasename(), $this->current_row_number);
  133 + }
  134 +
  135 + $this->row = array_combine($this->keys, $this->row);
  136 + }
  137 +
  138 + // попытаемся конвертировать прочитанные значения согласно конфигурации котнвертера значений
  139 + $this->row = $this->convert($this->row);
  140 +
  141 + // обрежем массив к первой значимой колонке
  142 + if ( $this->first_column ) {
  143 +
  144 + $this->row = array_slice($this->row, $this->first_column);
  145 +
  146 + }
  147 +
  148 + }
  149 +
  150 + protected function setKeysFromHeader(){
  151 + if ( $this->has_header_row ) {
  152 + // в файле есть заголовок, но он еще не назначен - назначим
  153 + if ($this->keys === NULL) {
  154 + $this->keys = array_values( $this->row );
  155 + }
  156 + }
  157 + }
  158 +
  159 + protected function filterRow(){
  160 + $this->row = array_filter( $this->row, function($val){
  161 + return !$this->isEmptyColumn($val);
  162 + });
  163 + }
  164 +
  165 + protected function isLastLine(){
  166 +
  167 + if ( ( $this->last_line ) && ( $this->current_row_number > $this->last_line ) ) {
  168 + return true;
  169 + }
  170 + return false;
  171 + }
  172 +
  173 +}
0 174 \ No newline at end of file
... ...
lib/XlsxParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Tsurkanov
  5 + * Date: 21.10.2015
  6 + * Time: 15:44
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +
  12 +use common\components\CustomVarDamp;
  13 +
  14 +
  15 +/**
  16 + * Class XlsxParser
  17 + * @package artweb\yii_multiparser
  18 + */
  19 +class XlsxParser extends TableParser {
  20 +
  21 + /**
  22 + * @var string - путь куда будут распаковываться файлы, если не указанно - во временный каталог сервера
  23 + */
  24 + public $path_for_extract_files = '';
  25 +
  26 +
  27 + /**
  28 + * @var int - если указано то считывание будет производиться с этого листа, иначе со всех листов
  29 + * при чтении со всех листов - выходной массив будет иметь номера листов первыми элементами
  30 + */
  31 + public $active_sheet = 0;
  32 +
  33 + protected $strings_arr = [];
  34 + protected $sheets_arr = [];
  35 +
  36 + protected $current_node;
  37 + protected $current_sheet;
  38 +
  39 + public function setup()
  40 + {
  41 +
  42 + parent::setup();
  43 +
  44 + if ( $this->path_for_extract_files == '' ) {
  45 + $this->path_for_extract_files = sys_get_temp_dir();
  46 + }
  47 + }
  48 +
  49 +
  50 + public function read()
  51 + {
  52 +
  53 + // $this->extractFiles();
  54 +
  55 + $this->readSheets();
  56 + $this->readStrings();
  57 +
  58 + foreach ( $this->sheets_arr as $sheet ) {
  59 + //проходим по всем файлам из директории /xl/worksheets/
  60 +
  61 + $sheet_path = $this->path_for_extract_files . '/xl/worksheets/' . $sheet . '.xml';
  62 + if ( file_exists( $sheet_path ) && is_readable( $sheet_path ) ) {
  63 +
  64 + $xml = simplexml_load_file( $sheet_path, "SimpleXMLIterator" );
  65 + $this->current_node = $xml->sheetData->row;
  66 + $this->current_node->rewind();
  67 +
  68 + parent::read();
  69 +
  70 + }
  71 +
  72 + }
  73 +CustomVarDamp::dumpAndDie($this->$result);
  74 + // return $this->$result_arr;
  75 + }
  76 +
  77 + protected function extractFiles ()
  78 + {
  79 + $zip = new \ZipArchive;
  80 + if ( $zip->open( $this->file->getPathname() ) === TRUE ) {
  81 + $zip->extractTo( $this->path_for_extract_files );
  82 + $zip->close();
  83 + } else {
  84 + throw new \Exception( 'Ошибка чтения xlsx файла' );
  85 + }
  86 + }
  87 +
  88 + protected function readSheets ()
  89 + {
  90 + if ( $this->active_sheet ) {
  91 + $this->sheets_arr[ $this->active_sheet ] = 'Sheet' . $this->active_sheet;
  92 + return;
  93 + }
  94 +
  95 + $xml = simplexml_load_file( $this->path_for_extract_files . '/xl/workbook.xml' );
  96 + foreach ( $xml->sheets->children() as $sheet ) {
  97 + $sheet_name = '';
  98 + $sheet_id = 0;
  99 + $attr = $sheet->attributes();
  100 + foreach ( $attr as $name => $value ) {
  101 + if ($name == 'name')
  102 + $sheet_name = (string)$value;
  103 +
  104 + if ($name == 'sheetId')
  105 + $sheet_id = $value;
  106 +
  107 + }
  108 + if ( $sheet_name && $sheet_id ) {
  109 + $this->sheets_arr[$sheet_name] = 'Sheet' . $sheet_id;
  110 + }
  111 +//
  112 + }
  113 + }
  114 +
  115 + protected function readStrings ()
  116 + {
  117 + $xml = simplexml_load_file( $this->path_for_extract_files . '/xl/sharedStrings.xml' );
  118 + foreach ( $xml->children() as $item ) {
  119 + $this->strings_arr[] = (string)$item->t;
  120 + }
  121 + }
  122 +
  123 +
  124 +
  125 + // protected function readRow ( $item, $sheet , $current_row )
  126 + protected function readRow ( )
  127 + {
  128 + $node = $this->current_node->getChildren();
  129 +
  130 + foreach ( $node as $child ) {
  131 + $attr = $child->attributes();
  132 +
  133 + if( isset($child->v) ) {
  134 + $value = (string)$child->v;
  135 + }else{
  136 + $value = '';
  137 + }
  138 + if ( isset( $attr['t'] ) ) {
  139 + // $this->result_arr[$sheet][$current_row][$cell] = $this->strings_arr[ $value ];
  140 + $this->row[] = $this->strings_arr[ $value ];
  141 + }else{
  142 + // $this->result_arr[$sheet][$current_row][$cell] = $value;
  143 + $this->row[] = $value;
  144 + }
  145 +
  146 + }
  147 + $this->current_node->next();
  148 + CustomVarDamp::dump($this->row);
  149 + }
  150 +
  151 + protected function isEmptyRow(){
  152 +
  153 + $is_empty = false;
  154 +
  155 + if ( !count( $this->row ) || !$this->current_node->valid() ) {
  156 + return true;
  157 + }
  158 +
  159 + $j = 0;
  160 + for ($i = 1; $i <= count( $this->row ); $i++) {
  161 +
  162 + if ( $this->isEmptyColumn( $this->row[$i - 1] ) ) {
  163 + $j++;
  164 + }
  165 +
  166 + if ( $j >= $this->min_column_quantity ) {
  167 + $is_empty = true;
  168 + break;
  169 + }
  170 + }
  171 +
  172 + return $is_empty;
  173 + }
  174 +
  175 + protected function isEmptyColumn( $val ){
  176 + return $val == '';
  177 + }
  178 +}
0 179 \ No newline at end of file
... ...
lib/XmlParser.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 10.09.2015
  6 + * Time: 17:47
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +
  12 +use common\components\CustomVarDamp;
  13 +use common\components\CustomArrayHelper;
  14 +
  15 +class XmlParser extends Parser{
  16 +
  17 + public $node;
  18 +
  19 + public function read()
  20 + {
  21 + $file = $this->file;
  22 + $result = $this->xmlToArray( $file->getPathname() );
  23 +
  24 + if ( isset($this->node) ) {
  25 +
  26 + $result = $result[ $this->node ];
  27 +
  28 + }
  29 +
  30 + return $result;
  31 + }
  32 +
  33 +
  34 + /**
  35 + * Converts an XML string to a PHP array
  36 + *
  37 + * @uses recursiveXMLToArray()
  38 + * @param string $file_path
  39 + * @return array
  40 + */
  41 + protected function xmlToArray( $file_path ) {
  42 +
  43 + try {
  44 + $xml = new \SimpleXMLElement( $file_path, 0, true );
  45 + //\common\components\CustomVarDamp::dumpAndDie($xml->children()->children());
  46 + $result = $this->recursiveXMLToArray( $xml );
  47 + } catch(Exception $ex) {
  48 +
  49 + throw $ex;
  50 + }
  51 +
  52 + return $result;
  53 + }
  54 +
  55 + /**
  56 + * Convert a XML string to a PHP array recursively. Do not
  57 + * call this function directly
  58 + *
  59 + * @param SimpleXMLElement
  60 + *
  61 + * @return mixed
  62 + */
  63 + protected function recursiveXMLToArray($xml) {
  64 + if( $xml instanceof \SimpleXMLElement ) {
  65 + $attributes = $xml->attributes();
  66 +
  67 + foreach( $attributes as $key => $value ) {
  68 + if( $value ) {
  69 + $attribute_array[$key] = (string) $value;
  70 + }
  71 + }
  72 + $previous_xml = $xml;
  73 + $xml = get_object_vars($xml);
  74 + }
  75 +
  76 + if(is_array($xml)) {
  77 +
  78 + if( count($xml) == 0 )
  79 + return (string) $previous_xml; // for CDATA
  80 +
  81 + foreach($xml as $key => $value) {
  82 + $row[$key] = $this->recursiveXMLToArray($value);
  83 + }
  84 + if ( is_string($value) ) {
  85 + // дошли до конца рекурсии
  86 + // преобразуем ряд согласно конфигурации
  87 + if ( $this->keys !== NULL ) {
  88 + // назначим ключи из конфигурации, согласно массиву $keys
  89 + $row = $this->compareArrayWithKeys( $row );
  90 + }
  91 + $row = $this->convert( $row );
  92 +
  93 + }
  94 +
  95 +
  96 + if( isset( $attribute_array ) )
  97 + $row['@'] = $attribute_array; // Attributes
  98 +
  99 + return $row;
  100 + }
  101 + return (string) $xml;
  102 + }
  103 +
  104 + /**
  105 + * @param array $value_arr - текущий ряд, массив, которому нужно назначить конфигурационные ключи ($keys)
  106 + * @return array
  107 + */
  108 + protected function compareArrayWithKeys( array $value_arr ){
  109 + $res = $this->keys;
  110 + foreach ( $this->keys as $key => $value ) {
  111 + if ( array_key_exists( $value, $value_arr ) ) {
  112 + $res[$key] = $value_arr[$value];
  113 + }
  114 + }
  115 + return $res;
  116 + }
  117 +
  118 +}
0 119 \ No newline at end of file
... ...
lib/YiiConverter.php 0 → 100644
  1 +<?php
  2 +/**
  3 + * Created by PhpStorm.
  4 + * User: Cibermag
  5 + * Date: 07.09.2015
  6 + * Time: 15:56
  7 + */
  8 +
  9 +namespace artweb\yii_multiparser;
  10 +
  11 +use common\components\CustomVarDamp;
  12 +use yii\base\Component;
  13 +use yii\base\ErrorException;
  14 +
  15 +
  16 +class YiiConverter extends Component{
  17 +
  18 +public $configuration;
  19 +public $converter;
  20 +
  21 + public function init()
  22 + {
  23 + parent::init();
  24 + $converter = \Yii::createObject( $this->configuration );
  25 + if ( $converter instanceof ConverterInterface ) {
  26 +
  27 + $this->converter = $converter;
  28 + }else{
  29 + throw new ErrorException('Wrong type of converter');
  30 + }
  31 +
  32 +
  33 + }
  34 +
  35 + public function convertTo( $method, $value, $attributes = [] ){
  36 +
  37 + if ( $attributes ) {
  38 + $this->converter->setAttributes($attributes);
  39 + }
  40 + return $this->converter->$method( $value );
  41 +
  42 + }
  43 +
  44 + public function convertByConfiguration( $value, $configuration ){
  45 +
  46 + return $this->converter->convertByConfiguration( $value, $configuration );
  47 +
  48 + }
  49 +
  50 +
  51 +}
0 52 \ No newline at end of file
... ...
lib/YiiMultiparser.php
... ... @@ -6,7 +6,7 @@
6 6 * Time: 15:56
7 7 */
8 8  
9   -namespace yii\multiparser;
  9 +namespace artweb\yii_multiparser;
10 10  
11 11 use common\components\CustomVarDamp;
12 12 use yii\base\Component;
... ... @@ -17,27 +17,29 @@ use yii\base\Component;
17 17 class YiiMultiparser extends Component{
18 18  
19 19 public $configuration;
  20 +public $parserHandler;
20 21  
21   - public function getConfiguration($extension, $conf_parameter){
22   -
23   - if (!isset( $this->configuration[$extension] )){
24   - throw new \ErrorException( "Parser do not maintain file with extension {$extension}");
25   - }
26   - if (!isset( $this->configuration[$extension][$conf_parameter] )){
27   - throw new \ErrorException( "Parser configurator do not have settings for {$conf_parameter} parameter");
28   - }
29   -
30   - return $this->configuration[$extension][$conf_parameter];
  22 + public function init()
  23 + {
  24 + parent::init();
  25 + $this->parserHandler = new YiiParserHandler( );
  26 + $this->parserHandler->setConfiguration( $this->configuration );
31 27  
32 28 }
33 29  
34 30  
35 31 public function parse( $filePath, $options = [] ){
36 32  
37   - $parser = new YiiParserHandler( $filePath, $options );
38   - return $parser->run();
  33 + $this->parserHandler->setup( $filePath, $options );
  34 +
  35 + return $this->parserHandler->run();
39 36  
40 37 }
41 38  
  39 + public function getConfiguration( $extension, $parameter ){
  40 +
  41 + return $this->parserHandler->getCustomConfiguration( $extension, $parameter );
  42 +
  43 + }
42 44  
43 45 }
44 46 \ No newline at end of file
... ...
lib/YiiParserHandler.php
... ... @@ -6,7 +6,7 @@
6 6 * Time: 15:53
7 7 */
8 8  
9   -namespace yii\multiparser;
  9 +namespace artweb\yii_multiparser;
10 10  
11 11  
12 12 use common\components\CustomVarDamp;
... ... @@ -17,68 +17,74 @@ class YiiParserHandler extends ParserHandler{
17 17 /**
18 18 * @param $filePath
19 19 * @param array $options
20   - * проверяет читабенльность переданного файла, а также наличие настроек парсера в конфигурационном файле для данного типа файла
  20 + * проверяет читабельность переданного файла, а также наличие настроек парсера в конфигурационном файле для данного типа файла
21 21 */
22   - public function __construct($filePath, $options = [])
  22 +// public function setup($filePath, $options = [])
  23 +// {
  24 +// $this->filePath = $filePath;
  25 +// if (isset($options['mode'])) {
  26 +//
  27 +// $this->mode = $options['mode'];
  28 +// unset($options['mode']);
  29 +//
  30 +// } else {
  31 +//
  32 +// $this->mode = self::DEFAULT_MODE;
  33 +//
  34 +// }
  35 +//
  36 +// $this->options = $options;
  37 +//
  38 +// try {
  39 +// $this->fileObject = new \SplFileObject($this->filePath, 'r');
  40 +// } catch (\ErrorException $e) {
  41 +// // Yii::warning("Ошибка открытия файла {$this->filePath}");
  42 +// echo "Ошибка открытия файла {$this->filePath}";
  43 +// return [];
  44 +// }
  45 +//
  46 +// $options['file'] = $this->fileObject;
  47 +// $this->extension = $this->fileObject->getExtension();
  48 +//
  49 +// try {
  50 +//
  51 +// $this->configuration = array_merge_recursive ($this->configuration, $options);
  52 +//
  53 +// } catch (\ErrorException $e) {
  54 +// echo $e->getMessage();
  55 +// return [];
  56 +// }
  57 +//
  58 +// }
  59 +//
  60 +// public function run()
  61 +// {
  62 +//
  63 +// $result = [];
  64 +//
  65 +// // \common\components\CustomVarDamp::dumpAndDie($this);
  66 +// if (count($this->configuration)) {
  67 +// $parser = \Yii::createObject($this->configuration);
  68 +//
  69 +// try {
  70 +//
  71 +// $parser->setup();
  72 +// $result = $parser->read();
  73 +//
  74 +// } catch (\ErrorException $e) {
  75 +//
  76 +// echo $e->getMessage();
  77 +//
  78 +// }
  79 +//
  80 +// }
  81 +//
  82 +// return $result;
  83 +// }
  84 + protected function createObjectByConfiguration($configuration)
23 85 {
24   - $this->filePath = $filePath;
25   - if (isset($options['mode'])) {
26   -
27   - $this->mode = $options['mode'];
28   - unset($options['mode']);
29   -
30   - } else {
31   -
32   - $this->mode = self::DEFAULT_MODE;
33   -
34   - }
35   -
36   - $this->options = $options;
37   -
38   - try {
39   - $this->fileObject = new \SplFileObject($this->filePath, 'r');
40   - } catch (\ErrorException $e) {
41   - // Yii::warning("Ошибка открытия файла {$this->filePath}");
42   - echo "Ошибка открытия файла {$this->filePath}";
43   - return [];
44   - }
45   -
46   - $options['file'] = $this->fileObject;
47   - $this->extension = $this->fileObject->getExtension();
48   -
49   - try {
50   - $this->configuration = \Yii::$app->multiparser->getConfiguration($this->extension, $this->mode);
51   - $this->configuration = array_merge_recursive ($this->configuration, $options);
52   - } catch (\ErrorException $e) {
53   - echo $e->getMessage();
54   - return [];
55   - }
56   -
  86 + return \Yii::createObject($configuration);
57 87 }
58 88  
59   - public function run()
60   - {
61   -
62   - $result = [];
63   -
64   - // \common\components\CustomVarDamp::dumpAndDie($this);
65   - if (count($this->configuration)) {
66   - $parser = \Yii::createObject($this->configuration);
67   -
68   - try {
69   -
70   - $parser->setup();
71   - $result = $parser->read();
72   -
73   - } catch (\ErrorException $e) {
74   -
75   - echo $e->getMessage();
76   -
77   - }
78   -
79   - }
80   -
81   - return $result;
82   - }
83 89  
84 90 }
85 91 \ No newline at end of file
... ...
lib/config.php 0 → 100644
  1 +<?php
  2 +return
  3 + [
  4 + 'csv' =>
  5 + ['web' =>
  6 + ['class' => 'artweb\yii_multiparser\CsvParser',
  7 + 'auto_detect_first_line' => true,
  8 + 'converter_conf' => [
  9 + "float" => 'PRICE',
  10 + "integer" => 'QUANTITY',
  11 + "string" => 'DESCR'
  12 + ]],
  13 + 'basic_column' => [
  14 + "ARTICLE" => 'Артикул',
  15 + "PRICE" => 'Цена',
  16 + "DESCR" => 'Наименование',
  17 + "QUANTITY" => 'Колво'
  18 +
  19 + ],
  20 + ]];
... ...