1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Gielfeldt\Iterators; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Create multiple atomic temp files. |
7
|
|
|
*/ |
8
|
|
|
class AtomicTempFileObjects |
9
|
|
|
{ |
10
|
|
|
protected $files = []; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Constructor. |
14
|
|
|
* |
15
|
|
|
* @param array $files |
16
|
|
|
* The files to use. |
17
|
|
|
*/ |
18
|
11 |
|
public function __construct($files = []) |
19
|
|
|
{ |
20
|
11 |
|
$this->files = $files; |
21
|
11 |
|
} |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Get opened files. |
25
|
|
|
*/ |
26
|
1 |
|
public function getFiles(): array |
27
|
|
|
{ |
28
|
1 |
|
return $this->files; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Get an already opened file. |
33
|
|
|
* |
34
|
|
|
* @param string $fileName |
35
|
|
|
* The name of the file to get. |
36
|
|
|
* |
37
|
|
|
* @return AtomicTempFileObject |
38
|
|
|
* The open file. |
39
|
|
|
*/ |
40
|
5 |
|
public function getFile($fileName): AtomicTempFileObject |
41
|
|
|
{ |
42
|
5 |
|
if (!$this->isFileOpen($fileName)) { |
43
|
1 |
|
throw new \RuntimeException("File: $fileName not opened!"); |
44
|
|
|
} |
45
|
4 |
|
return $this->files[$fileName]; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Check if file is opened. |
50
|
|
|
* |
51
|
|
|
* @param string $fileName |
52
|
|
|
* The name of the file to check. |
53
|
|
|
* |
54
|
|
|
* @return boolean |
55
|
|
|
* True if opened, false if not. |
56
|
|
|
*/ |
57
|
11 |
|
public function isFileOpen($fileName): bool |
58
|
|
|
{ |
59
|
11 |
|
return isset($this->files[$fileName]); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Open a new atomic temp file. |
64
|
|
|
* |
65
|
|
|
* @param string $fileName |
66
|
|
|
* The name of the file to open. |
67
|
|
|
* |
68
|
|
|
* @return AtomicTempFileObject |
69
|
|
|
* The file opened. |
70
|
|
|
*/ |
71
|
9 |
|
public function openFile($fileName): AtomicTempFileObject |
72
|
|
|
{ |
73
|
9 |
|
if ($this->isFileOpen($fileName)) { |
74
|
1 |
|
throw new \RuntimeException("File: $fileName already opened!"); |
75
|
|
|
} |
76
|
9 |
|
$this->files[$fileName] = new AtomicTempFileObject($fileName); |
77
|
9 |
|
return $this->files[$fileName]; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Add an already opened AtomicTempFileObject file. |
82
|
|
|
* |
83
|
|
|
* @param AtomicTempFileObject $file |
84
|
|
|
*/ |
85
|
2 |
|
public function addFile($file): AtomicTempFileObjects |
86
|
|
|
{ |
87
|
2 |
|
$realPath = $file->getDestinationRealPath(); |
88
|
2 |
|
if ($this->isFileOpen($realPath)) { |
89
|
1 |
|
throw new \RuntimeException("File: " . $realPath . " already opened!"); |
90
|
|
|
} |
91
|
2 |
|
$this->files[$realPath] = $file; |
92
|
2 |
|
return $this; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* Split a csv file into multiple csv files. |
97
|
|
|
* |
98
|
|
|
* @param Iterator $input |
99
|
|
|
* The input to split. Each row must contain an array of key value pairs. |
100
|
|
|
* @param callable $callback |
101
|
|
|
* A callback returning the filename for the specific row. |
102
|
|
|
* @return |
103
|
|
|
*/ |
104
|
2 |
|
public function splitCsvFile(\Iterator $input, callable $callback) |
105
|
|
|
{ |
106
|
2 |
|
$callback = \Closure::fromCallable($callback); |
107
|
2 |
|
$this->process( |
108
|
|
|
$input, |
109
|
|
|
function ($row, $rowNum, $input, $output) use ($callback) { |
110
|
2 |
|
if ($fileName = $callback($row)) { |
111
|
2 |
|
if (!$output->isFileOpen($fileName)) { |
112
|
2 |
|
$output->openFile($fileName)->fputcsv(array_keys($row)); |
113
|
|
|
} |
114
|
2 |
|
$output->getFile($fileName)->fputcsv(array_values($row)); |
115
|
|
|
} |
116
|
2 |
|
} |
117
|
|
|
); |
118
|
2 |
|
return $this; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Easy access iterator apply for processing an entire file. |
123
|
|
|
* |
124
|
|
|
* @param Iterator $input |
125
|
|
|
* The input to split. |
126
|
|
|
* @param callable $callback |
127
|
|
|
* Callback for each item in iterator. |
128
|
|
|
*/ |
129
|
3 |
View Code Duplication |
public function process(\Iterator $input, callable $callback) |
|
|
|
|
130
|
|
|
{ |
131
|
3 |
|
$callback = \Closure::fromCallable($callback); |
132
|
3 |
|
$input->rewind(); |
133
|
3 |
|
iterator_apply( |
134
|
|
|
$input, |
135
|
3 |
|
function (\Iterator $iterator) use ($callback) { |
136
|
3 |
|
$callback($iterator->current(), $iterator->key(), $iterator, $this); |
137
|
3 |
|
return true; |
138
|
3 |
|
}, |
139
|
3 |
|
[$input] |
140
|
|
|
); |
141
|
3 |
|
return $this; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Dispatch method calls to all attached AtomicTempFileObject. |
146
|
|
|
*/ |
147
|
11 |
|
public function __call($method, $arguments) |
148
|
|
|
{ |
149
|
11 |
|
foreach ($this->files as $file) { |
150
|
11 |
|
call_user_func_array([$file, $method], $arguments); |
151
|
|
|
} |
152
|
11 |
|
return $this; |
153
|
|
|
} |
154
|
|
|
} |
155
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.