StreamHandler::recoursiveGlob()   B
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 36
Code Lines 20

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 36
rs 8.439
cc 5
eloc 20
nc 9
nop 2
1
<?php 
2
/**
3
 * Junty
4
 *
5
 * @author Gabriel Jacinto aka. GabrielJMJ <[email protected]>
6
 * @license MIT License
7
 */
8
9
namespace Junty\Stream;
10
11
use Psr\Http\Message\StreamInterface;
12
use GuzzleHttp\Psr7;
13
use Junty\Stream\Stream;
14
use Junty\Plugin\PluginInterface;
15
use Junty\ToDir\ToDirPlugin;
16
17
class StreamHandler
18
{
19
    private $globs = [];
20
21
    private $toPush = [];
22
23
    private $temp = [];
24
25
    private $streams = [];
26
27
    /**
28
     * Provides streams by the pattern passed
29
     *
30
     * @param string|array      $accept
31
     * @param string|array|null $exclude
32
     *
33
     * @return self
34
     */
35
    public function src($accept, $exclude = null) : self
36
    {
37
        if ((!is_string($accept) && !is_array($accept)) || ($exclude !== null && !is_string($exclude) && !is_array($exclude))) {
38
            throw new \InvalidArgumentException('You can only pass a string pattern or array with patterns');
39
        }
40
41
        if (is_array($accept)) {
42
            $fileGroups = [];
43
44
            foreach ($accept as $pattern) {
45
                $fileGroups[] = $this->recoursiveGlob($pattern, GLOB_ERR);
46
            }
47
48
            $globs = call_user_func_array('array_merge', $fileGroups);
49
        } else {
50
            $globs = $this->recoursiveGlob($accept, GLOB_ERR);
51
        }
52
53
        $this->globs = $this->ignoreFiles($globs, $exclude);
54
55
        foreach ($this->globs as $glob) {
56
            $this->streams[] = new Stream(fopen($glob, 'r+'));
57
        }
58
59
        return $this;
60
    }
61
62
    /**
63
     * Handle each stream
64
     *
65
     * @param callable|PluginInterface $callback
66
     *
67
     * @return self
68
     */
69
    public function forStream($callback) : self
70
    {
71
        $cb = \Closure::bind($this->getCallback($callback), $this);
72
73
        if (count($this->toPush)) {
74
            foreach ($this->toPush as &$stream) {
75
                $cb($stream);
76
            }
77
78
            return $this;
79
        }
80
81
        foreach ($this->streams as &$stream) {
82
            $cb($stream);
83
        }
84
85
        return $this;
86
    }
87
88
    /**
89
     * Handle all streams
90
     *
91
     * @param callable|PluginInterface $callback
92
     *
93
     * @return self
94
     */
95
    public function forStreams($callback) : self
96
    {
97
        $cb = \Closure::bind($this->getCallback($callback), $this);
98
99
        if (count($this->toPush)) {
100
            $cb($this->toPush);
101
102
            return $this;
103
        }
104
105
        $cb($this->streams);
106
107
        return $this;
108
    }
109
110
    /**
111
     * Pushes a stream to be used on destination
112
     *
113
     * @param StreamInterface $stream
114
     */
115
    public function push(StreamInterface $stream)
116
    {
117
        $this->toPush[] = $stream;
118
    }
119
120
    /**
121
     * Creates a temporary stream
122
     * It will be deleted in the end of task execution
123
     *
124
     * @param StreamInterface $stream
125
     */
126
    public function temp(StreamInterface $stream)
127
    {
128
        $this->push($stream);
129
        $this->temp[] = $stream->getMetadata('uri');
130
    }
131
132
    /**
133
     * Sends pushed streams to a directory (plugin)
134
     *
135
     * @param string $dest
136
     *
137
     * @return callable
138
     */
139
    public function toDir(string $dest) : ToDirPlugin
140
    {
141
        return new ToDirPlugin($dest);
142
    }
143
144
    /**
145
     * Cleans up pushed streams and delete indicated temporary files
146
     *
147
     * @return self
148
     */
149
    public function end() : self
150
    {
151
        $this->closeAll();
152
153
        foreach ($this->temp as $tempf) {
154
            unlink($tempf);
155
        }
156
157
        $this->toPush = [];
158
        return $this;
159
    }
160
161
    /**
162
     * Destructor closes all opened streams
163
     */
164
    public function __destruct()
165
    {
166
        $this->closeAll();
167
    }
168
169
    /**
170
     * Search for files inside folders and subfolders
171
     *
172
     * @param string  $pattern
173
     * @param integer $flags
174
     *
175
     * @return array
176
     */
177
    private function recoursiveGlob($pattern, $flags = 0) : array
178
    {
179
        $globs = glob($pattern, $flags);
180
        $hasDir = false;
181
182
        foreach ($globs as $glob) {
183
            if (is_dir($glob)) {
184
                $hasDir = true;
185
            }
186
        }
187
188
        if (!$hasDir) {
189
            return $globs;
190
        }
191
192
        foreach (
193
            glob(
194
                dirname($pattern) . DIRECTORY_SEPARATOR . '*',
195
                GLOB_ONLYDIR | GLOB_NOSORT
196
            )
197
            as $dir
198
        ) {
199
            $globs = array_merge(
200
                $globs,
201
                $this->recoursiveGlob(
202
                    $dir . DIRECTORY_SEPARATOR . basename($pattern), $flags
203
                )
204
            );
205
        }
206
207
        $globs = array_filter($globs, function ($res) {
208
            return !is_dir($res);
209
        });
210
211
        return $globs;
212
    }
213
214
    /**
215
     * Ignore files provided by glob function
216
     *
217
     * @param array             $files
218
     * @param array|string|null $patterns
219
     *
220
     * @return array
221
     */
222
    private function ignoreFiles(array $files, $patterns)
223
    {
224
        if (null !== $patterns) {
225
            $cbFilter = function () {return true;};
226
227
            if (is_array($patterns)) {
228
                $cbFilter = function ($glob) use ($patterns) {
229
                    foreach ($patterns as $pattern) {
230
                        if (preg_match($pattern, $glob)) {
231
                            return false;
232
                        }
233
                    }
234
235
                    return true;
236
                };
237
            } elseif (is_string($patterns)) {
238
                $cbFilter = function ($glob) use ($patterns) {
239
                    return !preg_match($patterns, $glob);
240
                };
241
            }
242
243
            $files = array_filter($files, $cbFilter);
244
        }
245
246
        return $files;
247
    }
248
249
    /**
250
     * Returns a plugin callback
251
     *
252
     * @param PluginInterface|callable $cb
253
     *
254
     * @return callable
255
     */
256
    private function getCallback($cb) : callable
257
    {
258
        if (!($cb instanceof PluginInterface) && !is_callable($cb)) {
259
            throw new \InvalidArgumentException('Invalid callback type: ' + gettype($cb));
260
        }
261
262
        if ($cb instanceof PluginInterface) {
263
            return $cb->getCallback();
264
        }
265
266
        return $cb;
267
    }
268
269
    /**
270
     * Closes all opened streams
271
     */
272
    private function closeAll()
273
    {
274
        foreach ($this->streams as $stream) {
275
            $stream->close();
276
        }
277
278
        foreach ($this->toPush as $stream) {
279
            $stream->close();
280
        }
281
    }
282
}