|
1
|
|
|
<?php
|
|
2
|
|
|
/**
|
|
3
|
|
|
* @author Alexey Tatarinov <[email protected]>
|
|
4
|
|
|
* @link https://github.com/shogodev/argilla/
|
|
5
|
|
|
* @copyright Copyright © 2003-2015 Shogo
|
|
6
|
|
|
* @license http://argilla.ru/LICENSE
|
|
7
|
|
|
*/
|
|
8
|
|
|
Yii::import('frontend.share.formatters.SFormatter');
|
|
9
|
|
|
|
|
10
|
|
|
class ImportCsvReader
|
|
11
|
|
|
{
|
|
12
|
|
|
/**
|
|
13
|
|
|
* @var int $headerRowIndex - индекс заголовка (нуменация начинается с 1)
|
|
14
|
|
|
*/
|
|
15
|
|
|
public $headerRowIndex = 1;
|
|
16
|
|
|
|
|
17
|
|
|
/**
|
|
18
|
|
|
* @var int $skipTopRowAmount - количество попушенних строк с начала файла
|
|
19
|
|
|
*/
|
|
20
|
|
|
public $skipTopRowAmount = 1;
|
|
21
|
|
|
|
|
22
|
|
|
public $csvDelimiter = ',';
|
|
23
|
|
|
|
|
24
|
|
|
protected $header;
|
|
25
|
|
|
|
|
26
|
|
|
protected $currentFileName;
|
|
27
|
|
|
|
|
28
|
|
|
protected $currentRow;
|
|
29
|
|
|
|
|
30
|
|
|
/**
|
|
31
|
|
|
* @var ConsoleFileLogger
|
|
32
|
|
|
*/
|
|
33
|
|
|
private $logger;
|
|
34
|
|
|
|
|
35
|
|
|
/**
|
|
36
|
|
|
* @var AbstractAggregator
|
|
37
|
|
|
*/
|
|
38
|
|
|
private $importAggregator;
|
|
39
|
|
|
|
|
40
|
|
|
private $timeBegin;
|
|
41
|
|
|
|
|
42
|
|
|
private $currentFile;
|
|
43
|
|
|
|
|
44
|
|
|
/**
|
|
45
|
|
|
* @var FileBackup $fileBackup
|
|
46
|
|
|
*/
|
|
47
|
|
|
private $fileBackup;
|
|
48
|
|
|
|
|
49
|
|
|
public function __construct(ConsoleFileLogger $logger, AbstractAggregator $importAggregator)
|
|
50
|
|
|
{
|
|
51
|
|
|
$this->logger = $logger;
|
|
52
|
|
|
$this->importAggregator = $importAggregator;
|
|
53
|
|
|
$this->basePath = realpath(Yii::getPathOfAlias('frontend').'/..');
|
|
|
|
|
|
|
54
|
|
|
}
|
|
55
|
|
|
|
|
56
|
|
|
public function start()
|
|
57
|
|
|
{
|
|
58
|
|
|
$this->timeBegin = microtime(true);
|
|
59
|
|
|
$this->logger->log('Начало импорта');
|
|
60
|
|
|
}
|
|
61
|
|
|
|
|
62
|
|
|
public function processFiles($files = array())
|
|
63
|
|
|
{
|
|
64
|
|
|
$counter = 1;
|
|
65
|
|
|
foreach($files as $file)
|
|
66
|
|
|
{
|
|
67
|
|
|
$this->logger->log('Обработка файла '.$file.' '.($counter++).'/'.count($files));
|
|
68
|
|
|
|
|
69
|
|
|
try
|
|
70
|
|
|
{
|
|
71
|
|
|
$this->processFile($file);
|
|
72
|
|
|
}
|
|
73
|
|
|
catch(WarningException $e)
|
|
74
|
|
|
{
|
|
75
|
|
|
$this->logger->warning($e->getMessage());
|
|
76
|
|
|
}
|
|
77
|
|
|
}
|
|
78
|
|
|
}
|
|
79
|
|
|
|
|
80
|
|
|
public function finish()
|
|
81
|
|
|
{
|
|
82
|
|
|
$time = microtime(true) - $this->timeBegin;
|
|
83
|
|
|
$this->logger->log('Импорт завершен. Время выполнения '.sprintf("%.1f", $time).' с.');
|
|
84
|
|
|
}
|
|
85
|
|
|
|
|
86
|
|
|
public function setBackupConfig($newPath)
|
|
87
|
|
|
{
|
|
88
|
|
|
$this->fileBackup = new FileBackup(ImportHelper::wrapInSlashEnd($newPath));
|
|
89
|
|
|
}
|
|
90
|
|
|
|
|
91
|
|
|
protected function processFile($file)
|
|
92
|
|
|
{
|
|
93
|
|
|
if( !($handle = @fopen($file, 'r')) )
|
|
94
|
|
|
throw new WarningException('Не удальсь открыть файл '.$file);
|
|
95
|
|
|
|
|
96
|
|
|
$this->currentFile = $file;
|
|
97
|
|
|
|
|
98
|
|
|
$this->currentFileName = basename($file);
|
|
99
|
|
|
$progress = new ConsoleProgressBar($this->countFileLines($file));
|
|
100
|
|
|
$this->processData($handle, $progress);
|
|
101
|
|
|
}
|
|
102
|
|
|
|
|
103
|
|
|
protected function processData($handle, ConsoleProgressBar $progress)
|
|
104
|
|
|
{
|
|
105
|
|
|
$this->currentRow = 0;
|
|
106
|
|
|
|
|
107
|
|
|
$this->importAggregator->init();
|
|
108
|
|
|
$progress->start();
|
|
109
|
|
|
while(($item = fgetcsv($handle, null, $this->csvDelimiter)) !== false)
|
|
110
|
|
|
{
|
|
111
|
|
|
$this->currentRow++;
|
|
112
|
|
|
$progress->setValueMap('memory', Yii::app()->format->formatSize(memory_get_usage()));
|
|
113
|
|
|
$progress->advance();
|
|
114
|
|
|
|
|
115
|
|
|
if( !is_null($this->headerRowIndex) && $this->currentRow == $this->headerRowIndex )
|
|
116
|
|
|
{
|
|
117
|
|
|
$this->header = $this->formatItem($item);
|
|
118
|
|
|
$this->importAggregator->setHeader($this->header);
|
|
119
|
|
|
}
|
|
120
|
|
|
|
|
121
|
|
|
if( $this->currentRow <= $this->skipTopRowAmount )
|
|
122
|
|
|
continue;
|
|
123
|
|
|
|
|
124
|
|
|
$this->importAggregator->collect($this->formatItem($item), $this->currentRow, $this->currentFileName);
|
|
125
|
|
|
}
|
|
126
|
|
|
$progress->finish();
|
|
127
|
|
|
|
|
128
|
|
|
$this->onEndReadFile($handle);
|
|
129
|
|
|
|
|
130
|
|
|
$this->importAggregator->end();
|
|
131
|
|
|
}
|
|
132
|
|
|
|
|
133
|
|
|
protected function formatItem($item)
|
|
134
|
|
|
{
|
|
135
|
|
|
foreach($item as $key => $value)
|
|
136
|
|
|
{
|
|
137
|
|
|
$item[$key] = trim($value, '"\' ');
|
|
138
|
|
|
}
|
|
139
|
|
|
|
|
140
|
|
|
return $item;
|
|
141
|
|
|
}
|
|
142
|
|
|
|
|
143
|
|
|
protected function countFileLines($file)
|
|
144
|
|
|
{
|
|
145
|
|
|
//todo: Сделать альтернативу для windows
|
|
146
|
|
|
|
|
147
|
|
|
$file = realpath($this->basePath.'/'.$file);
|
|
148
|
|
|
$result = trim(exec("wc -l '$file'"));
|
|
149
|
|
|
|
|
150
|
|
|
return intval(substr($result, 0, strpos($result, ' ')));
|
|
151
|
|
|
}
|
|
152
|
|
|
|
|
153
|
|
|
protected function onEndReadFile($handle)
|
|
154
|
|
|
{
|
|
155
|
|
|
if( $handle )
|
|
156
|
|
|
fclose($handle);
|
|
157
|
|
|
|
|
158
|
|
|
if( $this->fileBackup )
|
|
159
|
|
|
$this->fileBackup->makeBackup($this->currentFile);
|
|
160
|
|
|
}
|
|
161
|
|
|
} |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: