• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

webeweb / edm-bundle / 4872211109

pending completion
4872211109

push

github

webeweb
Improve PHPDoc

723 of 735 relevant lines covered (98.37%)

10.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.26
/Provider/Storage/FilesystemStorageProvider.php
1
<?php
2

3
/*
4
 * This file is part of the edm-bundle package.
5
 *
6
 * (c) 2017 WEBEWEB
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
namespace WBW\Bundle\EDMBundle\Provider\Storage;
13

14
use DateTime;
15
use Psr\Log\LoggerInterface;
16
use ReflectionClass;
17
use ReflectionException;
18
use Symfony\Component\Filesystem\Filesystem;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\HttpFoundation\StreamedResponse;
21
use Throwable;
22
use WBW\Bundle\EDMBundle\Entity\Document;
23
use WBW\Bundle\EDMBundle\Helper\DocumentHelper;
24
use WBW\Bundle\EDMBundle\Model\DocumentInterface;
25
use WBW\Bundle\EDMBundle\Provider\StorageProviderInterface;
26
use WBW\Library\Logger\LoggerTrait;
27
use WBW\Library\Traits\Strings\StringDirectoryTrait;
28
use ZipArchive;
29

30
/**
31
 * Filesystem storage provider.
32
 *
33
 * @author webeweb <https://github.com/webeweb>
34
 * @package WBW\Bundle\EDMBundle\Provider\Storage
35
 */
36
class FilesystemStorageProvider implements StorageProviderInterface {
37

38
    use LoggerTrait;
39
    use StringDirectoryTrait;
40

41
    /**
42
     * Service name.
43
     *
44
     * @var string
45
     */
46
    const SERVICE_NAME = "wbw.edm.provider.storage.filesystem";
47

48
    /**
49
     * Constructor.
50
     *
51
     * @param LoggerInterface $logger The logger.
52
     * @param string $directory The directory.
53
     */
54
    public function __construct(LoggerInterface $logger, string $directory) {
55
        $logger->debug(sprintf('Filesystem storage provider use this directory "%s"', $directory));
38✔
56
        $this->setDirectory($directory);
38✔
57
        $this->setLogger($logger);
38✔
58
    }
59

60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function deleteDirectory(DocumentInterface $directory): void {
64

65
        DocumentHelper::isDirectory($directory);
6✔
66

67
        $pathname = $this->getAbsolutePath($directory);
4✔
68
        $context  = [
4✔
69
            "_provider" => get_class($this),
4✔
70
        ];
4✔
71

72
        $this->logInfo(sprintf('Filesystem storage provider tries to delete the directory "%s"', $pathname), $context);
4✔
73

74
        $filesystem = new Filesystem();
4✔
75
        $filesystem->remove($pathname);
4✔
76
    }
77

78
    /**
79
     * {@inheritdoc}
80
     */
81
    public function deleteDocument(DocumentInterface $document): void {
82

83
        DocumentHelper::isDocument($document);
4✔
84

85
        $pathname = $this->getAbsolutePath($document);
2✔
86
        $context  = [
2✔
87
            "_provider" => get_class($this),
2✔
88
        ];
2✔
89

90
        $this->logInfo(sprintf('Filesystem storage provider tries to delete the document "%s"', $pathname), $context);
2✔
91

92
        $filesystem = new Filesystem();
2✔
93
        $filesystem->remove($pathname);
2✔
94
    }
95

96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function downloadDirectory(DocumentInterface $directory): Response {
100
        return $this->newStreamedResponse($directory);
4✔
101
    }
102

103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function downloadDocument(DocumentInterface $document): Response {
107
        return $this->newStreamedResponse($document);
2✔
108
    }
109

110
    /**
111
     * Get an absolute path.
112
     *
113
     * @param DocumentInterface $document The document.
114
     * @param bool $rename Rename ?
115
     * @return string Returns the absolute path.
116
     */
117
    protected function getAbsolutePath(DocumentInterface $document, bool $rename = false): string {
118

119
        $path = [
22✔
120
            $this->getDirectory(),
22✔
121
        ];
22✔
122

123
        foreach (DocumentHelper::getPaths($document, $rename) as $current) {
22✔
124
            $path[] = $current->getId();
22✔
125
        }
126

127
        return implode(DIRECTORY_SEPARATOR, $path);
22✔
128
    }
129

130
    /**
131
     * Log an info.
132
     *
133
     * @param string $message The message.
134
     * @param array $context The context.
135
     * @return StorageProviderInterface Returns this filesystem storage provider.
136
     */
137
    protected function logInfo(string $message, array $context = []): StorageProviderInterface {
138
        $this->getLogger()->info($message, $context);
20✔
139
        return $this;
20✔
140
    }
141

142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function moveDocument(DocumentInterface $document): void {
146

147
        $src = $this->getAbsolutePath($document, true);
4✔
148
        $dst = $this->getAbsolutePath($document);
4✔
149

150
        $context = [
4✔
151
            "_provider" => get_class($this),
4✔
152
        ];
4✔
153

154
        $this->logInfo(sprintf('Filesystem storage provider tries to move "%s" into "%s"', $src, $dst), $context);
4✔
155

156
        $filesystem = new Filesystem();
4✔
157
        $filesystem->rename($src, $dst);
4✔
158
    }
159

160
    /**
161
     * {@inheritdoc}
162
     */
163
    public function newDirectory(DocumentInterface $directory): void {
164

165
        DocumentHelper::isDirectory($directory);
6✔
166

167
        $pathname = $this->getAbsolutePath($directory);
4✔
168
        $context  = [
4✔
169
            "_provider" => get_class($this),
4✔
170
        ];
4✔
171

172
        $this->logInfo(sprintf('Filesystem storage provider tries to create the directory "%s"', $pathname), $context);
4✔
173

174
        $filesystem = new Filesystem();
4✔
175
        $filesystem->mkdir($pathname);
4✔
176
    }
177

178
    /**
179
     * Create a new streamed response.
180
     *
181
     * @param DocumentInterface $document The document.
182
     * @return StreamedResponse Returns the streamed response.
183
     * @throws Throwable Throws an exception if an error occurs.
184
     */
185
    protected function newStreamedResponse(DocumentInterface $document): Response {
186

187
        $myself = $this;
6✔
188

189
        /** @var callable $callback */
190
        $callback = function() use ($document, $myself) {
6✔
191

192
            if (true === $document->isDocument()) {
2✔
193
                $myself->streamDocument($document);
×
194
                return;
×
195
            }
196

197
            $archive = $myself->zipDirectory($document);
2✔
198
            $myself->streamDocument($archive);
2✔
199
        };
6✔
200

201
        $timestamp = (new DateTime())->format("Y.m.d-H.i");
6✔
202
        $filename  = str_replace(" ", "_", DocumentHelper::getFilename($document));
6✔
203
        $extension = true === $document->isDirectory() ? ".zip" : "";
6✔
204
        $mimeType  = true === $document->isDirectory() ? "application/zip" : $document->getMimeType();
6✔
205

206
        $response = new StreamedResponse();
6✔
207
        $response->headers->set("Content-Disposition", "attachement; filename=\"{$timestamp}_$filename$extension\"");
6✔
208
        $response->headers->set("Content-Type", $mimeType);
6✔
209
        $response->setCallback($callback);
6✔
210
        $response->setStatusCode(200);
6✔
211

212
        return $response;
6✔
213
    }
214

215
    /**
216
     * Create a ZIP document.
217
     *
218
     * @param DocumentInterface $document The document.
219
     * @return DocumentInterface Returns the ZIP document.
220
     * @throws ReflectionException Throws a Reflection exception if an error occurs.
221
     */
222
    protected function newZipDocument(DocumentInterface $document): DocumentInterface {
223

224
        $id = (new DateTime())->format("YmdHisu");
2✔
225

226
        $model = new Document();
2✔
227
        $model->setExtension("zip");
2✔
228
        $model->setMimeType("application/zip");
2✔
229
        $model->setName($document->getName() . "-" . $id);
2✔
230
        $model->setType(DocumentInterface::TYPE_DOCUMENT);
2✔
231

232
        // Use reflection to set the private id attribute.
233
        $idProperty = (new ReflectionClass($model))->getProperty("id");
2✔
234
        $idProperty->setAccessible(true);
2✔
235
        $idProperty->setValue($model, intval($id));
2✔
236

237
        return $model;
2✔
238
    }
239

240
    /**
241
     * Set the directory.
242
     *
243
     * @param string $directory The directory.
244
     * @return StorageProviderInterface Returns this storage provider.
245
     */
246
    protected function setDirectory(string $directory): StorageProviderInterface {
247
        $this->directory = $directory;
38✔
248
        return $this;
38✔
249
    }
250

251
    /**
252
     * Stream a document.
253
     *
254
     * @param DocumentInterface $document The document.
255
     * @return void
256
     */
257
    protected function streamDocument(DocumentInterface $document): void {
258

259
        $filename = $this->getAbsolutePath($document);
2✔
260

261
        $input  = fopen($filename, "r");
2✔
262
        $output = fopen("php://output", "w+");
2✔
263

264
        while (false === feof($input)) {
2✔
265
            stream_copy_to_stream($input, $output, 4096);
2✔
266
        }
267

268
        fclose($input);
2✔
269
        fclose($output);
2✔
270

271
        if (1 === preg_match("/\.download$/", $filename)) {
2✔
272
            unlink($filename);
×
273
        }
274
    }
275

276
    /**
277
     * {@inheritdoc}
278
     */
279
    public function uploadDocument(DocumentInterface $document): void {
280

281
        DocumentHelper::isDocument($document);
8✔
282

283
        $src = $document->getUploadedFile()->getRealPath();
6✔
284
        $dst = $this->getAbsolutePath($document);
6✔
285

286
        $context = [
6✔
287
            "_provider" => get_class($this),
6✔
288
        ];
6✔
289

290
        $this->logInfo(sprintf('Filesystem storage provider tries to copy the uploaded document "%s" into "%s"', $src, $dst), $context);
6✔
291

292
        $filesystem = new Filesystem();
6✔
293
        $filesystem->copy($src, $dst);
6✔
294
    }
295

296
    /**
297
     * Zip a directory.
298
     *
299
     * @param DocumentInterface $directory The directory.
300
     * @return DocumentInterface Returns the zipped directory in case success.
301
     * @throws ReflectionException Throws a Reflection exception if an error occurs.
302
     */
303
    protected function zipDirectory(DocumentInterface $directory): DocumentInterface {
304

305
        $archive = $this->newZipDocument($directory);
2✔
306

307
        $src = $this->getAbsolutePath($directory);
2✔
308
        $dst = $this->getAbsolutePath($archive);
2✔
309

310
        $zip = new ZipArchive();
2✔
311
        $zip->open($dst, ZipArchive::CREATE);
2✔
312

313
        foreach ($directory->getChildren() as $current) {
2✔
314

315
            $pattern = implode("", ["/^", preg_quote("$src/", "/"), "/"]);
2✔
316
            $zipPath = preg_replace($pattern, "", DocumentHelper::getPathname($current));
2✔
317

318
            if (true === $current->isDirectory()) {
2✔
319
                $zip->addEmptyDir($zipPath);
2✔
320
            }
321
            if (true === $current->isDocument()) {
2✔
322
                $zip->addFromString($zipPath, file_get_contents($this->getAbsolutePath($current)));
×
323
            }
324
        }
325

326
        $zip->close();
2✔
327

328
        return $archive;
2✔
329
    }
330
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc