Passed
Push — master ( 9ea830...49221b )
by Petr
08:01
created

ProcessFile   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Test Coverage

Coverage 86.52%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 93
c 1
b 0
f 0
dl 0
loc 199
ccs 77
cts 89
cp 0.8652
rs 9.6
wmc 35

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A readFile() 0 18 5
A deleteFile() 0 8 2
A noDirectoryDelimiterSet() 0 3 1
A fullPath() 0 3 1
A copyFile() 0 9 2
A copyFileStream() 0 9 2
A saveFileStream() 0 24 5
B saveFile() 0 43 9
A moveFileStream() 0 5 2
A readFileStream() 0 12 3
A moveFile() 0 9 2
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
                    @ftruncate($handler, intval($offset));
0 ignored issues
show
Bug introduced by
It seems like $handler can also be of type false; however, parameter $stream of ftruncate() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

80
                    @ftruncate(/** @scrutinizer ignore-type */ $handler, intval($offset));
Loading history...
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

80
                    /** @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...
81 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

81
                    /** @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...
Bug introduced by
It seems like $handler can also be of type false; however, parameter $stream of fseek() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

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