diff --git a/phpBB/config/default/container/services.yml b/phpBB/config/default/container/services.yml index 17fa223dda..c917c948dd 100644 --- a/phpBB/config/default/container/services.yml +++ b/phpBB/config/default/container/services.yml @@ -25,6 +25,7 @@ imports: - { resource: services_profilefield.yml } - { resource: services_report.yml } - { resource: services_routing.yml } + - { resource: services_storage.yml } - { resource: services_text_formatter.yml } - { resource: services_text_reparser.yml } - { resource: services_twig.yml } diff --git a/phpBB/config/default/container/services_storage.yml b/phpBB/config/default/container/services_storage.yml new file mode 100644 index 0000000000..7d004ec9c6 --- /dev/null +++ b/phpBB/config/default/container/services_storage.yml @@ -0,0 +1,14 @@ +services: + storage.adapter.avatar: + class: phpbb\storage\adapter\local + arguments: + - '@filesystem' + - '%core.root_path%' + storage.avatar: + class: phpbb\storage\storage + arguments: + - '@storage.adapter.avatar' + storage.controller: + class: phpbb\storage\controller + arguments: + - '@service_container' diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php new file mode 100644 index 0000000000..01feb50179 --- /dev/null +++ b/phpBB/phpbb/storage/adapter/adapter_interface.php @@ -0,0 +1,78 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage\adapter; + +interface adapter_interface +{ + /** + * Dumps content into a file. + * + * @param string path The file to be written to. + * @param string content The data to write into the file. + * + * @throws \phpbb\storage\exception\exception When the file already exists + * When the file cannot be written + */ + public function put_contents($path, $content); + + /** + * Read the contents of a file + * + * @param string $path The file to read + * + * @throws \phpbb\storage\exception\exception When the file dont exists + * When cannot read file contents + */ + public function get_contents($path); + + /** + * Checks the existence of files or directories. + * + * @param string $path file/directory to check + * + * @return bool Returns true if all files/directories exist, false otherwise + */ + public function exists($path); + + /** + * Removes files or directories. + * + * @param string $path file/directory to remove + * + * @throws \phpbb\storage\exception\exception When removal fails. + */ + public function delete($path); + + /** + * Rename a file or a directory. + * + * @param string $path_orig The original file/direcotry + * @param string $path_dest The target file/directory + * + * @throws \phpbb\storage\exception\exception When target exists + * When file/directory cannot be renamed + */ + public function rename($path_orig, $path_dest); + + /** + * Copies a file. + * + * @param string $path_orig The original filename + * @param string $path_dest The target filename + * + * @throws \phpbb\storage\exception\exception When target exists + * When the file cannot be copied + */ + public function copy($path_orig, $path_dest); +} diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php new file mode 100644 index 0000000000..b2d8c58d36 --- /dev/null +++ b/phpBB/phpbb/storage/adapter/local.php @@ -0,0 +1,146 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage\adapter; + +use phpbb\storage\exception\exception; +use phpbb\filesystem\filesystem_exception; + +class local implements adapter_interface +{ + /** + * Filesystem component + * + * @var \phpbb\filesystem\filesystem + */ + protected $filesystem; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + /** + * Constructor + */ + public function __construct($filesystem, $phpbb_root_path) + { + $this->filesystem = $filesystem; + $this->phpbb_root_path = $phpbb_root_path; + } + + /** + * {@inheritdoc} + */ + public function put_contents($path, $content) + { + if ($this->exists($this->phpbb_root_path.$path)) + { + throw new exception('', $path); // FILE_EXISTS + } + + try + { + $this->filesystem->dump_file($this->phpbb_root_path.$path, $content); + } + catch (filesystem_exception $e) + { + throw new exception('', $path, array(), $e); // CANNOT_DUMP_FILE + } + } + + /** + * {@inheritdoc} + */ + public function get_contents($path) + { + if (!$this->exists($this->phpbb_root_path.$path)) + { + throw new exception('', $path); // FILE_DONT_EXIST + } + + if (($content = @file_get_contents($this->phpbb_root_path.$path)) === false) + { + throw new exception('', $path); // CANNOT READ FILE + } + + return $content; + } + + /** + * {@inheritdoc} + */ + public function exists($path) + { + return $this->filesystem->exists($this->phpbb_root_path.$path); + } + + /** + * {@inheritdoc} + */ + public function delete($path) + { + try + { + $this->filesystem->remove($this->phpbb_root_path.$path); + } + catch (filesystem_exception $e) + { + throw new exception('', $path, array(), $e); // CANNOT DELETE + } + } + + /** + * {@inheritdoc} + */ + public function rename($path_orig, $path_dest) + { + try + { + $this->filesystem->rename($this->phpbb_root_path.$path_orig, $this->phpbb_root_path.$path_dest, false); + } + catch (filesystem_exception $e) + { + throw new exception('', $path_orig, array(), $e); // CANNOT_RENAME + } + } + + /** + * {@inheritdoc} + */ + public function copy($path_orig, $path_dest) + { + try + { + $this->filesystem->copy($this->phpbb_root_path.$path_orig, $this->phpbb_root_path.$path_dest, false); + } + catch (filesystem_exception $e) + { + throw new exception('', '', array(), $e); // CANNOT_COPY_FILES + } + } + + /** + * {@inheritdoc} + */ + public function create_dir($path) + { + try + { + $this->filesystem->mkdir($this->phpbb_root_path.$path); + } + catch (filesystem_exception $e) + { + throw new exception('', $path, array(), $e); // CANNOT_CREATE_DIRECTORY + } + } + +} diff --git a/phpBB/phpbb/storage/controller.php b/phpBB/phpbb/storage/controller.php new file mode 100644 index 0000000000..22f733faf7 --- /dev/null +++ b/phpBB/phpbb/storage/controller.php @@ -0,0 +1,39 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage; + +class controller +{ + /** + * @var ContainerInterface + */ + protected $container; + + /** + * Constructor. + * + * @param ContainerInterface $container A ContainerInterface instance + */ + public function __construct(ContainerInterface $container) + { + $this->container = $container; + } + + public function get_file($storage, $file) + { + $storage = $this->container->get($storage); + + return $storage->get_contents($file); + } +} diff --git a/phpBB/phpbb/storage/exception/exception.php b/phpBB/phpbb/storage/exception/exception.php new file mode 100644 index 0000000000..ee42178961 --- /dev/null +++ b/phpBB/phpbb/storage/exception/exception.php @@ -0,0 +1,42 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage\exception; + +class exception extends \phpbb\exception\runtime_exception +{ + /** + * Constructor + * + * @param string $message The Exception message to throw (must be a language variable). + * @param string $filename The file that caused the error. + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($message = "", $filename = '', $parameters = array(), \Exception $previous = null, $code = 0) + { + parent::__construct($message, array_merge(array('filename' => $filename), $parameters), $previous, $code); + } + + /** + * Returns the filename that triggered the error + * + * @return string + */ + public function get_filename() + { + $parameters = parent::get_parameters(); + return $parameters['filename']; + } +} diff --git a/phpBB/phpbb/storage/exception/not_implemented.php b/phpBB/phpbb/storage/exception/not_implemented.php new file mode 100644 index 0000000000..5504cfa76f --- /dev/null +++ b/phpBB/phpbb/storage/exception/not_implemented.php @@ -0,0 +1,42 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage\exception; + +class not_implemented extends \phpbb\exception\runtime_exception +{ + /** + * Constructor + * + * @param string $message The Exception message to throw (must be a language variable). + * @param string $filename The file that caused the error. + * @param array $parameters The parameters to use with the language var. + * @param \Exception $previous The previous runtime_exception used for the runtime_exception chaining. + * @param integer $code The Exception code. + */ + public function __construct($message = "", $filename = '', $parameters = array(), \Exception $previous = null, $code = 0) + { + parent::__construct($message, array_merge(array('filename' => $filename), $parameters), $previous, $code); + } + + /** + * Returns the filename that triggered the error + * + * @return string + */ + public function get_filename() + { + $parameters = parent::get_parameters(); + return $parameters['filename']; + } +} diff --git a/phpBB/phpbb/storage/helper.php b/phpBB/phpbb/storage/helper.php new file mode 100644 index 0000000000..4c8dfe466b --- /dev/null +++ b/phpBB/phpbb/storage/helper.php @@ -0,0 +1,365 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage; + +class helper +{ + /** + * Eliminates useless . and .. components from specified path. + * + * @param string $path Path to clean + * + * @return string Cleaned path + */ + public static function clean_path($path) + { + $exploded = explode('/', $path); + $filtered = array(); + foreach ($exploded as $part) + { + if ($part === '.' && !empty($filtered)) + { + continue; + } + + if ($part === '..' && !empty($filtered) && $filtered[sizeof($filtered) - 1] !== '.' && $filtered[sizeof($filtered) - 1] !== '..') + { + array_pop($filtered); + } + else + { + $filtered[] = $part; + } + } + $path = implode('/', $filtered); + return $path; + } + + /** + * Checks if a path is absolute or not + * + * @param string $path Path to check + * + * @return bool true if the path is absolute, false otherwise + */ + public static function is_absolute_path($path) + { + return (isset($path[0]) && $path[0] === '/' || preg_match('#^[a-z]:[/\\\]#i', $path)) ? true : false; + } + + /** + * Try to resolve real path when PHP's realpath failes to do so + * + * @param string $path + * @return bool|string + */ + protected static function phpbb_own_realpath($path) + { + + // Replace all directory separators with '/' + $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); + + $is_absolute_path = false; + $path_prefix = ''; + + if (self::is_absolute_path($path)) + { + $is_absolute_path = true; + } + else + { + if (function_exists('getcwd')) + { + $working_directory = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()); + } + + // + // From this point on we really just guessing + // If chdir were called we screwed + // + else if (function_exists('debug_backtrace')) + { + $call_stack = debug_backtrace(0); + $working_directory = str_replace(DIRECTORY_SEPARATOR, '/', dirname($call_stack[sizeof($call_stack) - 1]['file'])); + } + else + { + // + // Assuming that the working directory is phpBB root + // we could use this as a fallback, when phpBB will use controllers + // everywhere this will be a safe assumption + // + //$dir_parts = explode(DIRECTORY_SEPARATOR, __DIR__); + //$namespace_parts = explode('\\', trim(__NAMESPACE__, '\\')); + + //$namespace_part_count = sizeof($namespace_parts); + + // Check if we still loading from root + //if (array_slice($dir_parts, -$namespace_part_count) === $namespace_parts) + //{ + // $working_directory = implode('/', array_slice($dir_parts, 0, -$namespace_part_count)); + //} + //else + //{ + // $working_directory = false; + //} + + $working_directory = false; + } + + if ($working_directory !== false) + { + $is_absolute_path = true; + $path = $working_directory . '/' . $path; + } + } + + if ($is_absolute_path) + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) + { + $path_prefix = $path[0] . ':'; + $path = substr($path, 2); + } + else + { + $path_prefix = ''; + } + } + + $resolved_path = self::resolve_path($path, $path_prefix, $is_absolute_path); + if ($resolved_path === false) + { + return false; + } + + if (!@file_exists($resolved_path) || (!@is_dir($resolved_path . '/') && !is_file($resolved_path))) + { + return false; + } + + // Return OS specific directory separators + $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved_path); + + // Check for DIRECTORY_SEPARATOR at the end (and remove it!) + if (substr($resolved, -1) === DIRECTORY_SEPARATOR) + { + return substr($resolved, 0, -1); + } + + return $resolved; + } + + /** + * A wrapper for PHP's realpath + * + * Try to resolve realpath when PHP's realpath is not available, or + * known to be buggy. + * + * @param string $path Path to resolve + * + * @return string Resolved path + */ + public static function realpath($path) + { + if (!function_exists('realpath')) + { + return self::phpbb_own_realpath($path); + } + + $realpath = realpath($path); + + // Strangely there are provider not disabling realpath but returning strange values. :o + // We at least try to cope with them. + if ((!self::is_absolute_path($path) && $realpath === $path) || $realpath === false) + { + return self::phpbb_own_realpath($path); + } + + // Check for DIRECTORY_SEPARATOR at the end (and remove it!) + if (substr($realpath, -1) === DIRECTORY_SEPARATOR) + { + $realpath = substr($realpath, 0, -1); + } + + return $realpath; + } + + /** + * Given an existing path, convert it to a path relative to a given starting path + * + * @param string $end_path Absolute path of target + * @param string $start_path Absolute path where traversal begins + * + * @return string Path of target relative to starting path + */ + public static function make_path_relative($end_path, $start_path) + { + $symfony_filesystem = new \Symfony\Component\Filesystem\Filesystem(); + return $symfony_filesystem->makePathRelative($end_path, $start_path); + } + + /** + * Try to resolve symlinks in path + * + * @param string $path The path to resolve + * @param string $prefix The path prefix (on windows the drive letter) + * @param bool $absolute Whether or not the path is absolute + * @param bool $return_array Whether or not to return path parts + * + * @return string|array|bool returns the resolved path or an array of parts of the path if $return_array is true + * or false if path cannot be resolved + */ + protected static function resolve_path($path, $prefix = '', $absolute = false, $return_array = false) + { + if ($return_array) + { + $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); + } + + trim ($path, '/'); + $path_parts = explode('/', $path); + $resolved = array(); + $resolved_path = $prefix; + $file_found = false; + + foreach ($path_parts as $path_part) + { + if ($file_found) + { + return false; + } + + if (empty($path_part) || ($path_part === '.' && ($absolute || !empty($resolved)))) + { + continue; + } + else if ($absolute && $path_part === '..') + { + if (empty($resolved)) + { + // No directories above root + return false; + } + + array_pop($resolved); + $resolved_path = false; + } + else if ($path_part === '..' && !empty($resolved) && !in_array($resolved[sizeof($resolved) - 1], array('.', '..'))) + { + array_pop($resolved); + $resolved_path = false; + } + else + { + if ($resolved_path === false) + { + if (empty($resolved)) + { + $resolved_path = ($absolute) ? $prefix . '/' . $path_part : $path_part; + } + else + { + $tmp_array = $resolved; + if ($absolute) + { + array_unshift($tmp_array, $prefix); + } + + $resolved_path = implode('/', $tmp_array); + } + } + + $current_path = $resolved_path . '/' . $path_part; + + // Resolve symlinks + if (is_link($current_path)) + { + if (!function_exists('readlink')) + { + return false; + } + + $link = readlink($current_path); + + // Is link has an absolute path in it? + if (self::is_absolute_path($link)) + { + if (defined('PHP_WINDOWS_VERSION_MAJOR')) + { + $prefix = $link[0] . ':'; + $link = substr($link, 2); + } + else + { + $prefix = ''; + } + + $resolved = self::resolve_path($link, $prefix, true, true); + $absolute = true; + } + else + { + $resolved = self::resolve_path($resolved_path . '/' . $link, $prefix, $absolute, true); + } + + if (!$resolved) + { + return false; + } + + $resolved_path = false; + } + else if (is_dir($current_path . '/')) + { + $resolved[] = $path_part; + $resolved_path = $current_path; + } + else if (is_file($current_path)) + { + $resolved[] = $path_part; + $resolved_path = $current_path; + $file_found = true; + } + else + { + return false; + } + } + } + + // If at the end of the path there were a .. or . + // we need to build the path again. + // Only doing this when a string is expected in return + if ($resolved_path === false && $return_array === false) + { + if (empty($resolved)) + { + $resolved_path = ($absolute) ? $prefix . '/' : './'; + } + else + { + $tmp_array = $resolved; + if ($absolute) + { + array_unshift($tmp_array, $prefix); + } + + $resolved_path = implode('/', $tmp_array); + } + } + + return ($return_array) ? $resolved : $resolved_path; + } +} diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php new file mode 100644 index 0000000000..dc52004b3a --- /dev/null +++ b/phpBB/phpbb/storage/storage.php @@ -0,0 +1,66 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\storage; + +use phpbb\storage\exception\exception; + +class storage +{ + protected $adapter; + + public function __construct($adapter) + { + $this->adapter = $adapter; + } + + public function put_contents($path, $content) + { + $this->adapter->put_contents($path, $content); + } + + public function get_contents($path) + { + $this->adapter->put_contents($path); + } + + public function exists($path) + { + return $this->adapter->exists($path); + } + + public function delete($path) + { + $this->adapter->delete($path); + } + + public function rename($path_orig, $path_dest) + { + $this->adapter->rename($path_orig, $path_dest); + } + + public function copy($path_orig, $path_dest) + { + $this->adapter->copy($path_orig, $path_dest); + } + + public function create_dir($path) + { + $this->adapter->create_dir($path); + } + + public function delete_dir($path) + { + $this->adapter->delete_dir($path); + } +} diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php new file mode 100644 index 0000000000..ecc06cf688 --- /dev/null +++ b/tests/storage/adapter/local_test.php @@ -0,0 +1,86 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + + class phpbb_storage_adapter_local_test extends phpbb_test_case + { + protected $adapter; + + public function setUp() + { + parent::setUp(); + $this->adapter = new \phpbb\storage\adapter\local(); + } + + public function tearDown() + { + $this->adapter = null; + } + + public function test_put_contents() + { + $this->adapter->put_contents('file.txt', 'abc'); + $this->assertTrue(file_exists('file.txt')); + $this->assertEquals(file_get_contents('file.txt'), 'abc'); + unlink('file.txt'); + } + + public function test_get_contents() + { + file_put_contents('file.txt', 'abc'); + $this->assertEquals($this->adapter->get_contents('file.txt'), 'abc'); + unlink('file.txt'); + } + + public function test_exists() + { + // Exists with files + $this->assertTrue($this->adapter->exists(__DIR__.'/local_test.php')); + $this->assertFalse($this->adapter->exists(__DIR__.'/nonexistent_file.php')); + // exists with directory + $this->assertTrue($this->adapter->exists(__DIR__.'/../adapter')); + $this->assertFalse($this->adapter->exists(__DIR__.'/../nonexistet_folder')); + } + + public function test_delete() + { + // Delete with files + file_put_contents('file.txt', ''); + $this->assertTrue(file_exists('file.txt')); + $this->adapter->delete('file.txt'); + $this->assertFalse(file_exists('file.txt')); + // Delete with directories + mkdir('path/to/dir', 0777, true); + $this->assertTrue(file_exists('path/to/dir')); + $this->adapter->delete('path'); + $this->assertFalse(file_exists('path/to/dir')); + } + + public function test_rename() + { + file_put_contents('file.txt', ''); + $this->adapter->rename('file.txt', 'file2.txt'); + $this->assertFalse(file_exists('file.txt')); + $this->assertTrue(file_exists('file2.txt')); + unlink('file2.txt'); + } + + public function test_copy() + { + file_put_contents('file.txt', 'abc'); + $this->adapter->copy('file.txt', 'file2.txt'); + $this->assertEquals(file_get_contents('file.txt'), 'abc'); + $this->assertEquals(file_get_contents('file.txt'), 'abc'); + unlink('file.txt'); + unlink('file2.txt'); + } + } diff --git a/tests/storage/helper_clean_path_test.php b/tests/storage/helper_clean_path_test.php new file mode 100644 index 0000000000..ab9fabd0cb --- /dev/null +++ b/tests/storage/helper_clean_path_test.php @@ -0,0 +1,52 @@ + +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +class phpbb_storage_helper_clean_path_test extends phpbb_test_case +{ + + public function setUp() + { + parent::setUp(); + } + + public function clean_path_data() + { + return array( + array('foo', 'foo'), + array('foo/bar', 'foo/bar'), + array('foo/bar/', 'foo/bar/'), + array('foo/./bar', 'foo/bar'), + array('foo/./././bar', 'foo/bar'), + array('foo/bar/.', 'foo/bar'), + array('./foo/bar', './foo/bar'), + array('../foo/bar', '../foo/bar'), + array('./../foo/bar', './../foo/bar'), + array('././../foo/bar', './../foo/bar'), + array('one/two/three', 'one/two/three'), + array('one/two/../three', 'one/three'), + array('one/../two/three', 'two/three'), + array('one/two/..', 'one'), + array('one/two/../', 'one/'), + array('one/two/../three/../four', 'one/four'), + array('one/two/three/../../four', 'one/four'), + ); + } + + /** + * @dataProvider clean_path_data + */ + public function test_clean_path($input, $expected) + { + $this->assertEquals($expected, \phpbb\storage\helper::clean_path($input)); + } +} diff --git a/tests/storage/helper_is_absolute_test.php b/tests/storage/helper_is_absolute_test.php new file mode 100644 index 0000000000..05a048cb91 --- /dev/null +++ b/tests/storage/helper_is_absolute_test.php @@ -0,0 +1,64 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_storage_helper_is_absolute_test extends phpbb_test_case +{ + + public function setUp() + { + parent::setUp(); + } + + static public function is_absolute_data() + { + return array( + // Empty + array('', false), + + // Absolute unix style + array('/etc/phpbb', true), + // Unix does not support \ so that is not an absolute path + array('\etc\phpbb', false), + + // Absolute windows style + array('c:\windows', true), + array('C:\Windows', true), + array('c:/windows', true), + array('C:/Windows', true), + + // Executable + array('etc/phpbb', false), + array('explorer.exe', false), + + // Relative subdir + array('Windows\System32', false), + array('Windows\System32\explorer.exe', false), + array('Windows/System32', false), + array('Windows/System32/explorer.exe', false), + + // Relative updir + array('..\Windows\System32', false), + array('..\Windows\System32\explorer.exe', false), + array('../Windows/System32', false), + array('../Windows/System32/explorer.exe', false), + ); + } + + /** + * @dataProvider is_absolute_data + */ + public function test_is_absolute($path, $expected) + { + $this->assertEquals($expected, \phpbb\storage\helper::is_absolute_path($path)); + } +} diff --git a/tests/storage/helper_realpath_test.php b/tests/storage/helper_realpath_test.php new file mode 100644 index 0000000000..1cc5e4b1ce --- /dev/null +++ b/tests/storage/helper_realpath_test.php @@ -0,0 +1,83 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_storage_helper_realpath_test extends phpbb_test_case +{ + protected static $storage_helper_phpbb_own_realpath; + + static public function setUpBeforeClass() + { + parent::setUpBeforeClass(); + + self::$storage_helper_phpbb_own_realpath = new ReflectionMethod('\phpbb\storage\helper', 'phpbb_own_realpath'); + self::$storage_helper_phpbb_own_realpath->setAccessible(true); + } + + public function setUp() + { + parent::setUp(); + } + + public function realpath_resolve_absolute_without_symlinks_data() + { + return array( + // Constant data + array(__DIR__, __DIR__), + array(__DIR__ . '/../storage/../storage', __DIR__), + array(__DIR__ . '/././', __DIR__), + array(__DIR__ . '/non_existent', false), + + array(__FILE__, __FILE__), + array(__FILE__ . '../', false), + ); + } + + public function realpath_resolve_relative_without_symlinks_data() + { + if (!function_exists('getcwd')) + { + return array(); + } + + $relative_path = \phpbb\storage\helper::make_path_relative(__DIR__, getcwd()); + + return array( + array($relative_path, __DIR__), + array($relative_path . '../storage/../storage', __DIR__), + array($relative_path . '././', __DIR__), + + array($relative_path . 'helper_realpath_test.php', __FILE__), + ); + } + + /** + * @dataProvider realpath_resolve_absolute_without_symlinks_data + */ + public function test_realpath_absolute_without_links($path, $expected) + { + $this->assertEquals($expected, self::$storage_helper_phpbb_own_realpath->invoke(null, $path)); + } + + /** + * @dataProvider realpath_resolve_relative_without_symlinks_data + */ + public function test_realpath_relative_without_links($path, $expected) + { + if (!function_exists('getcwd')) + { + $this->markTestSkipped('phpbb_own_realpath() cannot be tested with relative paths: getcwd is not available.'); + } + + $this->assertEquals($expected, self::$storage_helper_phpbb_own_realpath->invoke(null, $path)); + } +}