1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace TreeHouse\Feeder\Resource\Transformer; |
4
|
|
|
|
5
|
|
|
use TreeHouse\Feeder\Reader\ReaderInterface; |
6
|
|
|
use TreeHouse\Feeder\Resource\FileResource; |
7
|
|
|
use TreeHouse\Feeder\Resource\ResourceCollection; |
8
|
|
|
use TreeHouse\Feeder\Resource\ResourceInterface; |
9
|
|
|
use TreeHouse\Feeder\Transport\FileTransport; |
10
|
|
|
use TreeHouse\Feeder\Writer\WriterInterface; |
11
|
|
|
|
12
|
|
|
class MultiPartTransformer implements ResourceTransformerInterface |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* @var ReaderInterface |
16
|
|
|
*/ |
17
|
|
|
protected $reader; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var WriterInterface |
21
|
|
|
*/ |
22
|
|
|
protected $writer; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var int |
26
|
|
|
*/ |
27
|
|
|
protected $size; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var int|null |
31
|
|
|
*/ |
32
|
|
|
protected $maxParts; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @param ReaderInterface $reader |
36
|
|
|
* @param WriterInterface $writer |
37
|
|
|
* @param int $size |
38
|
|
|
* @param int $maxParts |
39
|
|
|
*/ |
40
|
|
|
public function __construct(ReaderInterface $reader, WriterInterface $writer, $size = 1000, $maxParts = null) |
41
|
|
|
{ |
42
|
|
|
$this->reader = $reader; |
43
|
|
|
$this->writer = $writer; |
44
|
|
|
$this->size = $size; |
45
|
|
|
$this->maxParts = $maxParts; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @inheritdoc |
50
|
|
|
*/ |
51
|
|
|
public function transform(ResourceInterface $resource, ResourceCollection $collection) |
52
|
|
|
{ |
53
|
|
|
// break up again |
54
|
|
|
$files = $this->breakup($resource); |
55
|
|
|
|
56
|
|
|
$resources = []; |
57
|
|
|
foreach ($files as $file) { |
58
|
|
|
$transport = FileTransport::create($file); |
59
|
|
|
$transport->setDestination($file); |
60
|
|
|
$resources[] = new FileResource($transport); |
61
|
|
|
|
62
|
|
|
if (($this->maxParts > 0) && (sizeof($resources) >= $this->maxParts)) { |
63
|
|
|
break; |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
if ($resources) { |
|
|
|
|
68
|
|
|
$collection->unshiftAll($resources); |
69
|
|
|
|
70
|
|
|
return $collection->shift(); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
return $resource; |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @inheritdoc |
78
|
|
|
*/ |
79
|
|
|
public function needsTransforming(ResourceInterface $resource) |
80
|
|
|
{ |
81
|
|
|
return !$this->isPartFile($resource); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @param ResourceInterface $resource |
86
|
|
|
* |
87
|
|
|
* @return int |
88
|
|
|
*/ |
89
|
|
|
protected function isPartFile(ResourceInterface $resource) |
90
|
|
|
{ |
91
|
|
|
return preg_match('/\.part(\d+)$/', $resource->getFile()->getBasename()); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @param ResourceInterface $resource |
96
|
|
|
* |
97
|
|
|
* @return array |
98
|
|
|
*/ |
99
|
|
|
protected function getPartFiles(ResourceInterface $resource) |
100
|
|
|
{ |
101
|
|
|
$files = []; |
102
|
|
|
|
103
|
|
|
$originalFile = $resource->getFile(); |
104
|
|
|
$regex = sprintf('/^%s\.part(\d+)$/', preg_quote($originalFile->getBasename(), '/')); |
105
|
|
|
$finder = new \DirectoryIterator($originalFile->getPath()); |
106
|
|
|
|
107
|
|
|
/** @var $file \SplFileInfo */ |
108
|
|
|
foreach ($finder as $file) { |
109
|
|
|
if ($file->isFile() && preg_match($regex, $file->getBaseName(), $matches)) { |
110
|
|
|
$files[(int) $matches[1]] = $file->getPathname(); |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
ksort($files); |
115
|
|
|
|
116
|
|
|
return $files; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @param ResourceInterface $resource |
121
|
|
|
* |
122
|
|
|
* @return array |
123
|
|
|
*/ |
124
|
|
|
protected function breakup(ResourceInterface $resource) |
125
|
|
|
{ |
126
|
|
|
$originalFile = $resource->getFile(); |
127
|
|
|
$baseFile = $originalFile->getPathname(); |
128
|
|
|
|
129
|
|
|
$this->reader->setResources(new ResourceCollection([$resource])); |
130
|
|
|
|
131
|
|
|
$partCount = 0; |
132
|
|
|
$started = false; |
133
|
|
|
while ($this->reader->valid()) { |
134
|
|
|
if ($this->reader->key() % $this->size === 0) { |
135
|
|
|
if ($this->reader->key() > 0) { |
136
|
|
|
$this->endPart(); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
$file = sprintf('%s.part%s', $baseFile, ++$partCount); |
140
|
|
|
$this->startPart($file); |
141
|
|
|
$started = true; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
$this->writer->write($this->reader->current()); |
145
|
|
|
$this->writer->flush(); |
146
|
|
|
|
147
|
|
|
$this->reader->next(); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
if ($started) { |
151
|
|
|
$this->endPart(); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
return $this->getPartFiles($resource); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @param string $file |
159
|
|
|
*/ |
160
|
|
|
protected function startPart($file) |
161
|
|
|
{ |
162
|
|
|
$this->writer = clone($this->writer); |
163
|
|
|
$this->writer->setFile(new \SplFileObject($file, 'w')); |
164
|
|
|
$this->writer->start(); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
*/ |
169
|
|
|
protected function endPart() |
170
|
|
|
{ |
171
|
|
|
$this->writer->end(); |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.