1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-07-30 21:40:43 +02:00

[ticket/15253] Add storage abstraction

PHPBB3-15253
This commit is contained in:
Rubén Calvo
2017-06-25 14:22:54 +02:00
parent 2482ec6897
commit 334b44057d
13 changed files with 1078 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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);
}

View File

@@ -0,0 +1,146 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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
}
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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);
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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'];
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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'];
}
}

View File

@@ -0,0 +1,365 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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;
}
}

View File

@@ -0,0 +1,66 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @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);
}
}