diff --git a/phpBB/config/default/container/services.yml b/phpBB/config/default/container/services.yml index 2f5a4bd5a6..950b8ae295 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_files.yml b/phpBB/config/default/container/services_files.yml index ba1fdb4c9a..96682d6713 100644 --- a/phpBB/config/default/container/services_files.yml +++ b/phpBB/config/default/container/services_files.yml @@ -16,6 +16,16 @@ services: - '@mimetype.guesser' - '@plupload' + files.filespec_storage: + class: phpbb\files\filespec_storage + shared: false + arguments: + - '@language' + - '@php_ini' + - '@upload_imagesize' + - '@mimetype.guesser' + - '@plupload' + files.upload: class: phpbb\files\upload shared: false @@ -36,6 +46,16 @@ services: - '@plupload' - '@request' + files.types.form_storage: + class: phpbb\files\types\form_storage + shared: false + arguments: + - '@files.factory' + - '@language' + - '@php_ini' + - '@plupload' + - '@request' + files.types.local: class: phpbb\files\types\local shared: false @@ -55,3 +75,14 @@ services: - '@php_ini' - '@request' - '%core.root_path%' + + files.types.remote_storage: + class: phpbb\files\types\remote_storage + shared: false + arguments: + - '@config' + - '@files.factory' + - '@language' + - '@php_ini' + - '@request' + - '%core.root_path%' diff --git a/phpBB/config/default/container/services_storage.yml b/phpBB/config/default/container/services_storage.yml new file mode 100644 index 0000000000..abf51d5f97 --- /dev/null +++ b/phpBB/config/default/container/services_storage.yml @@ -0,0 +1,40 @@ +services: +# Factory + storage.adapter.factory: + class: phpbb\storage\adapter_factory + arguments: + - '@config' + - '@storage.adapter_collection' + - '@storage.provider_collection' + +# Collections + storage.adapter_collection: + class: phpbb\di\service_collection + arguments: + - '@service_container' + tags: + - { name: service_collection, tag: storage.adapter, class_name_aware: true } + + storage.provider_collection: + class: phpbb\di\service_collection + arguments: + - '@service_container' + tags: + - { name: service_collection, tag: storage.provider, class_name_aware: true } + +# Adapters + storage.adapter.local: + class: phpbb\storage\adapter\local + shared: false + arguments: + - '@filesystem' + - '%core.root_path%' + tags: + - { name: storage.adapter } + +# Providers + storage.provider.local: + class: phpbb\storage\provider\local + arguments: + tags: + - { name: storage.provider } diff --git a/phpBB/language/en/common.php b/phpBB/language/en/common.php index 1414f0fd1a..006582772a 100644 --- a/phpBB/language/en/common.php +++ b/phpBB/language/en/common.php @@ -727,6 +727,7 @@ $lang = array_merge($lang, array( 'SUBJECT' => 'Subject', 'SUBMIT' => 'Submit', + 'STORAGE_ADAPTER_NOT_AVAILABLE' => 'Selected storage is not available.', 'STORAGE_FILE_EXISTS' => 'File already exists.', 'STORAGE_FILE_NO_EXIST' => 'File does not exist.', 'STORAGE_CANNOT_WRITE_FILE' => 'Can not write to file.', diff --git a/phpBB/phpbb/files/filespec_storage.php b/phpBB/phpbb/files/filespec_storage.php new file mode 100644 index 0000000000..b4042ebf10 --- /dev/null +++ b/phpBB/phpbb/files/filespec_storage.php @@ -0,0 +1,510 @@ + + * @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\files; + +use phpbb\language\language; + +/** + * Responsible for holding all file relevant information, as well as doing file-specific operations. + * The {@link fileupload fileupload class} can be used to upload several files, each of them being this object to operate further on. + */ +class filespec_storage +{ + /** @var string File name */ + protected $filename = ''; + + /** @var string Real name of file */ + protected $realname = ''; + + /** @var string Upload name of file */ + protected $uploadname = ''; + + /** @var string Mimetype of file */ + protected $mimetype = ''; + + /** @var string File extension */ + protected $extension = ''; + + /** @var int File size */ + protected $filesize = 0; + + /** @var int Width of file */ + protected $width = 0; + + /** @var int Height of file */ + protected $height = 0; + + /** @var array Image info including type and size */ + protected $image_info = array(); + + /** @var string Destination file name */ + protected $destination_file = ''; + + /** @var string Destination file path */ + protected $destination_path = ''; + + /** @var bool Whether file was moved */ + protected $file_moved = false; + + /** @var bool Whether file is local */ + protected $local = false; + + /** @var bool Class initialization flag */ + protected $class_initialized = false; + + /** @var array Error array */ + public $error = array(); + + /** @var upload Instance of upload class */ + public $upload; + + /** @var \bantu\IniGetWrapper\IniGetWrapper ini_get() wrapper class */ + protected $php_ini; + + /** @var \FastImageSize\FastImageSize */ + protected $imagesize; + + /** @var language Language class */ + protected $language; + + /** @var \phpbb\plupload\plupload The plupload object */ + protected $plupload; + + /** @var \phpbb\mimetype\guesser phpBB Mimetype guesser */ + protected $mimetype_guesser; + + /** + * File upload class + * + * @param language $language Language + * @param \bantu\IniGetWrapper\IniGetWrapper $php_ini ini_get() wrapper + * @param \FastImageSize\FastImageSize $imagesize Imagesize class + * @param \phpbb\mimetype\guesser $mimetype_guesser Mime type guesser + * @param \phpbb\plupload\plupload $plupload Plupload + */ + public function __construct(language $language, \bantu\IniGetWrapper\IniGetWrapper $php_ini, \FastImageSize\FastImageSize $imagesize, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null) + { + $this->language = $language; + $this->php_ini = $php_ini; + $this->imagesize = $imagesize; + $this->plupload = $plupload; + $this->mimetype_guesser = $mimetype_guesser; + } + + /** + * Set upload ary + * + * @param array $upload_ary Upload ary + * + * @return filespec This instance of the filespec class + */ + public function set_upload_ary($upload_ary) + { + if (!isset($upload_ary) || !sizeof($upload_ary)) + { + return $this; + } + + $this->class_initialized = true; + $this->filename = $upload_ary['tmp_name']; + $this->filesize = $upload_ary['size']; + $name = $upload_ary['name']; + $name = trim(utf8_basename($name)); + $this->realname = $this->uploadname = $name; + $this->mimetype = $upload_ary['type']; + + // Opera adds the name to the mime type + $this->mimetype = (strpos($this->mimetype, '; name') !== false) ? str_replace(strstr($this->mimetype, '; name'), '', $this->mimetype) : $this->mimetype; + + if (!$this->mimetype) + { + $this->mimetype = 'application/octet-stream'; + } + + $this->extension = strtolower(self::get_extension($this->realname)); + + // Try to get real filesize from temporary folder (not always working) ;) + $this->filesize = ($this->get_filesize($this->filename)) ?: $this->filesize; + + $this->width = $this->height = 0; + $this->file_moved = false; + + $this->local = (isset($upload_ary['local_mode'])) ? true : false; + + return $this; + } + + /** + * Set the upload namespace + * + * @param upload $namespace Instance of upload class + * + * @return filespec This instance of the filespec class + */ + public function set_upload_namespace($namespace) + { + $this->upload = $namespace; + + return $this; + } + + /** + * Check if class members were not properly initialised yet + * + * @return bool True if there was an init error, false if not + */ + public function init_error() + { + return !$this->class_initialized; + } + + /** + * Set error in error array + * + * @param mixed $error Content for error array + * + * @return \phpbb\files\filespec This instance of the filespec class + */ + public function set_error($error) + { + $this->error[] = $error; + + return $this; + } + + /** + * Cleans destination filename + * + * @param string $mode Either real, unique, or unique_ext. Real creates a + * realname, filtering some characters, lowering every + * character. Unique creates a unique filename. + * @param string $prefix Prefix applied to filename + * @param string $user_id The user_id is only needed for when cleaning a user's avatar + */ + public function clean_filename($mode = 'unique', $prefix = '', $user_id = '') + { + if ($this->init_error()) + { + return; + } + + switch ($mode) + { + case 'real': + // Remove every extension from filename (to not let the mime bug being exposed) + if (strpos($this->realname, '.') !== false) + { + $this->realname = substr($this->realname, 0, strpos($this->realname, '.')); + } + + // Replace any chars which may cause us problems with _ + $bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|'); + + $this->realname = rawurlencode(str_replace($bad_chars, '_', strtolower($this->realname))); + $this->realname = preg_replace("/%(\w{2})/", '_', $this->realname); + + $this->realname = $prefix . $this->realname . '.' . $this->extension; + break; + + case 'unique': + $this->realname = $prefix . md5(unique_id()); + break; + + case 'avatar': + $this->extension = strtolower($this->extension); + $this->realname = $prefix . $user_id . '.' . $this->extension; + + break; + + case 'unique_ext': + default: + $this->realname = $prefix . md5(unique_id()) . '.' . $this->extension; + } + } + + /** + * Get property from file object + * + * @param string $property Name of property + * + * @return mixed Content of property + */ + public function get($property) + { + if ($this->init_error() || !isset($this->$property)) + { + return false; + } + + return $this->$property; + } + + /** + * Check if file is an image (mime type) + * + * @return bool true if it is an image, false if not + */ + public function is_image() + { + return (strpos($this->mimetype, 'image/') === 0); + } + + /** + * Check if the file got correctly uploaded + * + * @return bool true if it is a valid upload, false if not + */ + public function is_uploaded() + { + $is_plupload = $this->plupload && $this->plupload->is_active(); + + if (!$this->local && !$is_plupload && !is_uploaded_file($this->filename)) + { + return false; + } + + if (($this->local || $is_plupload) && !file_exists($this->filename)) + { + return false; + } + + return true; + } + + /** + * Remove file + */ + public function remove($storage) + { + if ($this->file_moved) + { + $storage->delete($this->destination_file); + } + } + + /** + * Get file extension + * + * @param string $filename Filename that needs to be checked + * + * @return string Extension of the supplied filename + */ + static public function get_extension($filename) + { + $filename = utf8_basename($filename); + + if (strpos($filename, '.') === false) + { + return ''; + } + + $filename = explode('.', $filename); + return array_pop($filename); + } + + /** + * Get mime type + * + * @param string $filename Filename that needs to be checked + * @return string Mime type of supplied filename + */ + public function get_mimetype($filename) + { + if ($this->mimetype_guesser !== null) + { + $mimetype = $this->mimetype_guesser->guess($filename, $this->uploadname); + + if ($mimetype !== 'application/octet-stream') + { + $this->mimetype = $mimetype; + } + } + + return $this->mimetype; + } + + /** + * Get file size + * + * @param string $filename File name of file to check + * + * @return int File size + */ + public function get_filesize($filename) + { + return @filesize($filename); + } + + + /** + * Check the first 256 bytes for forbidden content + * + * @param array $disallowed_content Array containg disallowed content + * + * @return bool False if disallowed content found, true if not + */ + public function check_content($disallowed_content) + { + if (empty($disallowed_content)) + { + return true; + } + + $fp = @fopen($this->filename, 'rb'); + + if ($fp !== false) + { + $ie_mime_relevant = fread($fp, 256); + fclose($fp); + foreach ($disallowed_content as $forbidden) + { + if (stripos($ie_mime_relevant, '<' . $forbidden) !== false) + { + return false; + } + } + } + return true; + } + + /** + * Move file to destination folder + * + * @param bool $overwrite If set to true, an already existing file will be overwritten + * @param bool $skip_image_check If set to true, the check for the file to be a valid image is skipped + * + * @return bool True if file was moved, false if not + * @access public + */ + public function move_file($storage, $overwrite = false, $skip_image_check = false) + { + if (sizeof($this->error)) + { + return false; + } + + $this->destination_file = utf8_basename($this->realname); + + // Try to get real filesize from destination folder + $this->filesize = ($this->get_filesize($this->filename)) ?: $this->filesize; + + // Get mimetype of supplied file + $this->mimetype = $this->get_mimetype($this->filename); + + if ($this->is_image() && !$skip_image_check) + { + $this->width = $this->height = 0; + + $this->image_info = $this->imagesize->getImageSize($this->filename, $this->mimetype); + + if ($this->image_info !== false) + { + $this->width = $this->image_info['width']; + $this->height = $this->image_info['height']; + + // Check image type + $types = upload::image_types(); + + if (!isset($types[$this->image_info['type']]) || !in_array($this->extension, $types[$this->image_info['type']])) + { + if (!isset($types[$this->image_info['type']])) + { + $this->error[] = $this->language->lang('IMAGE_FILETYPE_INVALID', $this->image_info['type'], $this->mimetype); + } + else + { + $this->error[] = $this->language->lang('IMAGE_FILETYPE_MISMATCH', $types[$this->image_info['type']][0], $this->extension); + } + } + + // Make sure the dimensions match a valid image + if (empty($this->width) || empty($this->height)) + { + $this->error[] = $this->language->lang('ATTACHED_IMAGE_NOT_IMAGE'); + } + } + else + { + $this->error[] = $this->language->lang('UNABLE_GET_IMAGE_SIZE'); + } + } + + if ($overwrite && $storage->exists($this->destination_file)) + { + $storage->delete($this->destination_file); + } + + try + { + $storage->put_contents($this->destination_file, file_get_contents($this->filename)); + } + catch (\phpbb\storage\exception\exception $e) + { + $this->error[] = $this->language->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); + $this->file_moved = false; + } + + // Remove temporary filename + @unlink($this->filename); + + if (sizeof($this->error)) + { + return false; + } + + $this->file_moved = true; + $this->additional_checks(); + unset($this->upload); + + return true; + } + + /** + * Performing additional checks + * + * @return bool False if issue was found, true if not + */ + public function additional_checks() + { + if (!$this->file_moved) + { + return false; + } + + // Filesize is too big or it's 0 if it was larger than the maxsize in the upload form + if ($this->upload->max_filesize && ($this->get('filesize') > $this->upload->max_filesize || $this->filesize == 0)) + { + $max_filesize = get_formatted_filesize($this->upload->max_filesize, false); + + $this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_FILESIZE', $max_filesize['value'], $max_filesize['unit']); + + return false; + } + + if (!$this->upload->valid_dimensions($this)) + { + $this->error[] = $this->language->lang($this->upload->error_prefix . 'WRONG_SIZE', + $this->language->lang('PIXELS', (int) $this->upload->min_width), + $this->language->lang('PIXELS', (int) $this->upload->min_height), + $this->language->lang('PIXELS', (int) $this->upload->max_width), + $this->language->lang('PIXELS', (int) $this->upload->max_height), + $this->language->lang('PIXELS', (int) $this->width), + $this->language->lang('PIXELS', (int) $this->height)); + + return false; + } + + return true; + } +} diff --git a/phpBB/phpbb/storage/adapter/adapter_interface.php b/phpBB/phpbb/storage/adapter/adapter_interface.php index 70eb6f1cc7..207f001fad 100644 --- a/phpBB/phpbb/storage/adapter/adapter_interface.php +++ b/phpBB/phpbb/storage/adapter/adapter_interface.php @@ -15,6 +15,13 @@ namespace phpbb\storage\adapter; interface adapter_interface { + /** + * Set adapter parameters + * + * @param array options Storage-specific options. + */ + public function configure($options); + /** * Dumps content into a file. * @@ -77,13 +84,4 @@ interface adapter_interface * When the file cannot be copied */ public function copy($path_orig, $path_dest); - - /** - * Creates a directory recursively. - * - * @param string $path The directory path - * - * @throws \phpbb\storage\exception\exception On any directory creation failure - */ - public function create_dir($path); } diff --git a/phpBB/phpbb/storage/adapter/local.php b/phpBB/phpbb/storage/adapter/local.php index 68b9ce0c2c..a8498d6d8c 100644 --- a/phpBB/phpbb/storage/adapter/local.php +++ b/phpBB/phpbb/storage/adapter/local.php @@ -15,8 +15,8 @@ namespace phpbb\storage\adapter; use phpbb\storage\exception\exception; use phpbb\filesystem\exception\filesystem_exception; -use phpbb\config\config; use phpbb\filesystem\filesystem; +use phpbb\filesystem\helper as filesystem_helper; /** * @internal Experimental @@ -30,18 +30,33 @@ class local implements adapter_interface */ protected $filesystem; - /** @var string path */ + /** + * @var string path + */ + protected $phpbb_root_path; + + /** + * @var string path + */ protected $root_path; /** * Constructor */ - public function __construct(config $config, filesystem $filesystem, $phpbb_root_path, $path_key) + public function __construct(filesystem $filesystem, $phpbb_root_path) { $this->filesystem = $filesystem; - $this->root_path = $phpbb_root_path . $config[$path_key]; + $this->phpbb_root_path = $phpbb_root_path; + } - if (substr($this->root_path, -1, 1) != DIRECTORY_SEPARATOR) + /** + * {@inheritdoc} + */ + public function configure($options) + { + $this->root_path = $this->phpbb_root_path . $options['path']; + + if (substr($this->root_path, -1, 1) !== DIRECTORY_SEPARATOR) { $this->root_path = $this->root_path . DIRECTORY_SEPARATOR; } @@ -52,6 +67,8 @@ class local implements adapter_interface */ public function put_contents($path, $content) { + $this->ensure_directory_exists($path); + if ($this->exists($path)) { throw new exception('STORAGE_FILE_EXISTS', $path); @@ -115,6 +132,8 @@ class local implements adapter_interface */ public function rename($path_orig, $path_dest) { + $this->ensure_directory_exists($path_dest); + try { $this->filesystem->rename($this->root_path . $path_orig, $this->root_path . $path_dest, false); @@ -130,6 +149,8 @@ class local implements adapter_interface */ public function copy($path_orig, $path_dest) { + $this->ensure_directory_exists($path_dest); + try { $this->filesystem->copy($this->root_path . $path_orig, $this->root_path . $path_dest, false); @@ -141,9 +162,13 @@ class local implements adapter_interface } /** - * {@inheritdoc} + * Creates a directory recursively. + * + * @param string $path The directory path + * + * @throws \phpbb\storage\exception\exception On any directory creation failure */ - public function create_dir($path) + protected function create_dir($path) { try { @@ -155,4 +180,19 @@ class local implements adapter_interface } } + /** + * Ensures that the directory of a file exists. + * + * @param string $path The file path + */ + protected function ensure_directory_exists($path) + { + $path = dirname($this->root_path . $path); + $path = filesystem_helper::make_path_relative($path, $this->root_path); + + if (!$this->exists($path)) + { + $this->create_dir($path); + } + } } diff --git a/phpBB/phpbb/storage/adapter_factory.php b/phpBB/phpbb/storage/adapter_factory.php new file mode 100644 index 0000000000..4b27269524 --- /dev/null +++ b/phpBB/phpbb/storage/adapter_factory.php @@ -0,0 +1,93 @@ + + * @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\config\config; +use phpbb\di\service_collection; +use phpbb\storage\exception\exception; + +class adapter_factory +{ + /** + * @var \phpbb\config\config + */ + protected $config; + + /** + * @var \phpbb\di\service_collection + */ + protected $adapters; + + /** + * @var \phpbb\di\service_collection + */ + protected $providers; + + /** + * Constructor + * + * @param \phpbb\config\config $config + * @param \phpbb\di\service_collection $adapters + * @param \phpbb\di\service_collection $providers + */ + public function __construct(config $config, service_collection $adapters, service_collection $providers) + { + $this->config = $config; + $this->adapters = $adapters; + $this->providers = $providers; + } + + /** + * Obtains a configured adapters for a given storage + * + * @param string $storage_name + * + * @return \phpbb\storage\adapter\adapter_interface + */ + public function get($storage_name) + { + $provider_class = $this->config['storage\\' . $storage_name . '\\provider']; + $provider = $this->providers->get_by_class($provider_class); + + if (!$provider->is_available()) + { + throw new exception('STORAGE_ADAPTER_NOT_AVAILABLE'); + } + + $adapter = $this->adapters->get_by_class($provider->get_adapter_class()); + $adapter->configure($this->build_options($storage_name, $provider->get_options())); + + return $adapter; + } + + /** + * Obtains configuration for a given storage + * + * @param string $storage_name + * @param array $definitions + * + * @return array Returns storage configuration values + */ + public function build_options($storage_name, array $definitions) + { + $options = []; + + foreach ($definitions as $def) + { + $options[$def] = $this->config['storage\\' . $storage_name . '\\config\\' . $def]; + } + + return $options; + } +} diff --git a/phpBB/phpbb/storage/provider/local.php b/phpBB/phpbb/storage/provider/local.php new file mode 100644 index 0000000000..370178cf47 --- /dev/null +++ b/phpBB/phpbb/storage/provider/local.php @@ -0,0 +1,41 @@ + + * @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\provider; + +class local implements provider_interface +{ + /** + * {@inheritdoc} + */ + public function get_adapter_class() + { + return \phpbb\storage\adapter\local::class; + } + + /** + * {@inheritdoc} + */ + public function get_options() + { + return ['path']; + } + + /** + * {@inheritdoc} + */ + public function is_available() + { + return true; + } +} diff --git a/phpBB/phpbb/storage/provider/provider_interface.php b/phpBB/phpbb/storage/provider/provider_interface.php new file mode 100644 index 0000000000..d2e78d907f --- /dev/null +++ b/phpBB/phpbb/storage/provider/provider_interface.php @@ -0,0 +1,38 @@ + + * @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\provider; + +interface provider_interface +{ + /** + * Gets adapter class. + * + * @return \phpbb\storage\adapter\adapter_interface + */ + public function get_adapter_class(); + + /** + * Gets adapter options. + * + * @return array Configuration keys + */ + public function get_options(); + + /** + * Return true if the adapter is available. + * + * @return bool + */ + public function is_available(); +} diff --git a/phpBB/phpbb/storage/storage.php b/phpBB/phpbb/storage/storage.php index f532f4d95f..65a4f6eb72 100644 --- a/phpBB/phpbb/storage/storage.php +++ b/phpBB/phpbb/storage/storage.php @@ -18,45 +18,126 @@ namespace phpbb\storage; */ class storage { + /** + * @var string + */ + protected $storage_name; + + /** + * @var \phpbb\storage\adapter_factory + */ + protected $factory; + + /** + * @var \phpbb\storage\adapter\adapter_interface + */ protected $adapter; - public function __construct($adapter) + /** + * Constructor + * + * @param \phpbb\storage\adapter_factory $factory + * @param string $storage_name + */ + public function __construct(adapter_factory $factory, $storage_name) { - $this->adapter = $adapter; + $this->factory = $factory; + $this->storage_name = $storage_name; } + /** + * Returns an adapter instance + * + * @return \phpbb\storage\adapter\adapter_interface + */ + protected function get_adapter() + { + if ($this->adapter === null) + { + $this->adapter = $this->factory->get($this->storage_name); + } + + return $this->adapter; + } + + /** + * 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) { - $this->adapter->put_contents($path, $content); + $this->get_adapter()->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 + * @return string Returns file contents + * + */ public function get_contents($path) { - return $this->adapter->get_contents($path); + return $this->get_adapter()->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) { - return $this->adapter->exists($path); + return $this->get_adapter()->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) { - $this->adapter->delete($path); + $this->get_adapter()->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) { - $this->adapter->rename($path_orig, $path_dest); + $this->get_adapter()->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) { - $this->adapter->copy($path_orig, $path_dest); - } - - public function create_dir($path) - { - $this->adapter->create_dir($path); + $this->get_adapter()->copy($path_orig, $path_dest); } } diff --git a/tests/storage/adapter/local_test.php b/tests/storage/adapter/local_test.php index 42b4c5929b..a2725cd387 100644 --- a/tests/storage/adapter/local_test.php +++ b/tests/storage/adapter/local_test.php @@ -13,47 +13,51 @@ class phpbb_storage_adapter_local_test extends phpbb_test_case { - protected $adapter; + protected $adapter; + + protected $path; public function setUp() { parent::setUp(); - $config = new \phpbb\config\config(array( - 'test_path' => '.', - )); $filesystem = new \phpbb\filesystem\filesystem(); $phpbb_root_path = getcwd() . DIRECTORY_SEPARATOR; - $path_key = 'test_path'; - $this->adapter = new \phpbb\storage\adapter\local($config, $filesystem, $phpbb_root_path, $path_key); + + $this->adapter = new \phpbb\storage\adapter\local($filesystem, $phpbb_root_path); + $this->adapter->configure(['path' => 'test_path']); + + $this->path = $phpbb_root_path . 'test_path/'; + mkdir($this->path); } public function data_test_exists() { - yield ['README.md', true]; - yield ['nonexistent_file.php', false]; - yield ['phpBB/phpbb', true]; - yield ['nonexistent/folder', false]; + yield [$this->path . '../README.md', true]; + yield [$this->path . 'nonexistent_file.php', false]; + yield [$this->path . '../phpBB/phpbb', true]; + yield [$this->path . 'nonexistent/folder', false]; } public function tearDown() { $this->adapter = null; + rmdir($this->path); } 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'); + $this->assertTrue(file_exists($this->path . 'file.txt')); + $this->assertEquals(file_get_contents($this->path . 'file.txt'), 'abc'); + unlink($this->path . 'file.txt'); } public function test_get_contents() { - file_put_contents('file.txt', 'abc'); + file_put_contents($this->path . 'file.txt', 'abc'); $this->assertEquals($this->adapter->get_contents('file.txt'), 'abc'); - unlink('file.txt'); + unlink($this->path . 'file.txt'); } /** @@ -66,37 +70,37 @@ public function test_delete_file() { - file_put_contents('file.txt', ''); - $this->assertTrue(file_exists('file.txt')); + file_put_contents($this->path . 'file.txt', ''); + $this->assertTrue(file_exists($this->path . 'file.txt')); $this->adapter->delete('file.txt'); - $this->assertFalse(file_exists('file.txt')); + $this->assertFalse(file_exists($this->path . 'file.txt')); } public function test_delete_folder() { - mkdir('path/to/dir', 0777, true); - $this->assertTrue(file_exists('path/to/dir')); + mkdir($this->path . 'path/to/dir', 0777, true); + $this->assertTrue(file_exists($this->path . 'path/to/dir')); $this->adapter->delete('path'); - $this->assertFalse(file_exists('path/to/dir')); + $this->assertFalse(file_exists($this->path . 'path/to/dir')); } public function test_rename() { - file_put_contents('file.txt', ''); + file_put_contents($this->path . 'file.txt', ''); $this->adapter->rename('file.txt', 'file2.txt'); - $this->assertFalse(file_exists('file.txt')); - $this->assertTrue(file_exists('file2.txt')); - unlink('file2.txt'); + $this->assertFalse(file_exists($this->path . 'file.txt')); + $this->assertTrue(file_exists($this->path . 'file2.txt')); + unlink($this->path . 'file2.txt'); } public function test_copy() { - file_put_contents('file.txt', 'abc'); + file_put_contents($this->path . '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'); + $this->assertEquals(file_get_contents($this->path . 'file.txt'), 'abc'); + $this->assertEquals(file_get_contents($this->path . 'file.txt'), 'abc'); + unlink($this->path . 'file.txt'); + unlink($this->path . 'file2.txt'); } }