Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
67.89% |
74 / 109 |
|
42.86% |
3 / 7 |
CRAP | |
0.00% |
0 / 1 |
helper | |
67.89% |
74 / 109 |
|
42.86% |
3 / 7 |
199.61 | |
0.00% |
0 / 1 |
clean_path | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
8 | |||
is_absolute_path | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
4 | |||
phpbb_own_realpath | |
71.43% |
20 / 28 |
|
0.00% |
0 / 1 |
15.36 | |||
realpath | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
6.07 | |||
make_path_relative | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
resolve_path | |
56.90% |
33 / 58 |
|
0.00% |
0 / 1 |
107.96 | |||
get_symfony_filesystem | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 |
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 | |
14 | namespace phpbb\filesystem; |
15 | |
16 | use Symfony\Component\Filesystem\Filesystem as symfony_filesystem; |
17 | |
18 | class helper |
19 | { |
20 | /** |
21 | * @var symfony_filesystem |
22 | */ |
23 | protected static $symfony_filesystem; |
24 | |
25 | /** |
26 | * Eliminates useless . and .. components from specified path. |
27 | * |
28 | * @param string $path Path to clean |
29 | * |
30 | * @return string Cleaned path |
31 | */ |
32 | public static function clean_path($path) |
33 | { |
34 | $exploded = explode('/', $path); |
35 | $filtered = array(); |
36 | foreach ($exploded as $part) |
37 | { |
38 | if ($part === '.' && !empty($filtered)) |
39 | { |
40 | continue; |
41 | } |
42 | |
43 | if ($part === '..' && !empty($filtered) && $filtered[count($filtered) - 1] !== '.' && $filtered[count($filtered) - 1] !== '..') |
44 | { |
45 | array_pop($filtered); |
46 | } |
47 | else |
48 | { |
49 | $filtered[] = $part; |
50 | } |
51 | } |
52 | $path = implode('/', $filtered); |
53 | return $path; |
54 | } |
55 | |
56 | /** |
57 | * Checks if a path is absolute or not |
58 | * |
59 | * @param string $path Path to check |
60 | * |
61 | * @return bool true if the path is absolute, false otherwise |
62 | */ |
63 | public static function is_absolute_path($path) |
64 | { |
65 | return (isset($path[0]) && $path[0] === '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false; |
66 | } |
67 | |
68 | /** |
69 | * Try to resolve real path when PHP's realpath failes to do so |
70 | * |
71 | * @param string $path |
72 | * @return string|false |
73 | */ |
74 | protected static function phpbb_own_realpath($path) |
75 | { |
76 | // Replace all directory separators with '/' |
77 | $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); |
78 | |
79 | $is_absolute_path = false; |
80 | $path_prefix = ''; |
81 | |
82 | if (self::is_absolute_path($path)) |
83 | { |
84 | $is_absolute_path = true; |
85 | } |
86 | else |
87 | { |
88 | if (function_exists('getcwd')) |
89 | { |
90 | $working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()); |
91 | } |
92 | |
93 | // |
94 | // From this point on we really just guessing |
95 | // If chdir were called we screwed |
96 | // |
97 | else if (function_exists('debug_backtrace')) |
98 | { |
99 | $call_stack = debug_backtrace(0); |
100 | $working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[max(0, count($call_stack) - 1)]['file'])); |
101 | } |
102 | else |
103 | { |
104 | // |
105 | // Assuming that the working directory is phpBB root |
106 | // we could use this as a fallback, when phpBB will use controllers |
107 | // everywhere this will be a safe assumption |
108 | // |
109 | //$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__); |
110 | //$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\')); |
111 | |
112 | //$namespace_part_count = count($namespace_parts); |
113 | |
114 | // Check if we still loading from root |
115 | //if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts) |
116 | //{ |
117 | // $working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count)); |
118 | //} |
119 | //else |
120 | //{ |
121 | // $working_directory = false; |
122 | //} |
123 | |
124 | $working_directory = false; |
125 | } |
126 | |
127 | if ($working_directory !== false) |
128 | { |
129 | $is_absolute_path = true; |
130 | $path = $working_directory . '/' . $path; |
131 | } |
132 | } |
133 | |
134 | if ($is_absolute_path) |
135 | { |
136 | if (defined('PHP_WINDOWS_VERSION_MAJOR')) |
137 | { |
138 | $path_prefix = $path[0] . ':'; |
139 | $path = substr($path, 2); |
140 | } |
141 | else |
142 | { |
143 | $path_prefix = ''; |
144 | } |
145 | } |
146 | |
147 | $resolved_path = self::resolve_path($path, $path_prefix, $is_absolute_path); |
148 | if ($resolved_path === false) |
149 | { |
150 | return false; |
151 | } |
152 | |
153 | if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path))) |
154 | { |
155 | return false; |
156 | } |
157 | |
158 | // Return OS specific directory separators |
159 | $resolved = str_replace('/', DIRECTORY_SEPARATOR, (string) $resolved_path); |
160 | |
161 | // Check for DIRECTORY_SEPARATOR at the end (and remove it!) |
162 | if (substr($resolved, -1) === DIRECTORY_SEPARATOR) |
163 | { |
164 | return substr($resolved, 0, -1); |
165 | } |
166 | |
167 | return $resolved; |
168 | } |
169 | |
170 | /** |
171 | * A wrapper for PHP's realpath |
172 | * |
173 | * Try to resolve realpath when PHP's realpath is not available, or |
174 | * known to be buggy. |
175 | * |
176 | * @param string $path Path to resolve |
177 | * |
178 | * @return string|false Resolved path or false if path could not be resolved |
179 | */ |
180 | public static function realpath($path) |
181 | { |
182 | if (!function_exists('realpath')) |
183 | { |
184 | return self::phpbb_own_realpath($path); |
185 | } |
186 | |
187 | $realpath = realpath($path); |
188 | |
189 | // Strangely there are provider not disabling realpath but returning strange values. :o |
190 | // We at least try to cope with them. |
191 | if ((!self::is_absolute_path($path) && $realpath === $path) || $realpath === false) |
192 | { |
193 | return self::phpbb_own_realpath($path); |
194 | } |
195 | |
196 | // Check for DIRECTORY_SEPARATOR at the end (and remove it!) |
197 | if (substr($realpath, -1) === DIRECTORY_SEPARATOR) |
198 | { |
199 | $realpath = substr($realpath, 0, -1); |
200 | } |
201 | |
202 | return $realpath; |
203 | } |
204 | |
205 | /** |
206 | * Given an existing path, convert it to a path relative to a given starting path |
207 | * |
208 | * @param string $end_path Absolute path of target |
209 | * @param string $start_path Absolute path where traversal begins |
210 | * |
211 | * @return string Path of target relative to starting path |
212 | */ |
213 | public static function make_path_relative($end_path, $start_path) |
214 | { |
215 | return self::get_symfony_filesystem()->makePathRelative($end_path, $start_path); |
216 | } |
217 | |
218 | /** |
219 | * Try to resolve symlinks in path |
220 | * |
221 | * @param string $path The path to resolve |
222 | * @param string $prefix The path prefix (on windows the drive letter) |
223 | * @param bool $absolute Whether or not the path is absolute |
224 | * @param bool $return_array Whether or not to return path parts |
225 | * |
226 | * @return string|array|bool returns the resolved path or an array of parts of the path if $return_array is true |
227 | * or false if path cannot be resolved |
228 | */ |
229 | public static function resolve_path($path, $prefix = '', $absolute = false, $return_array = false) |
230 | { |
231 | if ($return_array) |
232 | { |
233 | $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); |
234 | } |
235 | |
236 | //$path = trim($path, '/'); // TODO: Check this |
237 | $path_parts = explode('/', $path); |
238 | $resolved = array(); |
239 | $resolved_path = $prefix; |
240 | $file_found = false; |
241 | |
242 | foreach ($path_parts as $path_part) |
243 | { |
244 | if ($file_found) |
245 | { |
246 | return false; |
247 | } |
248 | |
249 | if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved)))) |
250 | { |
251 | continue; |
252 | } |
253 | else if ($absolute && $path_part === '..') |
254 | { |
255 | if (empty($resolved)) |
256 | { |
257 | // No directories above root |
258 | return false; |
259 | } |
260 | |
261 | array_pop($resolved); |
262 | $resolved_path = false; |
263 | } |
264 | else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[count($resolved) - 1], array('.', '..'))) |
265 | { |
266 | array_pop($resolved); |
267 | $resolved_path = false; |
268 | } |
269 | else |
270 | { |
271 | if ($resolved_path === false) |
272 | { |
273 | if (empty($resolved)) |
274 | { |
275 | $resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part; |
276 | } |
277 | else |
278 | { |
279 | $tmp_array = $resolved; |
280 | if ($absolute) |
281 | { |
282 | array_unshift($tmp_array, $prefix); |
283 | } |
284 | |
285 | $resolved_path = implode('/', $tmp_array); |
286 | } |
287 | } |
288 | |
289 | $current_path = $resolved_path . '/' . $path_part; |
290 | |
291 | // Resolve symlinks |
292 | if (@is_link($current_path)) |
293 | { |
294 | if (!function_exists('readlink')) |
295 | { |
296 | return false; |
297 | } |
298 | |
299 | $link = readlink($current_path); |
300 | |
301 | // Is link has an absolute path in it? |
302 | if (self::is_absolute_path($link)) |
303 | { |
304 | if (defined('PHP_WINDOWS_VERSION_MAJOR')) |
305 | { |
306 | $prefix = $link[0] . ':'; |
307 | $link = substr($link, 2); |
308 | } |
309 | else |
310 | { |
311 | $prefix = ''; |
312 | } |
313 | |
314 | $resolved = self::resolve_path($link, $prefix, true, true); |
315 | $absolute = true; |
316 | } |
317 | else |
318 | { |
319 | $resolved = self::resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true); |
320 | } |
321 | |
322 | if (!$resolved) |
323 | { |
324 | return false; |
325 | } |
326 | |
327 | $resolved_path = false; |
328 | } |
329 | else if (@is_dir($current_path . '/')) |
330 | { |
331 | $resolved[] = $path_part; |
332 | $resolved_path = $current_path; |
333 | } |
334 | else if (@is_file($current_path)) |
335 | { |
336 | $resolved[] = $path_part; |
337 | $resolved_path = $current_path; |
338 | $file_found = true; |
339 | } |
340 | else |
341 | { |
342 | return false; |
343 | } |
344 | } |
345 | } |
346 | |
347 | // If at the end of the path there were a .. or . |
348 | // we need to build the path again. |
349 | // Only doing this when a string is expected in return |
350 | if ($resolved_path === false && $return_array === false) |
351 | { |
352 | if (empty($resolved)) |
353 | { |
354 | $resolved_path = ($absolute) ? $prefix . '/' : './'; |
355 | } |
356 | else |
357 | { |
358 | $tmp_array = $resolved; |
359 | if ($absolute) |
360 | { |
361 | array_unshift($tmp_array, $prefix); |
362 | } |
363 | |
364 | $resolved_path = implode('/', $tmp_array); |
365 | } |
366 | } |
367 | |
368 | return $return_array ? $resolved : $resolved_path; |
369 | } |
370 | |
371 | /** |
372 | * Get an instance of symfony's filesystem object. |
373 | * |
374 | * @return symfony_filesystem Symfony filesystem |
375 | */ |
376 | protected static function get_symfony_filesystem() |
377 | { |
378 | if (self::$symfony_filesystem === null) |
379 | { |
380 | self::$symfony_filesystem = new symfony_filesystem(); |
381 | } |
382 | |
383 | return self::$symfony_filesystem; |
384 | } |
385 | } |