foldershare-8.x-1.2/src/Utilities/FileUtilities.php
src/Utilities/FileUtilities.php
<?php
namespace Drupal\foldershare\Utilities;
use Drupal\Core\File\FileSystemInterface;
/**
* Defines file utility functions used throughout the module.
*
* This class provides a set of wrapper functions that wrap standard PHP
* file and directory functions, or functions in Drupal's "file_system"
* service and implemented via its FileSystemInterface.
*
* <B>The need for wrappers</B>
* PHP provides a set of standard functions to manipulate file paths
* (e.g. basename() and dirname()), get and change file attributes
* (e.g. filesize(), chmod()), and create and delete
* files and directories (e.g. touch(), mkdir(), rmdir(), and unlink()).
* These functions initially worked only with local files, but as of
* PHP 5.0.0, many of these (but not all of them) gained support for URL
* wrappers and file paths of the form SCHEME://PATH.
*
* Prior to PHP 5.0.0, Drupal provided wrappers for many of these PHP
* functions to add support for URL wrappers, or work around quirks in
* PHP's implementation. These functions existed in Drupal's "file.inc"
* and had names of the form drupal_NAME(), where NAME was the original
* PHP function name (e.g. drupal_unlink()).
*
* As of Drupal 8.0, these drupal_NAME() functions have been gradually
* deprecated and moved into an implementation of the FileSystemInterface class.
* Ideally this isolates file system code into a single class implementation
* and enables a future where remote file systems are supported via stream
* wrappers hidden from the rest of Drupal code. Using these new functions,
* code gets the 'file_system' service to get a FileSystemInterface
* instance, and then calls a function on that instance (e.g.
* \Drupal::service('file_system')->NAME()).
*
* Unfortunately, Drupal's FileSystemInterface and service are incomplete.
* While they implement the old drupal_NAME() functions, they do not provide
* access to other key PHP functions for files and directories, such as
* filesize(), file_exists(), touch(), and scandir(). These are essential
* and are needed throughout Drupal's code and this module. This forces code
* to use an odd mix of FileSystemInterface calls and straight PHP calls.
*
* This utility class centralizes code dealing with files and directories.
* Many of the class's functions call Drupal's 'file_system' service,
* while other functions call PHP's functions directly. As Drupal's
* FileSystemInterface matures, the functions that call PHP will migrate to
* that interface.
*
* <B>Warning:</B> This class is strictly internal to the FolderShare
* module. The class's existance, name, and content may change from
* release to release without any promise of backwards compatability.
*
* @ingroup foldershare
*/
final class FileUtilities {
/*---------------------------------------------------------------------
*
* Path functions (wrappers).
*
*---------------------------------------------------------------------*/
/**
* Returns the trailing name component of a URI or local path.
*
* @param string $path
* The URI or local path to a file or directory.
* @param string $suffix
* The suffix to remove, if non-NULL.
*
* @return string
* Returns the name following the last directory separator on the
* URI or local file path.
*
* @see ::dirname()
* @see \Drupal\Core\File\FileSystemInterface::basename()
* @see https://php.net/manual/en/function.basename.php
*/
public static function basename(string $path, string $suffix = NULL) {
// The file system service replaces the deprecated drupal_basename().
return \Drupal::service('file_system')->basename($path, $suffix);
}
/**
* Returns the parent directory path component of a URI or local path.
*
* @param string $path
* The URI or local path to a file or directory.
*
* @return string
* Returns the URI or local path with the name following the last
* directory separator removed.
*
* @see ::basename()
* @see \Drupal\Core\File\FileSystemInterface::dirname()
* @see https://php.net/manual/en/function.dirname.php
*/
public static function dirname(string $path) {
// The file system service replaces the deprecated drupal_dirname().
return \Drupal::service('file_system')->dirname($path);
}
/**
* Returns the canonicalized absolute path of a URI or local path.
*
* URIs for local files or directories are converted to absolute paths.
* Local paths are converted to absolute paths by collapsing '.' and '..'
* in the path, removing trailing '/', and following symbolic links.
*
* @param string $path
* The URI or local path to a file or directory.
*
* @return string|bool
* Returns the absolute local path to the file or directory, or FALSE
* if the file or directory does not exist or the user does not have
* suitable permissions.
*
* @see \Drupal\Core\File\FileSystemInterface::realpath()
* @see https://php.net/manual/en/function.realpath.php
*/
public static function realpath(string $path) {
// The file system service replaces the deprecated drupal_realpath().
return \Drupal::service('file_system')->realpath($path);
}
/*---------------------------------------------------------------------
*
* File and directory attributes (wrappers).
*
*---------------------------------------------------------------------*/
/**
* Changes permissions on a file or directory.
*
* See PHP's chmod() documentation for an explanation of modes.
*
* Unlike the PHP chmod() function, this function (and Drupal's
* FileSystemInterface implementations) default the mode to the server's
* 'file_chmod_directory' and 'file_chmod_file' configuration settings.
* By default, these give everyone read access so that the web server
* can read and deliver the file or directory.
*
* @param string $path
* The URI or local path to parse.
* @param mixed $mode
* (optional) The permissions mode of the file. This is ignored on
* Windows. The default is NULL, which defaults the mode to the server's
* configuration settings.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see ::is_readable()
* @see ::is_writable()
* @see \Drupal\Core\File\FileSystemInterface::chmod()
* @see https://php.net/manual/en/function.chmod.php
*/
public static function chmod(string $path, $mode = NULL) {
// The file system service replaces the deprecated drupal_chmod().
return \Drupal::service('file_system')->chmod($path, $mode);
}
/**
* Returns TRUE if the file exists.
*
* @param string $path
* The URI or local path to a file or directory.
*
* @return bool
* Returns TRUE if the file exists.
*
* @see ::stat()
* @see https://php.net/manual/en/function.file-exists.php
*/
public static function fileExists(string $path) {
// Drupal's FileSystem class does not yet support file existance checks.
//
// PHP's file_exists() does not support URIs as of PHP 7, so use
// realpath() to convert from a URI to an absolute path first.
$absPath = self::realpath($path);
if ($absPath === FALSE) {
// Unrecognized URI scheme, missing directory, or bad path.
return FALSE;
}
return @file_exists($absPath);
}
/**
* Returns the size of the local file, in bytes.
*
* @param string $path
* The URI or local path to a file or directory.
*
* @return int
* Returns the size of the file, in bytes, or FALSE on failure.
*
* @see ::stat()
* @see https://php.net/manual/en/function.filesize.php
*/
public static function filesize(string $path) {
// Drupal's FileSystem class does not yet support file attributes.
//
// PHP's filesize() does not support URIs as of PHP 7, so use
// realpath() to convert from a URI to an absolute path first.
$absPath = self::realpath($path);
if ($absPath === FALSE) {
// Unrecognized URI scheme, missing directory, or bad path.
return FALSE;
}
return @filesize($absPath);
}
/**
* Returns information about a local file or directory.
*
* Most of the values returned by this function are low-level details
* that are of minimal use by platform-independent code working with
* local files. Key values that are useful include:
* - 'size': the size, in bytes.
* - 'atime': the last access time stamp.
* - 'mtime': the last modified time stamp.
* - 'ctime': the created time stamp.
*
* @param string $path
* The URI or local path to a file or directory.
*
* @return array
* Returns an associative array with attributes of the file. Array entries
* are as per PHP's stat() and include:
* - 'dev': the device number.
* - 'ino': the inode number on Linux/macOS, and 0 on Windows.
* - 'mode': the inode protection mode on Linux/macOS.
* - 'nlink': the number of links.
* - 'uid': the owner's user ID on Linux/macOS, and 0 on Windows.
* - 'gid': the owner's group ID on Linux/macOS, and 0 on Windows.
* - 'rdev': the device type.
* - 'size': the size, in bytes.
* - 'atime': the last access time stamp.
* - 'mtime': the last modified time stamp.
* - 'ctime': the created time stamp.
* - 'blksize': the block size on some systems, and -1 on Windows.
* - 'blocks': the number of blocks allocated, and -1 on Windows.
*
* @see ::filesize()
* @see https://php.net/manual/en/function.stat.php
*/
public static function stat(string $path) {
// Drupal's FileSystem class does not yet support file attributes.
//
// PHP's stat() does not support URIs as of PHP 7, so use
// realpath() to convert from a URI to an absolute path first.
$absPath = self::realpath($path);
if ($absPath === FALSE) {
// Unrecognized URI scheme, missing directory, or bad path.
return FALSE;
}
return @stat($absPath);
}
/*---------------------------------------------------------------------
*
* File handling (wrappers).
*
*---------------------------------------------------------------------*/
/**
* Renames a file or directory.
*
* @param string $oldPath
* The URI or local path to the file or directory.
* @param string $newPath
* The intended URI or local path to the file or directory. If using a
* URI, the scheme used by the $oldPath and $newPath must match.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see \Drupal\Core\File\FileSystemInterface::move()
* @see https://php.net/manual/en/function.rename.php
*/
public static function rename(
string $oldPath,
string $newPath) {
try {
// Drupal's move() throws an exception if the rename cannot be done.
// Map this to FALSE to better match PHP's rename().
$newPath = \Drupal::service('file_system')->move(
$oldPath,
$newPath,
FileSystemInterface::EXISTS_ERROR);
return TRUE;
}
catch (\Exception $e) {
return FALSE;
}
}
/**
* Creates a file with a unique name in the selected directory.
*
* The function creates a new file in the indicated directory, gives it
* a unique name, and sets access permission to 0600. The $prefix, if
* given, is used as the start of the unique file name.
*
* When the $path argument is a URI, a URI for the new file is returned.
* When the $path is a local file system path, a path for the new file
* is returned.
*
* @param string $path
* The URI or local path to the directory to contain the file.
* @param string $prefix
* (optional) The prefix for the temporary name. Some OSes (e.g. Windows)
* only use the first few characters of the prefix. The default is to
* include no prefix.
*
* @return string|bool
* Returns the URI or local path to the new file on success,
* and FALSE otherwise.
*
* @see \Drupal\Core\File\FileSystemInterface::tempnam()
* @see drupal_tempnam()
* @see https://php.net/manual/en/function.tempnam.php
*/
public static function tempnam(string $path, string $prefix = '') {
//
// Implementation note:
//
// Drupal's 'file_system' service implements tempnam(), so the
// apparent way to wrap the function is simply:
// - return \Drupal::service('file_system')->tempnam($path, $prefix);
//
// Unfortunately, Drupal's implementation ignores $path if there is
// a scheme in front. This is clearly a bug and we need a workaround.
//
// The incoming path may be of the form SCHEME://DIR. In this case,
// a correct implementation uses the SCHEME to look up the equivalent
// base directory path on the server. DIR is then appended and the
// resulting path passed to PHP's tempnam() to create a temporary file
// in that directory, and the resulting temp file path is returned.
//
// However, Drupal uses SCHEME to look up the base directory path,
// and then it ignores DIR. It passes the base path to tempnam(), which
// then always creates a temporary file in that scheme's top directory.
// For public://DIR, DIR is ignored and temp files always get created
// in public://, which is usually the /files directory of the site.
//
// So, below we reimplement tempnam() properly to use DIR.
//
// Split the path into a scheme and a directory path.
$parts = explode('://', $path, 2);
if (count($parts) === 1) {
// There is no scheme. $path is a local path, not a URI.
return tempnam($path, $prefix);
}
// Get the stream wrapper for the scheme.
$scheme = $parts[0];
$dirPath = $parts[1];
$mgr = \Drupal::service('stream_wrapper_manager');
$wrapper = $mgr->getViaScheme($scheme);
if ($wrapper === FALSE) {
// Unknown scheme.
return FALSE;
}
// Use the wrapper's base path to build a directory path and create
// a temporary file.
$basePath = $wrapper->realpath();
$parentPath = $basePath . DIRECTORY_SEPARATOR . $dirPath;
$tempPath = tempnam($parentPath, $prefix);
if ($tempPath === FALSE) {
// Could not create temporary file.
return FALSE;
}
// The wrapper's directory path is a site-relative path, but tempnam()
// returns an absolute path. To build a URI to return, we need to
// strip off the absolute path at the start.
$baseLen = mb_strlen($basePath);
return $scheme . '://' . mb_substr($tempPath, ($baseLen + 1));
}
/**
* Sets the access and last modification time for a local item.
*
* Sets the modified and access time of the local file or directory.
* If the file does not exist, it will be created.
*
* @param string $path
* The URI or local path to a file or directory.
* @param int $mtime
* (optional) The last modified time stamp.
* @param int $atime
* (optional) The last accessed time stamp.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see https://php.net/manual/en/function.touch.php
*/
public static function touch(string $path, $mtime = NULL, $atime = NULL) {
// Drupal's FileSystem class does not yet support file attributes.
//
// PHP's touch() does not support URIs as of PHP 7, so use
// realpath() to convert from a URI to an absolute path first.
if ($mtime === NULL) {
$mtime = time();
}
if ($atime === NULL) {
$atime = $mtime;
}
$absPath = self::realpath($path);
if ($absPath === FALSE) {
// Unrecognized URI scheme, missing directory, or bad path.
return FALSE;
}
return @touch($absPath, $mtime, $atime);
}
/**
* Removes a local file.
*
* The URI or path must be for a file.
*
* @param string $path
* The URI or local path to the file.
* @param resource $context
* (optional) The stream context. Default is NULL.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see ::rmdir()
* @see \Drupal\Core\File\FileSystemInterface::unlink()
* @see https://php.net/manual/en/function.unlink.php
*/
public static function unlink(string $path, $context = NULL) {
// The file system service replaces the deprecated drupal_unlink().
return \Drupal::service('file_system')->unlink($path, $context);
}
/*---------------------------------------------------------------------
*
* Directory handling (wrappers).
*
*---------------------------------------------------------------------*/
/**
* Makes a new local directory.
*
* See PHP's chmod() documentation for an explanation of modes.
*
* Unlike the PHP mkdir() function, this function (and Drupal's
* FileSystemInterface implementations) default the mode to the server's
* 'file_chmod_directory' and 'file_chmod_file' configuration settings.
* By default, these give everyone read access so that the web server
* can read and deliver the file or directory.
*
* @param string $path
* The URI or local path to the directory.
* @param mixed $mode
* (optional) The permissions mode of the directory. This is ignored on
* Windows. The default is NULL, which defaults the mode to the server's
* configuration settings.
* @param bool $recursive
* (optional) Whether to created nested directories. Default is FALSE.
* @param resource $context
* (optional) The stream context. Default is NULL.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see ::chmod()
* @see \Drupal\Core\File\FileSystemInterface::mkdir()
* @see https://php.net/manual/en/function.chmod.php
* @see https://php.net/manual/en/function.mkdir.php
*/
public static function mkdir(
string $path,
$mode = NULL,
bool $recursive = FALSE,
$context = NULL) {
// The file system service replaces the deprecated drupal_mkdir().
return \Drupal::service('file_system')->mkdir(
$path,
$mode,
$recursive,
$context);
}
/**
* Removes an empty local directory.
*
* The path must be for a directory, and it must be empty.
*
* @param string $path
* The URI or local path to the directory.
* @param resource $context
* (optional) The stream context. Default is NULL.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see ::unlink()
* @see \Drupal\Core\File\FileSystemInterface::rmdir()
* @see https://php.net/manual/en/function.rmdir.php
*/
public static function rmdir(string $path, $context = NULL) {
// The file system service replaces the deprecated drupal_rmdir().
return \Drupal::service('file_system')->rmdir($path, $context);
}
/**
* Recursively removes a local directory and all of its contents.
*
* The standard rmdir() function deletes an empty directory, but not
* one with files in it. The standard unlink() function deletes files,
* but not directories. There is no standad function to delete a
* directory that still contains files.
*
* This function recursively sweeps through a local directory and
* removes all of its files and subdirectories, then removes the
* resulting empty directory. If any file or directory cannot be
* deleted, this function aborts.
*
* @param string $path
* The URI or local path to a directory to delete.
*
* @return bool
* Returns TRUE on success and FALSE on failure.
*
* @see ::is_dir()
* @see ::rmdir()
* @see ::scandir()
* @see ::unlink()
* @see \Drupal\Core\File\FileSystemInterface::deleteRecursive()
*/
public static function rrmdir(string $path) {
// The file system service replaces the deprecated
// file_unmanaged_delete_recursive().
return \Drupal::service('file_system')->deleteRecursive($path);
}
/**
* Returns a list of items inside a directory.
*
* @param string $path
* The URI or local path to a directory.
* @param int $sortingOrder
* (optional) The sort order for the returned items. Recognized
* values are:
* - SCANDIR_SORT_ASCENDING (default).
* - SCANDIR_SORT_DESCENDING.
* - SCANDIR_SORT_NONE.
* @param resource $context
* (optional) The stream context. Default is NULL.
*
* @return array
* Returns an array of names (without a leading directory path)
* for all of the items in the directory.
*
* @see https://php.net/manual/en/function.scandir.php
*/
public static function scandir(
string $path,
int $sortingOrder = SCANDIR_SORT_ASCENDING,
$context = NULL) {
// Drupal's FileSystem class does not yet support directory operations.
//
// PHP's scandir() does not support URIs as of PHP 7, so use
// realpath() to convert from a URI to an absolute path first.
$absPath = self::realpath($path);
if ($absPath === FALSE) {
// Unrecognized URI scheme, missing directory, or bad path.
return FALSE;
}
if ($context === NULL) {
return @scandir($absPath, $sortingOrder);
}
return @scandir($absPath, $sortingOrder, $context);
}
}
