Lines 83.92% 47 / 56
Methods 75.00% 12 / 16
Classes 0.00% 0 / 1
Covered by tests of size
Name Lines Methods CRAP
 putChunk 100.00% 1 / 1 100.00% 1 / 1 1
 copyTo 33.33% 3 / 9 0.00% 0 / 1 12.41
 getChunk 100.00% 2 / 2 100.00% 1 / 1 2
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] __construct 100.00% 4 / 4 100.00% 1 / 1 2
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] __destruct 100.00% 2 / 2 100.00% 1 / 1 2
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] eof 100.00% 1 / 1 100.00% 1 / 1 1
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] delete 83.33% 5 / 6 0.00% 0 / 1 5.12
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] getMode 100.00% 1 / 1 100.00% 1 / 1 1
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] setMode 100.00% 6 / 6 100.00% 1 / 1 2
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] copyToSimple 66.66% 2 / 3 0.00% 0 / 1 2.15
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] isOk 100.00% 1 / 1 100.00% 1 / 1 1
 [phpMyFAQ\Attachment\Filesystem\AbstractFile] deleteDir 93.75% 15 / 16 0.00% 0 / 1 9.02
 [phpMyFAQ\Attachment\Filesystem\AbstractEntry] moveTo 100.00% 1 / 1 100.00% 1 / 1 2
 [phpMyFAQ\Attachment\Filesystem\AbstractEntry] isEncrypted 100.00% 1 / 1 100.00% 1 / 1 1
 [phpMyFAQ\Attachment\Filesystem\AbstractEntry] getPath 100.00% 1 / 1 100.00% 1 / 1 1
 [phpMyFAQ\Attachment\Filesystem\AbstractEntry] __toString 100.00% 1 / 1 100.00% 1 / 1 1
29class VanillaFile extends AbstractFile
30{
31    /**
32     * Chunk size read/write operations will deal with (in bytes).
33     */
34    private const int CHUNK_SIZE = 512;
35
36    /**
37     * @inheritdoc
38     */
39    public function putChunk(string $chunk): int|bool
40    {
41        return fwrite($this->handle, $chunk);
42    }
43
44    /**
45     * @inheritdoc
46     */
47    public function copyTo(object|string $entry): bool
48    {
49        $doSimple = is_string($entry) || $entry instanceof self;
50        if ($doSimple) {
51            // If the target is a string or vanilla object, just move
52            // it the simplest way we can.
53            return $this->copyToSimple((string) $entry);
54        }
55
56        if (!$entry instanceof AbstractFile) {
57            return false;
58        }
59
60        $entry->setMode(self::MODE_WRITE);
61        while (!$this->eof()) {
62            $entry->putChunk($this->getChunk());
63        }
64
65        return true;
66    }
67
68    /**
69     * @inheritdoc
70     */
71    public function getChunk(): string
72    {
73        $chunk = fread($this->handle, self::CHUNK_SIZE);
74
75        return $chunk === false ? '' : $chunk;
76    }
77}

Inherited from phpMyFAQ\Attachment\Filesystem\AbstractFile

51    public function __construct(
52        string $filepath,
53        protected string $mode = self::MODE_READ,
54    ) {
55        $this->path = $filepath;
56
57        $this->handle = fopen($this->path, $this->mode);
58
59        if (!is_resource($this->handle)) {
60            throw new FileException('Could not open file: ' . $this->path);
61        }
62    }
67    public function __destruct()
68    {
69        if (is_resource($this->handle)) {
70            fclose($this->handle);
71        }
72    }
77    public function eof(): bool
78    {
79        return feof($this->handle);
80    }
100    public function delete(): bool
101    {
102        if ($this->handle) {
103            fclose($this->handle);
104        }
105
106        // Read the uploaded temp path directly from $_FILES instead of
107        // rebuilding a Symfony Request. Request::createFromGlobals() eagerly
108        // constructs an UploadedFile with path validation, which throws a
109        // FileNotFoundException here because move_uploaded_file() has already
110        // moved the temp file away during the preceding copyTo() call.
111        $uploadedTmpName = $_FILES['userfile']['tmp_name'] ?? null;
112        if (is_string($uploadedTmpName) && $this->path !== $uploadedTmpName && file_exists($this->path)) {
113            return $this->deleteDir(dirname($this->path));
114        }
115
116        return true;
117    }
122    public function getMode(): string
123    {
124        return $this->mode;
125    }
132    public function setMode($mode): bool
133    {
134        $retval = false;
135
136        if (in_array($mode, [self::MODE_WRITE, self::MODE_READ, self::MODE_APPEND], strict: true)) {
137            fclose($this->handle);
138            $this->handle = fopen($this->path, $mode);
139
140            $retval = is_resource($this->handle);
141        }
142
143        return $retval;
144    }
151    public function copyToSimple($target): bool
152    {
153        if (is_uploaded_file($this->path)) {
154            return move_uploaded_file($this->path, $target);
155        }
156
157        return copy($this->path, $target);
158    }
163    public function isOk(): bool
164    {
165        return is_resource($this->handle);
166    }
175    public function deleteDir($path): bool
176    {
177        if (!file_exists($path)) {
178            throw new FileException(sprintf("Directory %s doesn't exist.", $path));
179        }
180
181        $it = new RecursiveIteratorIterator(
182            new RecursiveDirectoryIterator($path),
183            RecursiveIteratorIterator::CHILD_FIRST,
184        );
185
186        foreach ($it as $file) {
187            if (!$file instanceof \SplFileInfo || in_array($file->getBasename(), ['.', '..'], strict: true)) {
188                continue;
189            }
190
191            if ($file->isDir()) {
192                rmdir($file->getPathname());
193            }
194
195            if ($file->isFile() || $file->isLink()) {
196                if (!is_writable($file->getPathname())) {
197                    throw new FileException("File can't be deleted.");
198                }
199
200                unlink($file->getPathname());
201            }
202        }
203
204        return rmdir($path);
205    }

Inherited from phpMyFAQ\Attachment\Filesystem\AbstractEntry

46    public function moveTo(object|string $entry): bool
47    {
48        return $this->copyTo($entry) && $this->delete();
49    }
66    public function isEncrypted(): bool
67    {
68        return false;
69    }
74    public function getPath(): string
75    {
76        return $this->path;
77    }
82    public function __toString(): string
83    {
84        return $this->path;
85    }