Passed
Push — master ( 49221b...48dfef )
by Petr
08:52 queued 06:24
created

ProcessFile::copyFileStream()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 9
ccs 4
cts 5
cp 0.8
crap 2.032
rs 10
1
<?php
2
3
namespace kalanis\kw_files\Processing\Volume;
4
5
6
use kalanis\kw_files\FilesException;
7
use kalanis\kw_files\Interfaces;
8
use kalanis\kw_files\Processing\TPath;
9
use kalanis\kw_files\Traits\TCheckModes;
10
use kalanis\kw_files\Traits\TLang;
11
use kalanis\kw_paths\Extras\TPathTransform;
12
use kalanis\kw_paths\PathsException;
13
use Throwable;
14
15
16
/**
17
 * Class ProcessFile
18
 * @package kalanis\kw_files\Processing\Volume
19
 * Process files in many ways
20
 */
21
class ProcessFile implements Interfaces\IProcessFiles, Interfaces\IProcessFileStreams
22
{
23
    use TCheckModes;
24
    use TLang;
25
    use TPath;
26
    use TPathTransform;
27
28 20
    public function __construct(string $path = '', ?Interfaces\IFLTranslations $lang = null)
29
    {
30 20
        $this->setPath($path);
31 20
        $this->setFlLang($lang);
32 20
    }
33
34 5
    public function readFile(array $entry, ?int $offset = null, ?int $length = null): string
35
    {
36 5
        $path = $this->fullPath($entry);
37
        try {
38 5
            if (!is_null($length)) {
39 1
                $content = @file_get_contents($path, false, null, intval($offset), $length);
40 5
            } elseif (!is_null($offset)) {
41 1
                $content = @file_get_contents($path, false, null, $offset);
42
            } else {
43 5
                $content = @file_get_contents($path);
44
            }
45 5
            if (false !== $content) {
46 4
                return strval($content);
47
            }
48 1
            throw new FilesException($this->getFlLang()->flCannotLoadFile($path));
49 1
        } catch (Throwable $ex) {
50
            // @codeCoverageIgnoreStart
51 1
            throw new FilesException($ex->getMessage(), $ex->getCode(), $ex);
52
        }
53
        // @codeCoverageIgnoreEnd
54
    }
55
56 3
    public function readFileStream(array $entry)
57
    {
58 3
        $path = $this->fullPath($entry);
59
        try {
60 3
            $handle = @fopen($path, 'rb');
61 3
            if (false !== $handle) {
62 2
                return $handle;
63
            }
64 1
            throw new FilesException($this->getFlLang()->flCannotLoadFile($path));
65 1
        } catch (Throwable $ex) {
66
            // @codeCoverageIgnoreStart
67 1
            throw new FilesException($ex->getMessage(), $ex->getCode(), $ex);
68
        }
69
        // @codeCoverageIgnoreEnd
70
    }
71
72 6
    public function saveFile(array $entry, string $content, ?int $offset = null, int $mode = 0): bool
73
    {
74 6
        $this->checkSupportedModes($mode);
75 5
        $path = $this->fullPath($entry);
76
        try {
77 5
            if (FILE_APPEND == $mode) {
78 2
                if (file_exists($path)) {
79 2
                    $handler = @fopen($path, 'rb+');
80 2
                    if (false === $handler) {
81
                        // @codeCoverageIgnoreStart
82
                        throw new FilesException($this->getFlLang()->flCannotOpenFile($path));
83
                    }
84 2
                    @ftruncate($handler, intval($offset));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ftruncate(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

84
                    /** @scrutinizer ignore-unhandled */ @ftruncate($handler, intval($offset));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
85 2
                    @fseek($handler, intval($offset));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fseek(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

85
                    /** @scrutinizer ignore-unhandled */ @fseek($handler, intval($offset));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
86
                } else {
87 2
                    $handler = @fopen($path, 'ab');
88
                }
89
            } else {
90 4
                $handler = @fopen($path, 'wb');
91
            }
92 5
            if (false === $handler) {
93
                // @codeCoverageIgnoreStart
94 1
                throw new FilesException($this->getFlLang()->flCannotOpenFile($path));
95
            }
96
            // @codeCoverageIgnoreEnd
97 5
            if (!is_null($offset)) {
98 3
                if (0 != @fseek($handler, $offset)) {
99
                    // @codeCoverageIgnoreStart
100 3
                    throw new FilesException($this->getFlLang()->flCannotSeekFile($path));
101
                }
102
                // @codeCoverageIgnoreEnd
103
            } else {
104 3
                if (!@rewind($handler)) {
105
                    // @codeCoverageIgnoreStart
106
                    throw new FilesException($this->getFlLang()->flCannotSeekFile($path));
107
                }
108
                // @codeCoverageIgnoreEnd
109
            }
110 5
            if (false === @fwrite($handler, $content)) {
111
                // @codeCoverageIgnoreStart
112
                throw new FilesException($this->getFlLang()->flCannotWriteFile($path));
113
            }
114
            // @codeCoverageIgnoreEnd
115 5
            return @fclose($handler);
116 1
        } catch (Throwable $ex) {
117
            // @codeCoverageIgnoreStart
118 1
            throw new FilesException($this->getFlLang()->flCannotSaveFile($path), $ex->getCode(), $ex);
119
        }
120
        // @codeCoverageIgnoreEnd
121
    }
122
123 5
    public function saveFileStream(array $entry, $content, int $mode = 0): bool
124
    {
125 5
        $this->checkSupportedModes($mode);
126 4
        $path = $this->fullPath($entry);
127
        try {
128 4
            if (FILE_APPEND == $mode) { // append to the end
129 1
                $handler = @fopen($path, 'ab');
130
            } else { // rewrite all from offset
131 3
                $handler = @fopen($path, 'wb');
132
            }
133 4
            if (false === $handler) {
134
                // @codeCoverageIgnoreStart
135 1
                throw new FilesException($this->getFlLang()->flCannotOpenFile($path));
136
            }
137
            // @codeCoverageIgnoreEnd
138 4
            if (false === @stream_copy_to_stream($content, $handler)) {
139
                // @codeCoverageIgnoreStart
140
                throw new FilesException($this->getFlLang()->flCannotWriteFile($path));
141
            }
142
            // @codeCoverageIgnoreEnd
143 4
            return @fclose($handler);
144 1
        } catch (Throwable $ex) {
145
            // @codeCoverageIgnoreStart
146 1
            throw new FilesException($this->getFlLang()->flCannotSaveFile($path), $ex->getCode(), $ex);
147
        }
148
        // @codeCoverageIgnoreEnd
149
    }
150
151 1
    public function copyFile(array $source, array $dest): bool
152
    {
153 1
        $src = $this->fullPath($source);
154 1
        $dst = $this->fullPath($dest);
155
        try {
156 1
            return @copy($src, $dst);
157
            // @codeCoverageIgnoreStart
158
        } catch (Throwable $ex) {
159
            throw new FilesException($this->getFlLang()->flCannotCopyFile($src, $dst), $ex->getCode(), $ex);
160
        }
161
        // @codeCoverageIgnoreEnd
162
    }
163
164 1
    public function copyFileStream(array $source, array $dest): bool
165
    {
166 1
        $stream = $this->readFileStream($source);
167 1
        if (!@rewind($stream)) {
168
            // @codeCoverageIgnoreStart
169
            throw new FilesException($this->getFlLang()->flCannotSeekFile($this->fullPath($source)));
170
        }
171
        // @codeCoverageIgnoreEnd
172 1
        return $this->saveFileStream($dest, $stream);
173
    }
174
175 1
    public function moveFile(array $source, array $dest): bool
176
    {
177 1
        $src = $this->fullPath($source);
178 1
        $dst = $this->fullPath($dest);
179
        try {
180 1
            return @rename($src, $dst);
181
            // @codeCoverageIgnoreStart
182
        } catch (Throwable $ex) {
183
            throw new FilesException($this->getFlLang()->flCannotMoveFile($src, $dst), $ex->getCode(), $ex);
184
        }
185
        // @codeCoverageIgnoreEnd
186
    }
187
188 1
    public function moveFileStream(array $source, array $dest): bool
189
    {
190 1
        $r1 = $this->copyFileStream($source, $dest);
191 1
        $r2 = $this->deleteFile($source);
192 1
        return $r1 && $r2;
193
    }
194
195 2
    public function deleteFile(array $entry): bool
196
    {
197 2
        $path = $this->fullPath($entry);
198
        try {
199 2
            return @unlink($path);
200
            // @codeCoverageIgnoreStart
201
        } catch (Throwable $ex) {
202
            throw new FilesException($this->getFlLang()->flCannotRemoveFile($path), $ex->getCode(), $ex);
203
        }
204
        // @codeCoverageIgnoreEnd
205
    }
206
207
    /**
208
     * @param array<string> $path
209
     * @throws PathsException
210
     * @return string
211
     */
212 14
    protected function fullPath(array $path): string
213
    {
214 14
        return $this->getPath() . DIRECTORY_SEPARATOR . $this->compactName($path);
215
    }
216
217
    /**
218
     * @return string
219
     * @codeCoverageIgnore only when path fails
220
     */
221
    protected function noDirectoryDelimiterSet(): string
222
    {
223
        return $this->getFlLang()->flNoDirectoryDelimiterSet();
224
    }
225
}
226