ProcessFile   A
last analyzed

Complexity

Total Complexity 37

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Test Coverage

Coverage 98.61%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 96
dl 0
loc 205
ccs 71
cts 72
cp 0.9861
rs 9.44
c 2
b 0
f 0
wmc 37

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A fullPath() 0 3 1
A moveFileStream() 0 5 2
A readFile() 0 18 5
A deleteFile() 0 8 2
A noDirectoryDelimiterSet() 0 3 1
A copyFile() 0 9 2
A copyFileStream() 0 9 2
A saveFileStream() 0 24 5
B saveFile() 0 49 11
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
    }
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
            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
            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 (@is_file($path)) {
79 2
                    $handler = @fopen($path, 'ab+');
80 2
                    if (false === $handler) {
81
                        // @codeCoverageIgnoreStart
82
                        throw new FilesException($this->getFlLang()->flCannotOpenFile($path));
83
                    }
84 2
                    if (!is_null($offset)) {
85 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

85
                        /** @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...
86 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

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