Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
54.24% covered (warning)
54.24%
32 / 59
31.25% covered (danger)
31.25%
5 / 16
CRAP
0.00% covered (danger)
0.00%
0 / 1
local
54.24% covered (warning)
54.24%
32 / 59
31.25% covered (danger)
31.25%
5 / 16
123.10
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
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
 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;
21
22/**
23 * Experimental
24 */
25class local implements adapter_interface, stream_interface
26{
27    /**
28     * Filesystem component
29     *
30     * @var filesystem
31     */
32    protected $filesystem;
33
34    /**
35     * @var string path
36     */
37    protected $phpbb_root_path;
38
39    /**
40     * Absolute path to the storage folder
41     * Always finish with DIRECTORY_SEPARATOR
42     * Example:
43     * - /var/www/phpBB/images/avatar/upload/
44     * - C:\phpBB\images\avatars\upload\
45     *
46     * @var string path
47     */
48    protected $root_path;
49
50    /**
51     * Relative path from $phpbb_root_path to the storage folder
52     * Always finish with slash (/) character
53     * Example:
54     * - images/avatars/upload/
55     *
56     * @var string path
57     */
58    protected $path;
59
60    /**
61     * Constructor
62     *
63     * @param filesystem $filesystem
64     * @param string $phpbb_root_path
65     */
66    public function __construct(filesystem $filesystem, string $phpbb_root_path)
67    {
68        $this->filesystem = $filesystem;
69        $this->phpbb_root_path = $phpbb_root_path;
70    }
71
72    /**
73     * {@inheritdoc}
74     */
75    public function configure(array $options): void
76    {
77        $this->path = $options['path'];
78
79        if (substr($this->path, -1, 1) !== '/')
80        {
81            $this->path = $this->path . '/';
82        }
83
84        $this->root_path = filesystem_helper::realpath($this->phpbb_root_path . $options['path']) . DIRECTORY_SEPARATOR;
85    }
86
87    /**
88     * {@inheritdoc}
89     */
90    public function put_contents(string $path, string $content): void
91    {
92        $this->ensure_directory_exists($path);
93
94        try
95        {
96            $this->filesystem->dump_file($this->root_path . $this->get_path($path) . $this->get_filename($path), $content);
97        }
98        catch (filesystem_exception $e)
99        {
100            throw new storage_exception('STORAGE_CANNOT_WRITE_FILE', $path, array(), $e);
101        }
102    }
103
104    /**
105     * {@inheritdoc}
106     */
107    public function get_contents(string $path): string
108    {
109        $content = @file_get_contents($this->root_path . $this->get_path($path) . $this->get_filename($path));
110
111        if ($content === false)
112        {
113            throw new storage_exception('STORAGE_CANNOT_READ_FILE', $path);
114        }
115
116        return $content;
117    }
118
119    /**
120     * {@inheritdoc}
121     */
122    public function exists(string $path): bool
123    {
124        return $this->filesystem->exists($this->root_path . $this->get_path($path) . $this->get_filename($path));
125    }
126
127    /**
128     * {@inheritdoc}
129     */
130    public function delete(string $path): void
131    {
132        try
133        {
134            $this->filesystem->remove($this->root_path . $this->get_path($path) . $this->get_filename($path));
135        }
136        catch (filesystem_exception $e)
137        {
138            throw new storage_exception('STORAGE_CANNOT_DELETE', $path, array(), $e);
139        }
140    }
141
142    /**
143     * {@inheritdoc}
144     */
145    public function rename(string $path_orig, string $path_dest): void
146    {
147        $this->ensure_directory_exists($path_dest);
148
149        try
150        {
151            $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);
152        }
153        catch (filesystem_exception $e)
154        {
155            throw new storage_exception('STORAGE_CANNOT_RENAME', $path_orig, array(), $e);
156        }
157    }
158
159    /**
160     * {@inheritdoc}
161     */
162    public function copy(string $path_orig, string $path_dest): void
163    {
164        $this->ensure_directory_exists($path_dest);
165
166        try
167        {
168            $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);
169        }
170        catch (filesystem_exception $e)
171        {
172            throw new storage_exception('STORAGE_CANNOT_COPY', $path_orig, array(), $e);
173        }
174    }
175
176    /**
177     * Creates a directory recursively.
178     *
179     * @param string    $path    The directory path
180     *
181     * @throws storage_exception    On any directory creation failure
182     */
183    protected function create_dir(string $path): void
184    {
185        try
186        {
187            $this->filesystem->mkdir($this->root_path . $path);
188        }
189        catch (filesystem_exception $e)
190        {
191            throw new storage_exception('STORAGE_CANNOT_CREATE_DIR', $path, array(), $e);
192        }
193    }
194
195    /**
196     * Ensures that the directory of a file exists.
197     *
198     * @param string    $path    The file path
199     *
200     * @throws storage_exception    On any directory creation failure
201     */
202    protected function ensure_directory_exists(string $path): void
203    {
204        $path = dirname($this->root_path . $this->get_path($path) . $this->get_filename($path));
205        $path = filesystem_helper::make_path_relative($path, $this->root_path);
206
207        if (!$this->exists($path))
208        {
209            $this->create_dir($path);
210        }
211    }
212
213    /**
214     * Get the path to the file
215     *
216     * @param string $path The file path
217     * @return string
218     */
219    protected function get_path(string $path): string
220    {
221        $dirname = dirname($path);
222        $dirname = ($dirname != '.') ? $dirname . DIRECTORY_SEPARATOR : '';
223
224        return $dirname;
225    }
226
227    /**
228     * To be used in other PR
229     *
230     * @param string $path The file path
231     * @return string
232     */
233    protected function get_filename(string $path): string
234    {
235        return basename($path);
236    }
237
238    /**
239     * {@inheritdoc}
240     */
241    public function read_stream(string $path)
242    {
243        $stream = @fopen($this->root_path . $this->get_path($path) . $this->get_filename($path), 'rb');
244
245        if (!$stream)
246        {
247            throw new storage_exception('STORAGE_CANNOT_OPEN_FILE', $path);
248        }
249
250        return $stream;
251    }
252
253    /**
254     * {@inheritdoc}
255     */
256    public function write_stream(string $path, $resource): void
257    {
258        $this->ensure_directory_exists($path);
259
260        $stream = @fopen($this->root_path . $this->get_path($path) . $this->get_filename($path), 'w+b');
261
262        if (!$stream)
263        {
264            throw new storage_exception('STORAGE_CANNOT_CREATE_FILE', $path);
265        }
266
267        if (stream_copy_to_stream($resource, $stream) === false)
268        {
269            fclose($stream);
270            throw new storage_exception('STORAGE_CANNOT_COPY_RESOURCE');
271        }
272
273        fclose($stream);
274    }
275
276    /**
277     * {@inheritdoc}
278     */
279    public function file_size(string $path): int
280    {
281        $size = @filesize($this->root_path . $this->get_path($path) . $this->get_filename($path));
282
283        if ($size === null)
284        {
285            throw new storage_exception('STORAGE_CANNOT_GET_FILESIZE');
286        }
287
288        return $size;
289    }
290
291    /**
292     * {@inheritdoc}
293     */
294    public function free_space(): float
295    {
296        if (function_exists('disk_free_space'))
297        {
298            $free_space = @disk_free_space($this->root_path);
299
300            if ($free_space === false)
301            {
302                throw new storage_exception('STORAGE_CANNOT_GET_FREE_SPACE');
303            }
304        }
305        else
306        {
307            throw new storage_exception('STORAGE_CANNOT_GET_FREE_SPACE');
308        }
309
310        return $free_space;
311    }
312}