MergeHandler::setManager()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
namespace Jobtech\LaravelChunky\Handlers;
4
5
use Illuminate\Container\Container;
0 ignored issues
show
Bug introduced by
The type Illuminate\Container\Container was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use Illuminate\Support\Collection;
7
use Illuminate\Support\Traits\ForwardsCalls;
8
use Jobtech\LaravelChunky\Chunk;
9
use Jobtech\LaravelChunky\Contracts\ChunkyManager;
10
use Jobtech\LaravelChunky\Contracts\MergeHandler as MergeHandlerContract;
11
use Jobtech\LaravelChunky\Events\ChunksMerged;
12
use Jobtech\LaravelChunky\Exceptions\ChunksIntegrityException;
13
use Jobtech\LaravelChunky\Exceptions\ChunkyException;
14
use Jobtech\LaravelChunky\Http\Requests\AddChunkRequest;
15
use Jobtech\LaravelChunky\Jobs\MergeChunks;
16
use Jobtech\LaravelChunky\Support\TempFilesystem;
17
use Keven\AppendStream\AppendStream;
18
19
/**
20
 * @method \Jobtech\LaravelChunky\ChunkySettings settings()
21
 * @method \Jobtech\LaravelChunky\Support\ChunksFilesystem chunksFilesystem()
22
 * @method \Jobtech\LaravelChunky\Support\MergeFilesystem mergeFilesystem()
23
 * @method array mergeOptions()
24
 * @method \Illuminate\Support\Collection listChunks(?string $folder = null)
25
 * @method resource|null readChunk($chunk)
26
 * @method bool deleteChunks(string $folder)
27
 * @method bool isValidChunksFolder(string $folder)
28
 * @method bool checkChunksIntegrity(string $folder, int $chunk_size, int $total_size)
29
 */
30
class MergeHandler implements MergeHandlerContract
31
{
32
    use ForwardsCalls;
33
34
    private ?ChunkyManager $manager;
35
36
    private TempFilesystem $temp_filesystem;
37
38
    public function __construct(?ChunkyManager $manager = null)
39
    {
40
        $this->manager = $manager;
41
        $this->temp_filesystem = Container::getInstance()->make(TempFilesystem::class);
42
    }
43
44
    /**
45
     * @param string $folder
46
     * @param string $target
47
     *
48
     * @throws \Illuminate\Contracts\Filesystem\FileExistsException|\Illuminate\Contracts\Filesystem\FileNotFoundException
49
     *
50
     * @return string
51
     */
52
    private function concatenate(string $folder, string $target): string
53
    {
54
        if (! $this->chunksFilesystem()->isLocal()) {
55
            return $this->temporaryConcatenate($target, $this->listChunks($folder));
56
        }
57
58
        $chunks = $this->listChunks($folder)->map(function (Chunk $item) {
59
            return $item->getPath();
60
        });
61
        $merge = $chunks->first();
62
63
        if (! $this->chunksFilesystem()->concatenate($merge, $chunks->toArray())) {
64
            throw new ChunkyException('Unable to concatenate chunks');
65
        }
66
67
        return $this->mergeFilesystem()
68
            ->store(
69
                $target,
70
                $this->readChunk($merge),
71
                $this->mergeOptions()
72
            );
73
    }
74
75
    /**
76
     * @param string $target
77
     * @param Collection $chunks
78
     * @return string
79
     * @throws \Illuminate\Contracts\Filesystem\FileExistsException
80
     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
81
     */
82
    private function temporaryConcatenate(string $target, Collection $chunks)
83
    {
84
        $stream = new AppendStream;
85
        $chunks->each(function (Chunk $chunk) use ($stream) {
86
            $path = $this->temp_filesystem->store('tmp-'.$chunk->getFilename(), $this->chunksFilesystem()->readChunk($chunk->getPath()));
87
            $stream->append($this->temp_filesystem->readFile($path));
88
        });
89
90
        $tmp_merge = $this->temp_filesystem->store($target, $stream->getResource());
91
        $path = $this->mergeFilesystem()->store($target, $this->temp_filesystem->readFile($tmp_merge), $this->mergeOptions());
92
93
        $this->temp_filesystem->clean();
94
95
        return $path;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     *
101
     * @codeCoverageIgnore
102
     */
103
    public function setManager(ChunkyManager $manager): MergeHandler
104
    {
105
        $this->manager = $manager;
106
107
        return $this;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     *
113
     * @codeCoverageIgnore
114
     */
115
    public function manager(): ChunkyManager
116
    {
117
        if ($this->manager === null) {
118
            $this->manager = Container::getInstance()->make('chunky');
119
        }
120
121
        return $this->manager;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->manager could return the type null which is incompatible with the type-hinted return Jobtech\LaravelChunky\Contracts\ChunkyManager. Consider adding an additional type-check to rule them out.
Loading history...
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     */
127
    public function dispatchMerge(AddChunkRequest $request, string $folder)
128
    {
129
        $merge_path = $request->fileInput()->getClientOriginalName();
130
        $chunk_size = $request->chunkSizeInput();
131
        $total_size = $request->totalSizeInput();
132
133
        if (empty($connection = $this->settings()->connection())) {
134
            if (! $this->checkChunksIntegrity($folder, $chunk_size, $total_size)) {
135
                throw new ChunksIntegrityException('Chunks total file size doesnt match with original file size');
136
            }
137
138
            return $this->merge($folder, $merge_path);
139
        }
140
141
        return MergeChunks::dispatch(
142
            $folder,
143
            $merge_path,
144
            $chunk_size,
145
            $total_size
146
        )->onConnection($connection)
147
            ->onQueue($this->settings()->queue());
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function merge(string $chunks_folder, string $merge_path): string
154
    {
155
        // Check chunks folder
156
        if (! $this->isValidChunksFolder($chunks_folder)) {
157
            throw new ChunkyException("Invalid chunks folder {$chunks_folder}");
158
        }
159
160
        /** @var resource $origin */
161
        $path = $this->concatenate($chunks_folder, $merge_path);
162
163
        // Final check and cleanup
164
        if (! $path) {
165
            throw new ChunkyException('An error occurred while moving merge to destination');
166
        }
167
168
        $this->deleteChunks($chunks_folder);
169
170
        event(new ChunksMerged($path));
0 ignored issues
show
Bug introduced by
The function event was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

170
        /** @scrutinizer ignore-call */ 
171
        event(new ChunksMerged($path));
Loading history...
171
172
        return $path;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178
    public static function instance(?ChunkyManager $manager = null): MergeHandlerContract
179
    {
180
        return new static($manager);
181
    }
182
183
    public function __call($method, $parameters)
184
    {
185
        if (! method_exists($this, $method)) {
186
            return $this->forwardCallTo($this->manager(), $method, $parameters);
187
        }
188
189
        return $this->{$method}(...$parameters);
190
    }
191
}
192