Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.57% covered (danger)
48.57%
34 / 70
23.81% covered (danger)
23.81%
5 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
local
48.57% covered (danger)
48.57%
34 / 70
23.81% covered (danger)
23.81%
5 / 21
223.22
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 put_contents
50.00% covered (danger)
50.00%
2 / 4
0.00% covered (danger)
0.00%
0 / 1
2.50
 get_contents
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 exists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
3.19
 rename
50.00% covered (danger)
50.00%
2 / 4
0.00% covered (danger)
0.00%
0 / 1
2.50
 copy
50.00% covered (danger)
50.00%
2 / 4
0.00% covered (danger)
0.00%
0 / 1
2.50
 create_dir
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 ensure_directory_exists
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 get_path
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 get_filename
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 read_stream
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 write_stream
62.50% covered (warning)
62.50%
5 / 8
0.00% covered (danger)
0.00%
0 / 1
3.47
 file_size
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 file_mimetype
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 image_dimensions
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 file_image_width
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 file_image_height
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 free_space
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 *
4 * This file is part of the phpBB Forum Software package.
5 *
6 * @copyright (c) phpBB Limited <https://www.phpbb.com>
7 * @license GNU General Public License, version 2 (GPL-2.0)
8 *
9 * For full copyright and license information, please see
10 * the docs/CREDITS.txt file.
11 *
12 */
13
14namespace phpbb\storage\adapter;
15
16use phpbb\storage\stream_interface;
17use phpbb\storage\exception\storage_exception;
18use phpbb\filesystem\exception\filesystem_exception;
19use phpbb\filesystem\filesystem;
20use phpbb\filesystem\helper as filesystem_helper;
21use phpbb\mimetype\guesser;
22use FastImageSize\FastImageSize;
23
24/**
25 * Experimental
26 */
27class local implements adapter_interface, stream_interface
28{
29    /**
30     * Filesystem component
31     *
32     * @var filesystem
33     */
34    protected $filesystem;
35
36    /**
37     * FastImageSize
38     *
39     * @var FastImageSize
40     */
41    protected $imagesize;
42
43    /**
44     * Mimetype Guesser component
45     *
46     * @var guesser
47     */
48    protected $mimetype_guesser;
49
50    /**
51     * @var string path
52     */
53    protected $phpbb_root_path;
54
55    /**
56     * Absolute path to the storage folder
57     * Always finish with DIRECTORY_SEPARATOR
58     * Example:
59     * - /var/www/phpBB/images/avatar/upload/
60     * - C:\phpBB\images\avatars\upload\
61     *
62     * @var string path
63     */
64    protected $root_path;
65
66    /**
67     * Relative path from $phpbb_root_path to the storage folder
68     * Always finish with slash (/) character
69     * Example:
70     * - images/avatars/upload/
71     *
72     * @var string path
73     */
74    protected $path;
75
76    /**
77     * Constructor
78     *
79     * @param filesystem $filesystem
80     * @param FastImageSize $imagesize
81     * @param guesser $mimetype_guesser
82     * @param string $phpbb_root_path
83     */
84    public function __construct(filesystem $filesystem, FastImageSize $imagesize, guesser $mimetype_guesser, string $phpbb_root_path)
85    {
86        $this->filesystem = $filesystem;
87        $this->imagesize = $imagesize;
88        $this->mimetype_guesser = $mimetype_guesser;
89        $this->phpbb_root_path = $phpbb_root_path;
90    }
91
92    /**
93     * {@inheritdoc}
94     */
95    public function configure(array $options): void
96    {
97        $this->path = $options['path'];
98
99        if (substr($this->path, -1, 1) !== '/')
100        {
101            $this->path = $this->path . '/';
102        }
103
104        $this->root_path = filesystem_helper::realpath($this->phpbb_root_path . $options['path']) . DIRECTORY_SEPARATOR;
105    }
106
107    /**
108     * {@inheritdoc}
109     */
110    public function put_contents(string $path, string $content): void
111    {
112        $this->ensure_directory_exists($path);
113
114        try
115        {
116            $this->filesystem->dump_file($this->root_path . $this->get_path($path) . $this->get_filename($path), $content);
117        }
118        catch (filesystem_exception $e)
119        {
120            throw new storage_exception('STORAGE_CANNOT_WRITE_FILE', $path, array(), $e);
121        }
122    }
123
124    /**
125     * {@inheritdoc}
126     */
127    public function get_contents(string $path): string
128    {
129        $content = @file_get_contents($this->root_path . $this->get_path($path) . $this->get_filename($path));
130
131        if ($content === false)
132        {
133            throw new storage_exception('STORAGE_CANNOT_READ_FILE', $path);
134        }
135
136        return $content;
137    }
138
139    /**
140     * {@inheritdoc}
141     */
142    public function exists(string $path): bool
143    {
144        return $this->filesystem->exists($this->root_path . $this->get_path($path) . $this->get_filename($path));
145    }
146
147    /**
148     * {@inheritdoc}
149     */
150    public function delete(string $path): void
151    {
152        try
153        {
154            $this->filesystem->remove($this->root_path . $this->get_path($path) . $this->get_filename($path));
155        }
156        catch (filesystem_exception $e)
157        {
158            throw new storage_exception('STORAGE_CANNOT_DELETE', $path, array(), $e);
159        }
160    }
161
162    /**
163     * {@inheritdoc}
164     */
165    public function rename(string $path_orig, string $path_dest): void
166    {
167        $this->ensure_directory_exists($path_dest);
168
169        try
170        {
171            $this->filesystem->rename($this->root_path . $this->get_path($path_orig) . $this->get_filename($path_orig), $this->root_path . $this->get_path($path_dest) . $this->get_filename($path_dest), false);
172        }
173        catch (filesystem_exception $e)
174        {
175            throw new storage_exception('STORAGE_CANNOT_RENAME', $path_orig, array(), $e);
176        }
177    }
178
179    /**
180     * {@inheritdoc}
181     */
182    public function copy(string $path_orig, string $path_dest): void
183    {
184        $this->ensure_directory_exists($path_dest);
185
186        try
187        {
188            $this->filesystem->copy($this->root_path . $this->get_path($path_orig) . $this->get_filename($path_orig), $this->root_path . $this->get_path($path_dest) . $this->get_filename($path_dest), false);
189        }
190        catch (filesystem_exception $e)
191        {
192            throw new storage_exception('STORAGE_CANNOT_COPY', $path_orig, array(), $e);
193        }
194    }
195
196    /**
197     * Creates a directory recursively.
198     *
199     * @param string    $path    The directory path
200     *
201     * @throws storage_exception    On any directory creation failure
202     */
203    protected function create_dir(string $path): void
204    {
205        try
206        {
207            $this->filesystem->mkdir($this->root_path . $path);
208        }
209        catch (filesystem_exception $e)
210        {
211            throw new storage_exception('STORAGE_CANNOT_CREATE_DIR', $path, array(), $e);
212        }
213    }
214
215    /**
216     * Ensures that the directory of a file exists.
217     *
218     * @param string    $path    The file path
219     *
220     * @throws storage_exception    On any directory creation failure
221     */
222    protected function ensure_directory_exists(string $path): void
223    {
224        $path = dirname($this->root_path . $this->get_path($path) . $this->get_filename($path));
225        $path = filesystem_helper::make_path_relative($path, $this->root_path);
226
227        if (!$this->exists($path))
228        {
229            $this->create_dir($path);
230        }
231    }
232
233    /**
234     * Get the path to the file
235     *
236     * @param string $path The file path
237     * @return string
238     */
239    protected function get_path(string $path): string
240    {
241        $dirname = dirname($path);
242        $dirname = ($dirname != '.') ? $dirname . DIRECTORY_SEPARATOR : '';
243
244        return $dirname;
245    }
246
247    /**
248     * To be used in other PR
249     *
250     * @param string $path The file path
251     * @return string
252     */
253    protected function get_filename(string $path): string
254    {
255        return basename($path);
256    }
257
258    /**
259     * {@inheritdoc}
260     */
261    public function read_stream(string $path)
262    {
263        $stream = @fopen($this->root_path . $this->get_path($path) . $this->get_filename($path), 'rb');
264
265        if (!$stream)
266        {
267            throw new storage_exception('STORAGE_CANNOT_OPEN_FILE', $path);
268        }
269
270        return $stream;
271    }
272
273    /**
274     * {@inheritdoc}
275     */
276    public function write_stream(string $path, $resource): void
277    {
278        $this->ensure_directory_exists($path);
279
280        $stream = @fopen($this->root_path . $this->get_path($path) . $this->get_filename($path), 'w+b');
281
282        if (!$stream)
283        {
284            throw new storage_exception('STORAGE_CANNOT_CREATE_FILE', $path);
285        }
286
287        if (stream_copy_to_stream($resource, $stream) === false)
288        {
289            fclose($stream);
290            throw new storage_exception('STORAGE_CANNOT_COPY_RESOURCE');
291        }
292
293        fclose($stream);
294    }
295
296    /**
297     * Get file size
298     *
299     * @param string    $path    The file
300     *
301     * @return array Properties
302     *
303     * @throws storage_exception        When cannot get size
304     */
305    public function file_size(string $path): array
306    {
307        $size = @filesize($this->root_path . $this->get_path($path) . $this->get_filename($path));
308
309        if ($size === null)
310        {
311            throw new storage_exception('STORAGE_CANNOT_GET_FILESIZE');
312        }
313
314        return ['size' => $size];
315    }
316
317    /**
318     * Get file mimetype
319     *
320     * @param string    $path    The file
321     *
322     * @return array    Properties
323     */
324    public function file_mimetype(string $path): array
325    {
326        return ['mimetype' => $this->mimetype_guesser->guess($this->root_path . $this->get_path($path) . $this->get_filename($path))];
327    }
328
329    /**
330     * Get image dimensions
331     *
332     * @param string    $path    The file
333     *
334     * @return array    Properties
335     */
336    protected function image_dimensions(string $path): array
337    {
338        $size = $this->imagesize->getImageSize($this->root_path . $this->get_path($path) . $this->get_filename($path));
339
340        // For not supported types like swf
341        if ($size === false)
342        {
343            $imsize = getimagesize($this->root_path . $this->get_path($path) . $this->get_filename($path));
344            $size = ['width' => $imsize[0], 'height' => $imsize[1]];
345        }
346
347        return ['image_width' => $size['width'], 'image_height' => $size['height']];
348    }
349
350    /**
351     * Get image width
352     *
353     * @param string    $path    The file
354     *
355     * @return array    Properties
356     */
357    public function file_image_width(string $path): array
358    {
359        return $this->image_dimensions($path);
360    }
361
362    /**
363     * Get image height
364     *
365     * @param string    $path    The file
366     *
367     * @return array    Properties
368     */
369    public function file_image_height(string $path): array
370    {
371        return $this->image_dimensions($path);
372    }
373
374    /**
375     * {@inheritdoc}
376     */
377    public function get_link(string $path): string
378    {
379        return generate_board_url() . '/' . $this->path . $path;
380    }
381
382    /**
383     * {@inheritdoc}
384     */
385    public function free_space(): float
386    {
387        if (function_exists('disk_free_space'))
388        {
389            $free_space = @disk_free_space($this->root_path);
390
391            if ($free_space === false)
392            {
393                throw new storage_exception('STORAGE_CANNOT_GET_FREE_SPACE');
394            }
395        }
396        else
397        {
398            throw new storage_exception('STORAGE_CANNOT_GET_FREE_SPACE');
399        }
400
401        return $free_space;
402    }
403}