class DrupalLocalStreamWrapper
Drupal stream wrapper base class for local files.
This class provides a complete stream wrapper implementation. URIs such as "public://example.txt" are expanded to a normal filesystem path such as "sites/default/files/example.txt" and then PHP filesystem functions are invoked.
DrupalLocalStreamWrapper implementations need to implement at least the getDirectoryPath() and getExternalUrl() methods.
Hierarchy
- class \DrupalLocalStreamWrapper implements \DrupalStreamWrapperInterface
Expanded class hierarchy of DrupalLocalStreamWrapper
2 string references to 'DrupalLocalStreamWrapper'
- file_uri_normalize_dot_segments in includes/
file.inc - Normalize dot segments in a URI.
- system_file_download in modules/
system/ system.module - Implements hook_file_download().
File
-
includes/
stream_wrappers.inc, line 233
View source
abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface {
/**
* Stream context resource.
*
* @var Resource
*/
public $context;
/**
* A generic resource handle.
*
* @var Resource
*/
public $handle = NULL;
/**
* Instance URI (stream).
*
* A stream is referenced as "scheme://target".
*
* @var String
*/
protected $uri;
/**
* Gets the path that the wrapper is responsible for.
* @TODO: Review this method name in D8 per http://drupal.org/node/701358
*
* @return
* String specifying the path.
*/
abstract function getDirectoryPath();
/**
* Base implementation of setUri().
*/
function setUri($uri) {
$this->uri = $uri;
}
/**
* Base implementation of getUri().
*/
function getUri() {
return $this->uri;
}
/**
* Returns the local writable target of the resource within the stream.
*
* This function should be used in place of calls to realpath() or similar
* functions when attempting to determine the location of a file. While
* functions like realpath() may return the location of a read-only file, this
* method may return a URI or path suitable for writing that is completely
* separate from the URI used for reading.
*
* @param $uri
* Optional URI.
*
* @return
* Returns a string representing a location suitable for writing of a file,
* or FALSE if unable to write to the file such as with read-only streams.
*/
protected function getTarget($uri = NULL) {
if (!isset($uri)) {
$uri = $this->uri;
}
list($scheme, $target) = explode('://', $uri, 2);
// Remove erroneous leading or trailing, forward-slashes and backslashes.
return trim($target, '\\/');
}
/**
* Base implementation of getMimeType().
*/
static function getMimeType($uri, $mapping = NULL) {
if (!isset($mapping)) {
// The default file map, defined in file.mimetypes.inc is quite big.
// We only load it when necessary.
include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
$mapping = file_mimetype_mapping();
}
$extension = '';
$file_parts = explode('.', drupal_basename($uri));
// Remove the first part: a full filename should not match an extension.
array_shift($file_parts);
// Iterate over the file parts, trying to find a match.
// For my.awesome.image.jpeg, we try:
// - jpeg
// - image.jpeg, and
// - awesome.image.jpeg
while ($additional_part = array_pop($file_parts)) {
$extension = strtolower($additional_part . ($extension ? '.' . $extension : ''));
if (isset($mapping['extensions'][$extension])) {
return $mapping['mimetypes'][$mapping['extensions'][$extension]];
}
}
return 'application/octet-stream';
}
/**
* Base implementation of chmod().
*/
function chmod($mode) {
$output = @chmod($this->getLocalPath(), $mode);
// We are modifying the underlying file here, so we have to clear the stat
// cache so that PHP understands that URI has changed too.
clearstatcache();
return $output;
}
/**
* Base implementation of realpath().
*/
function realpath() {
return $this->getLocalPath();
}
/**
* Returns the canonical absolute path of the URI, if possible.
*
* @param string $uri
* (optional) The stream wrapper URI to be converted to a canonical
* absolute path. This may point to a directory or another type of file.
*
* @return string|false
* If $uri is not set, returns the canonical absolute path of the URI
* previously set by the DrupalStreamWrapperInterface::setUri() function.
* If $uri is set and valid for this class, returns its canonical absolute
* path, as determined by the realpath() function. If $uri is set but not
* valid, returns FALSE.
*/
protected function getLocalPath($uri = NULL) {
if (!isset($uri)) {
$uri = $this->uri;
}
$path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
$realpath = realpath($path);
if (!$realpath) {
// This file does not yet exist.
$realpath = realpath(dirname($path)) . '/' . drupal_basename($path);
}
$directory = realpath($this->getDirectoryPath());
if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) {
return FALSE;
}
return $realpath;
}
/**
* Support for fopen(), file_get_contents(), file_put_contents() etc.
*
* @param $uri
* A string containing the URI to the file to open.
* @param $mode
* The file mode ("r", "wb" etc.).
* @param $options
* A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS.
* @param $opened_path
* A string containing the path actually opened.
*
* @return
* Returns TRUE if file was opened successfully.
*
* @see http://php.net/manual/streamwrapper.stream-open.php
*/
public function stream_open($uri, $mode, $options, &$opened_path) {
$this->uri = $uri;
$path = $this->getLocalPath();
if ($path === FALSE) {
if ($options & STREAM_REPORT_ERRORS) {
trigger_error('stream_open() filename cannot be empty', E_USER_WARNING);
}
return FALSE;
}
$this->handle = $options & STREAM_REPORT_ERRORS ? fopen($path, $mode) : @fopen($path, $mode);
if ((bool) $this->handle && $options & STREAM_USE_PATH) {
$opened_path = $path;
}
return (bool) $this->handle;
}
/**
* Support for flock().
*
* @param $operation
* One of the following:
* - LOCK_SH to acquire a shared lock (reader).
* - LOCK_EX to acquire an exclusive lock (writer).
* - LOCK_UN to release a lock (shared or exclusive).
* - LOCK_NB if you don't want flock() to block while locking (not
* supported on Windows).
*
* @return
* Always returns TRUE at the present time.
*
* @see http://php.net/manual/streamwrapper.stream-lock.php
*/
public function stream_lock($operation) {
if (in_array($operation, array(
LOCK_SH,
LOCK_EX,
LOCK_UN,
LOCK_NB,
))) {
return flock($this->handle, $operation);
}
return TRUE;
}
/**
* Support for fread(), file_get_contents() etc.
*
* @param $count
* Maximum number of bytes to be read.
*
* @return
* The string that was read, or FALSE in case of an error.
*
* @see http://php.net/manual/streamwrapper.stream-read.php
*/
public function stream_read($count) {
return fread($this->handle, $count);
}
/**
* Support for fwrite(), file_put_contents() etc.
*
* @param $data
* The string to be written.
*
* @return
* The number of bytes written (integer).
*
* @see http://php.net/manual/streamwrapper.stream-write.php
*/
public function stream_write($data) {
return fwrite($this->handle, $data);
}
/**
* Support for feof().
*
* @return
* TRUE if end-of-file has been reached.
*
* @see http://php.net/manual/streamwrapper.stream-eof.php
*/
public function stream_eof() {
return feof($this->handle);
}
/**
* Support for fseek().
*
* @param $offset
* The byte offset to got to.
* @param $whence
* SEEK_SET, SEEK_CUR, or SEEK_END.
*
* @return
* TRUE on success.
*
* @see http://php.net/manual/streamwrapper.stream-seek.php
*/
public function stream_seek($offset, $whence) {
// fseek returns 0 on success and -1 on a failure.
// stream_seek 1 on success and 0 on a failure.
return !fseek($this->handle, $offset, $whence);
}
/**
* Support for fflush().
*
* @return
* TRUE if data was successfully stored (or there was no data to store).
*
* @see http://php.net/manual/streamwrapper.stream-flush.php
*/
public function stream_flush() {
return fflush($this->handle);
}
/**
* Support for ftell().
*
* @return
* The current offset in bytes from the beginning of file.
*
* @see http://php.net/manual/streamwrapper.stream-tell.php
*/
public function stream_tell() {
return ftell($this->handle);
}
/**
* Support for fstat().
*
* @return
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*
* @see http://php.net/manual/streamwrapper.stream-stat.php
*/
public function stream_stat() {
return fstat($this->handle);
}
/**
* Support for fclose().
*
* @return
* TRUE if stream was successfully closed.
*
* @see http://php.net/manual/streamwrapper.stream-close.php
*/
public function stream_close() {
return fclose($this->handle);
}
/**
* Sets metadata on the stream.
*
* WARNING: Do not call this method directly! It will be called internally by
* PHP itself when one of the following functions is called on a stream URL:
*
* @param string $uri
* A string containing the URI to the file to set metadata on.
* @param int $option
* One of:
* - STREAM_META_TOUCH: The method was called in response to touch().
* - STREAM_META_OWNER_NAME: The method was called in response to chown()
* with string parameter.
* - STREAM_META_OWNER: The method was called in response to chown().
* - STREAM_META_GROUP_NAME: The method was called in response to chgrp().
* - STREAM_META_GROUP: The method was called in response to chgrp().
* - STREAM_META_ACCESS: The method was called in response to chmod().
* @param mixed $value
* If option is:
* - STREAM_META_TOUCH: Array consisting of two arguments of the touch()
* function.
* - STREAM_META_OWNER_NAME or STREAM_META_GROUP_NAME: The name of the owner
* user/group as string.
* - STREAM_META_OWNER or STREAM_META_GROUP: The value of the owner
* user/group as integer.
* - STREAM_META_ACCESS: The argument of the chmod() as integer.
*
* @return bool
* Returns TRUE on success or FALSE on failure. If $option is not
* implemented, FALSE should be returned.
*
* @see touch()
* @see chmod()
* @see chown()
* @see chgrp()
* @link http://php.net/manual/streamwrapper.stream-metadata.php
*/
public function stream_metadata($uri, $option, $value) {
$target = $this->getLocalPath($uri);
$return = FALSE;
switch ($option) {
case STREAM_META_TOUCH:
if (!empty($value)) {
$return = touch($target, $value[0], $value[1]);
}
else {
$return = touch($target);
}
break;
case STREAM_META_OWNER_NAME:
case STREAM_META_OWNER:
$return = chown($target, $value);
break;
case STREAM_META_GROUP_NAME:
case STREAM_META_GROUP:
$return = chgrp($target, $value);
break;
case STREAM_META_ACCESS:
$return = chmod($target, $value);
break;
}
if ($return) {
// For convenience clear the file status cache of the underlying file,
// since metadata operations are often followed by file status checks.
clearstatcache(TRUE, $target);
}
return $return;
}
/**
* Truncate stream.
*
* Will respond to truncation; e.g., through ftruncate().
*
* @param int $new_size
* The new size.
*
* @return bool
* TRUE on success, FALSE otherwise.
*/
public function stream_truncate($new_size) {
return ftruncate($this->handle, $new_size);
}
/**
* Retrieve the underlying stream resource.
*
* This method is called in response to stream_select().
*
* @param int $cast_as
* Can be STREAM_CAST_FOR_SELECT when stream_select() is calling
* stream_cast() or STREAM_CAST_AS_STREAM when stream_cast() is called for
* other uses.
*
* @return resource|false
* The underlying stream resource or FALSE if stream_select() is not
* supported.
*
* @see stream_select()
* @link http://php.net/manual/streamwrapper.stream-cast.php
*/
public function stream_cast($cast_as) {
return $this->handle ? $this->handle : FALSE;
}
/**
* Change stream options.
*
* This method is called to set options on the stream.
*
* Since Windows systems do not allow it and it is not needed for most use
* cases anyway, this method is not supported on local files and will trigger
* an error and return false. If needed, custom subclasses can provide
* OS-specific implementations for advanced use cases.
*
* @param int $option
* One of:
* - STREAM_OPTION_BLOCKING: The method was called in response to
* stream_set_blocking().
* - STREAM_OPTION_READ_TIMEOUT: The method was called in response to
* stream_set_timeout().
* - STREAM_OPTION_WRITE_BUFFER: The method was called in response to
* stream_set_write_buffer().
* @param int $arg1
* If option is:
* - STREAM_OPTION_BLOCKING: The requested blocking mode:
* - 1 means blocking.
* - 0 means not blocking.
* - STREAM_OPTION_READ_TIMEOUT: The timeout in seconds.
* - STREAM_OPTION_WRITE_BUFFER: The buffer mode, STREAM_BUFFER_NONE or
* STREAM_BUFFER_FULL.
* @param int $arg2
* If option is:
* - STREAM_OPTION_BLOCKING: This option is not set.
* - STREAM_OPTION_READ_TIMEOUT: The timeout in microseconds.
* - STREAM_OPTION_WRITE_BUFFER: The requested buffer size.
*
* @return bool
* TRUE on success, FALSE otherwise. If $option is not implemented, FALSE
* should be returned.
*/
public function stream_set_option($option, $arg1, $arg2) {
trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING);
return FALSE;
}
/**
* Support for unlink().
*
* @param $uri
* A string containing the URI to the resource to delete.
*
* @return
* TRUE if resource was successfully deleted.
*
* @see http://php.net/manual/streamwrapper.unlink.php
*/
public function unlink($uri) {
$this->uri = $uri;
return drupal_unlink($this->getLocalPath());
}
/**
* Support for rename().
*
* @param $from_uri,
* The URI to the file to rename.
* @param $to_uri
* The new URI for file.
*
* @return
* TRUE if file was successfully renamed.
*
* @see http://php.net/manual/streamwrapper.rename.php
*/
public function rename($from_uri, $to_uri) {
return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri));
}
/**
* Gets the name of the directory from a given path.
*
* This method is usually accessed through drupal_dirname(), which wraps
* around the PHP dirname() function because it does not support stream
* wrappers.
*
* @param $uri
* A URI or path.
*
* @return
* A string containing the directory name.
*
* @see drupal_dirname()
*/
public function dirname($uri = NULL) {
list($scheme, $target) = explode('://', $uri, 2);
$target = $this->getTarget($uri);
$dirname = dirname($target);
if ($dirname == '.') {
$dirname = '';
}
return $scheme . '://' . $dirname;
}
/**
* Support for mkdir().
*
* @param $uri
* A string containing the URI to the directory to create.
* @param $mode
* Permission flags - see mkdir().
* @param $options
* A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE.
*
* @return
* TRUE if directory was successfully created.
*
* @see http://php.net/manual/streamwrapper.mkdir.php
*/
public function mkdir($uri, $mode, $options) {
$this->uri = $uri;
$recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE);
if ($recursive) {
// $this->getLocalPath() fails if $uri has multiple levels of directories
// that do not yet exist.
$localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
}
else {
$localpath = $this->getLocalPath($uri);
}
if ($options & STREAM_REPORT_ERRORS) {
return drupal_mkdir($localpath, $mode, $recursive);
}
else {
return @drupal_mkdir($localpath, $mode, $recursive);
}
}
/**
* Support for rmdir().
*
* @param $uri
* A string containing the URI to the directory to delete.
* @param $options
* A bit mask of STREAM_REPORT_ERRORS.
*
* @return
* TRUE if directory was successfully removed.
*
* @see http://php.net/manual/streamwrapper.rmdir.php
*/
public function rmdir($uri, $options) {
$this->uri = $uri;
if ($options & STREAM_REPORT_ERRORS) {
return drupal_rmdir($this->getLocalPath());
}
else {
return @drupal_rmdir($this->getLocalPath());
}
}
/**
* Support for stat().
*
* @param $uri
* A string containing the URI to get information about.
* @param $flags
* A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET.
*
* @return
* An array with file status, or FALSE in case of an error - see fstat()
* for a description of this array.
*
* @see http://php.net/manual/streamwrapper.url-stat.php
*/
public function url_stat($uri, $flags) {
$this->uri = $uri;
$path = $this->getLocalPath();
// Suppress warnings if requested or if the file or directory does not
// exist. This is consistent with PHP's plain filesystem stream wrapper.
if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) {
return @stat($path);
}
else {
return stat($path);
}
}
/**
* Support for opendir().
*
* @param $uri
* A string containing the URI to the directory to open.
* @param $options
* Unknown (parameter is not documented in PHP Manual).
*
* @return
* TRUE on success.
*
* @see http://php.net/manual/streamwrapper.dir-opendir.php
*/
public function dir_opendir($uri, $options) {
$this->uri = $uri;
$this->handle = opendir($this->getLocalPath());
return (bool) $this->handle;
}
/**
* Support for readdir().
*
* @return
* The next filename, or FALSE if there are no more files in the directory.
*
* @see http://php.net/manual/streamwrapper.dir-readdir.php
*/
public function dir_readdir() {
return readdir($this->handle);
}
/**
* Support for rewinddir().
*
* @return
* TRUE on success.
*
* @see http://php.net/manual/streamwrapper.dir-rewinddir.php
*/
public function dir_rewinddir() {
rewinddir($this->handle);
// We do not really have a way to signal a failure as rewinddir() does not
// have a return value and there is no way to read a directory handler
// without advancing to the next file.
return TRUE;
}
/**
* Support for closedir().
*
* @return
* TRUE on success.
*
* @see http://php.net/manual/streamwrapper.dir-closedir.php
*/
public function dir_closedir() {
closedir($this->handle);
// We do not really have a way to signal a failure as closedir() does not
// have a return value.
return TRUE;
}
}
Members
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.