Completed
Push — master ( d53a17...aa33b2 )
by Gabriel
02:05
created

StreamHandler::recoursiveGlob()   B

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
    /**
26
     * Provides streams by the pattern passed
27
     *
28
     * @param string|array      $accept
29
     * @param string|array|null $exclude
30
     *
31
     * @return self
32
     */
33
    public function src($accept, $exclude = null) : self
34
    {
35
        if ((!is_string($accept) && !is_array($accept)) || ($exclude !== null && !is_string($exclude) && !is_array($exclude))) {
36
            throw new \InvalidArgumentException('You can only pass a string pattern or array with patterns');
37
        }
38
39
        if (is_array($accept)) {
40
            $fileGroups = [];
41
42
            foreach ($accept as $pattern) {
43
                $fileGroups[] = $this->recoursiveGlob($pattern, GLOB_ERR);
44
            }
45
46
            $globs = call_user_func_array('array_merge', $fileGroups);
47
        } else {
48
            $globs = $this->recoursiveGlob($accept, GLOB_ERR);
49
        }
50
51
        $this->globs = $this->ignoreFiles($globs, $exclude);
0 ignored issues
show
Bug introduced by
It seems like $exclude defined by parameter $exclude on line 33 can also be of type null; however, Junty\Stream\StreamHandler::ignoreFiles() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
52
53
        return $this;
54
    }
55
56
    /**
57
     * Handle each stream
58
     *
59
     * @param callable|PluginInterface $callback
60
     *
61
     * @return self
62
     */
63
    public function forStream($callback) : self
64
    {
65
        $cb = \Closure::bind($this->getCallback($callback), $this);
66
67
        if (count($this->toPush)) {
68
            foreach ($this->toPush as $stream) {
69
                $cb($stream);
70
            }
71
72
            return $this;
73
        }
74
75
        $streams = array_map(function ($file) {
76
            return new Stream(fopen($file, 'r+'));
77
        }, $this->globs);
78
79
        foreach ($streams as $stream) {
80
            $cb($stream);
81
        }
82
83
        return $this;
84
    }
85
86
    /**
87
     * Handle all streams
88
     *
89
     * @param callable|PluginInterface $callback
90
     *
91
     * @return self
92
     */
93
    public function forStreams($callback) : self
94
    {
95
        $cb = \Closure::bind($this->getCallback($callback), $this);
96
97
        if (count($this->toPush)) {
98
            $cb($this->toPush);
99
100
            return $this;
101
        }
102
103
        $streams = array_map(function ($file) {
104
            return new Stream(fopen($file, 'r+'));
105
        }, $this->globs);
106
107
        $cb($streams);
108
109
        return $this;
110
    }
111
112
    private function getCallback($cb) : callable
113
    {
114
        if (!($cb instanceof PluginInterface) && !is_callable($cb)) {
115
            throw new \InvalidArgumentException('Invalid callback type: ' + gettype($cb));
116
        }
117
118
        if ($cb instanceof PluginInterface) {
119
            return $cb->getCallback();
120
        }
121
122
        return $cb;
123
    }
124
125
    /**
126
     * Pushes a stream to be used on destination
127
     *
128
     * @param StreamInterface $stream
129
     */
130
    public function push(StreamInterface $stream)
131
    {
132
        $this->toPush[] = $stream;
133
    }
134
135
    /**
136
     * Creates a temporary stream
137
     * It will be deleted in the end of task execution
138
     *
139
     * @param StreamInterface $stream
140
     */
141
    public function temp(StreamInterface $stream)
142
    {
143
        $this->push($stream);
144
        $this->temp[] = $stream->getMetadata('uri');
145
    }
146
147
    /**
148
     * Sends pushed streams to a directory (plugin)
149
     *
150
     * @param string $dest
151
     *
152
     * @return callable
153
     */
154
    public function toDir(string $dest) : ToDirPlugin
155
    {
156
        return new ToDirPlugin($dest);
157
    }
158
159
    /**
160
     * Cleans up pushed streams and delete indicated temporary files
161
     *
162
     * @return self
163
     */
164
    public function end() : self
165
    {
166
        foreach ($this->toPush as $stream) {
167
            $stream->close();
168
        }
169
170
        foreach ($this->temp as $tempf) {
171
            unlink($tempf);
172
        }
173
174
        $this->toPush = [];
175
        return $this;
176
    }
177
178
    private function recoursiveGlob($pattern, $flags = 0) : array
179
    {
180
        $globs = glob($pattern, $flags);
181
        $hasDir = false;
182
183
        foreach ($globs as $glob) {
184
            if (is_dir($glob)) {
185
                $hasDir = true;
186
            }
187
        }
188
189
        if (!$hasDir) {
190
            return $globs;
191
        }
192
193
        foreach (
194
            glob(
195
                dirname($pattern) . DIRECTORY_SEPARATOR . '*',
196
                GLOB_ONLYDIR | GLOB_NOSORT
197
            )
198
            as $dir
199
        ) {
200
            $globs = array_merge(
201
                $globs,
202
                $this->recoursiveGlob(
203
                    $dir . DIRECTORY_SEPARATOR . basename($pattern), $flags
204
                )
205
            );
206
        }
207
208
        $globs = array_filter($globs, function ($res) {
209
            return !is_dir($res);
210
        });
211
212
        return $globs;
213
    }
214
215
    /**
216
     * Ignore files provided by glob function
217
     *
218
     * @param array        $files
219
     * @param array|string $patterns
220
     *
221
     * @return array
222
     */
223
    private function ignoreFiles(array $files, $patterns)
224
    {
225
        if ($patterns !== null) {
226
            $cbFilter = function () {return true;};
227
228
            if (is_array($patterns) && count($patterns)) {
229
                $cbFilter = function ($glob) use ($patterns) {
230
                    foreach ($patterns as $pattern) {
231
                        if (preg_match($pattern, $glob)) {
232
                            return false;
233
                        }
234
                    }
235
236
                    return true;
237
                };
238
            } elseif (is_string($patterns)) {
239
                $cbFilter = function ($glob) use ($patterns) {
240
                    return !preg_match($patterns, $glob);
241
                };
242
            }
243
244
            $files = array_filter($files, $cbFilter);
245
        }
246
247
        return $files;
248
    }
249
}