Chris@0: uri = $uri; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getUri() { Chris@0: return $this->uri; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the local writable target of the resource within the stream. Chris@0: * Chris@0: * This function should be used in place of calls to realpath() or similar Chris@0: * functions when attempting to determine the location of a file. While Chris@0: * functions like realpath() may return the location of a read-only file, this Chris@0: * method may return a URI or path suitable for writing that is completely Chris@0: * separate from the URI used for reading. Chris@0: * Chris@0: * @param string $uri Chris@0: * Optional URI. Chris@0: * Chris@0: * @return string|bool Chris@0: * Returns a string representing a location suitable for writing of a file, Chris@0: * or FALSE if unable to write to the file such as with read-only streams. Chris@0: */ Chris@0: protected function getTarget($uri = NULL) { Chris@0: if (!isset($uri)) { Chris@0: $uri = $this->uri; Chris@0: } Chris@0: Chris@0: list(, $target) = explode('://', $uri, 2); Chris@0: Chris@0: // Remove erroneous leading or trailing, forward-slashes and backslashes. Chris@0: return trim($target, '\/'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function realpath() { Chris@0: return $this->getLocalPath(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the canonical absolute path of the URI, if possible. Chris@0: * Chris@0: * @param string $uri Chris@0: * (optional) The stream wrapper URI to be converted to a canonical Chris@0: * absolute path. This may point to a directory or another type of file. Chris@0: * Chris@0: * @return string|bool Chris@0: * If $uri is not set, returns the canonical absolute path of the URI Chris@0: * previously set by the Chris@0: * Drupal\Core\StreamWrapper\StreamWrapperInterface::setUri() function. Chris@0: * If $uri is set and valid for this class, returns its canonical absolute Chris@0: * path, as determined by the realpath() function. If $uri is set but not Chris@0: * valid, returns FALSE. Chris@0: */ Chris@0: protected function getLocalPath($uri = NULL) { Chris@0: if (!isset($uri)) { Chris@0: $uri = $this->uri; Chris@0: } Chris@0: $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri); Chris@0: Chris@0: // In PHPUnit tests, the base path for local streams may be a virtual Chris@0: // filesystem stream wrapper URI, in which case this local stream acts like Chris@0: // a proxy. realpath() is not supported by vfsStream, because a virtual Chris@0: // file system does not have a real filepath. Chris@0: if (strpos($path, 'vfs://') === 0) { Chris@0: return $path; Chris@0: } Chris@0: Chris@0: $realpath = realpath($path); Chris@0: if (!$realpath) { Chris@0: // This file does not yet exist. Chris@18: $realpath = realpath(dirname($path)) . '/' . \Drupal::service('file_system')->basename($path); Chris@0: } Chris@0: $directory = realpath($this->getDirectoryPath()); Chris@0: if (!$realpath || !$directory || strpos($realpath, $directory) !== 0) { Chris@0: return FALSE; Chris@0: } Chris@0: return $realpath; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for fopen(), file_get_contents(), file_put_contents() etc. Chris@0: * Chris@0: * @param string $uri Chris@0: * A string containing the URI to the file to open. Chris@0: * @param int $mode Chris@0: * The file mode ("r", "wb" etc.). Chris@0: * @param int $options Chris@0: * A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS. Chris@0: * @param string $opened_path Chris@0: * A string containing the path actually opened. Chris@0: * Chris@0: * @return bool Chris@0: * Returns TRUE if file was opened successfully. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-open.php Chris@0: */ Chris@0: public function stream_open($uri, $mode, $options, &$opened_path) { Chris@0: $this->uri = $uri; Chris@0: $path = $this->getLocalPath(); Chris@0: $this->handle = ($options & STREAM_REPORT_ERRORS) ? fopen($path, $mode) : @fopen($path, $mode); Chris@0: Chris@0: if ((bool) $this->handle && $options & STREAM_USE_PATH) { Chris@0: $opened_path = $path; Chris@0: } Chris@0: Chris@0: return (bool) $this->handle; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for flock(). Chris@0: * Chris@0: * @param int $operation Chris@0: * One of the following: Chris@0: * - LOCK_SH to acquire a shared lock (reader). Chris@0: * - LOCK_EX to acquire an exclusive lock (writer). Chris@0: * - LOCK_UN to release a lock (shared or exclusive). Chris@0: * - LOCK_NB if you don't want flock() to block while locking (not Chris@0: * supported on Windows). Chris@0: * Chris@0: * @return bool Chris@0: * Always returns TRUE at the present time. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-lock.php Chris@0: */ Chris@0: public function stream_lock($operation) { Chris@0: if (in_array($operation, [LOCK_SH, LOCK_EX, LOCK_UN, LOCK_NB])) { Chris@0: return flock($this->handle, $operation); Chris@0: } Chris@0: Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for fread(), file_get_contents() etc. Chris@0: * Chris@0: * @param int $count Chris@0: * Maximum number of bytes to be read. Chris@0: * Chris@0: * @return string|bool Chris@0: * The string that was read, or FALSE in case of an error. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-read.php Chris@0: */ Chris@0: public function stream_read($count) { Chris@0: return fread($this->handle, $count); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for fwrite(), file_put_contents() etc. Chris@0: * Chris@0: * @param string $data Chris@0: * The string to be written. Chris@0: * Chris@0: * @return int Chris@0: * The number of bytes written. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-write.php Chris@0: */ Chris@0: public function stream_write($data) { Chris@0: return fwrite($this->handle, $data); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for feof(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if end-of-file has been reached. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-eof.php Chris@0: */ Chris@0: public function stream_eof() { Chris@0: return feof($this->handle); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function stream_seek($offset, $whence = SEEK_SET) { Chris@0: // fseek returns 0 on success and -1 on a failure. Chris@0: // stream_seek 1 on success and 0 on a failure. Chris@0: return !fseek($this->handle, $offset, $whence); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for fflush(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if data was successfully stored (or there was no data to store). Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-flush.php Chris@0: */ Chris@0: public function stream_flush() { Chris@0: return fflush($this->handle); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for ftell(). Chris@0: * Chris@0: * @return bool Chris@0: * The current offset in bytes from the beginning of file. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-tell.php Chris@0: */ Chris@0: public function stream_tell() { Chris@0: return ftell($this->handle); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for fstat(). Chris@0: * Chris@0: * @return bool Chris@0: * An array with file status, or FALSE in case of an error - see fstat() Chris@0: * for a description of this array. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-stat.php Chris@0: */ Chris@0: public function stream_stat() { Chris@0: return fstat($this->handle); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for fclose(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if stream was successfully closed. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.stream-close.php Chris@0: */ Chris@0: public function stream_close() { Chris@0: return fclose($this->handle); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function stream_cast($cast_as) { Chris@0: return $this->handle ? $this->handle : FALSE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function stream_metadata($uri, $option, $value) { Chris@0: $target = $this->getLocalPath($uri); Chris@0: $return = FALSE; Chris@0: switch ($option) { Chris@0: case STREAM_META_TOUCH: Chris@0: if (!empty($value)) { Chris@0: $return = touch($target, $value[0], $value[1]); Chris@0: } Chris@0: else { Chris@0: $return = touch($target); Chris@0: } Chris@0: break; Chris@0: Chris@0: case STREAM_META_OWNER_NAME: Chris@0: case STREAM_META_OWNER: Chris@0: $return = chown($target, $value); Chris@0: break; Chris@0: Chris@0: case STREAM_META_GROUP_NAME: Chris@0: case STREAM_META_GROUP: Chris@0: $return = chgrp($target, $value); Chris@0: break; Chris@0: Chris@0: case STREAM_META_ACCESS: Chris@0: $return = chmod($target, $value); Chris@0: break; Chris@0: } Chris@0: if ($return) { Chris@0: // For convenience clear the file status cache of the underlying file, Chris@0: // since metadata operations are often followed by file status checks. Chris@0: clearstatcache(TRUE, $target); Chris@0: } Chris@0: return $return; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: * Chris@0: * Since Windows systems do not allow it and it is not needed for most use Chris@0: * cases anyway, this method is not supported on local files and will trigger Chris@0: * an error and return false. If needed, custom subclasses can provide Chris@0: * OS-specific implementations for advanced use cases. Chris@0: */ Chris@0: public function stream_set_option($option, $arg1, $arg2) { Chris@0: trigger_error('stream_set_option() not supported for local file based stream wrappers', E_USER_WARNING); Chris@0: return FALSE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function stream_truncate($new_size) { Chris@0: return ftruncate($this->handle, $new_size); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for unlink(). Chris@0: * Chris@0: * @param string $uri Chris@0: * A string containing the URI to the resource to delete. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if resource was successfully deleted. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.unlink.php Chris@0: */ Chris@0: public function unlink($uri) { Chris@0: $this->uri = $uri; Chris@0: return drupal_unlink($this->getLocalPath()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for rename(). Chris@0: * Chris@0: * @param string $from_uri Chris@0: * The URI to the file to rename. Chris@0: * @param string $to_uri Chris@0: * The new URI for file. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if file was successfully renamed. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.rename.php Chris@0: */ Chris@0: public function rename($from_uri, $to_uri) { Chris@0: return rename($this->getLocalPath($from_uri), $this->getLocalPath($to_uri)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the name of the directory from a given path. Chris@0: * Chris@18: * This method is usually accessed through Chris@18: * \Drupal\Core\File\FileSystemInterface::dirname(), which wraps around the Chris@18: * PHP dirname() function because it does not support stream wrappers. Chris@0: * Chris@0: * @param string $uri Chris@0: * A URI or path. Chris@0: * Chris@0: * @return string Chris@0: * A string containing the directory name. Chris@0: * Chris@18: * @see \Drupal\Core\File\FileSystemInterface::dirname() Chris@0: */ Chris@0: public function dirname($uri = NULL) { Chris@0: list($scheme) = explode('://', $uri, 2); Chris@0: $target = $this->getTarget($uri); Chris@0: $dirname = dirname($target); Chris@0: Chris@0: if ($dirname == '.') { Chris@0: $dirname = ''; Chris@0: } Chris@0: Chris@0: return $scheme . '://' . $dirname; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for mkdir(). Chris@0: * Chris@0: * @param string $uri Chris@0: * A string containing the URI to the directory to create. Chris@0: * @param int $mode Chris@0: * Permission flags - see mkdir(). Chris@0: * @param int $options Chris@0: * A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if directory was successfully created. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.mkdir.php Chris@0: */ Chris@0: public function mkdir($uri, $mode, $options) { Chris@0: $this->uri = $uri; Chris@0: $recursive = (bool) ($options & STREAM_MKDIR_RECURSIVE); Chris@0: if ($recursive) { Chris@0: // $this->getLocalPath() fails if $uri has multiple levels of directories Chris@0: // that do not yet exist. Chris@0: $localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri); Chris@0: } Chris@0: else { Chris@0: $localpath = $this->getLocalPath($uri); Chris@0: } Chris@18: /** @var \Drupal\Core\File\FileSystemInterface $file_system */ Chris@18: $file_system = \Drupal::service('file_system'); Chris@0: if ($options & STREAM_REPORT_ERRORS) { Chris@18: return $file_system->mkdir($localpath, $mode, $recursive); Chris@0: } Chris@0: else { Chris@18: return @$file_system->mkdir($localpath, $mode, $recursive); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for rmdir(). Chris@0: * Chris@0: * @param string $uri Chris@0: * A string containing the URI to the directory to delete. Chris@0: * @param int $options Chris@0: * A bit mask of STREAM_REPORT_ERRORS. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if directory was successfully removed. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.rmdir.php Chris@0: */ Chris@0: public function rmdir($uri, $options) { Chris@0: $this->uri = $uri; Chris@18: /** @var \Drupal\Core\File\FileSystemInterface $file_system */ Chris@18: $file_system = \Drupal::service('file_system'); Chris@0: if ($options & STREAM_REPORT_ERRORS) { Chris@18: return $file_system->rmdir($this->getLocalPath()); Chris@0: } Chris@0: else { Chris@18: return @$file_system->rmdir($this->getLocalPath()); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for stat(). Chris@0: * Chris@0: * @param string $uri Chris@0: * A string containing the URI to get information about. Chris@0: * @param int $flags Chris@0: * A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET. Chris@0: * Chris@0: * @return array Chris@0: * An array with file status, or FALSE in case of an error - see fstat() Chris@0: * for a description of this array. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.url-stat.php Chris@0: */ Chris@0: public function url_stat($uri, $flags) { Chris@0: $this->uri = $uri; Chris@0: $path = $this->getLocalPath(); Chris@0: // Suppress warnings if requested or if the file or directory does not Chris@0: // exist. This is consistent with PHP's plain filesystem stream wrapper. Chris@0: if ($flags & STREAM_URL_STAT_QUIET || !file_exists($path)) { Chris@0: return @stat($path); Chris@0: } Chris@0: else { Chris@0: return stat($path); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for opendir(). Chris@0: * Chris@0: * @param string $uri Chris@0: * A string containing the URI to the directory to open. Chris@0: * @param int $options Chris@0: * Unknown (parameter is not documented in PHP Manual). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE on success. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.dir-opendir.php Chris@0: */ Chris@0: public function dir_opendir($uri, $options) { Chris@0: $this->uri = $uri; Chris@0: $this->handle = opendir($this->getLocalPath()); Chris@0: Chris@0: return (bool) $this->handle; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for readdir(). Chris@0: * Chris@0: * @return string Chris@0: * The next filename, or FALSE if there are no more files in the directory. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.dir-readdir.php Chris@0: */ Chris@0: public function dir_readdir() { Chris@0: return readdir($this->handle); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for rewinddir(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE on success. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.dir-rewinddir.php Chris@0: */ Chris@0: public function dir_rewinddir() { Chris@0: rewinddir($this->handle); Chris@0: // We do not really have a way to signal a failure as rewinddir() does not Chris@0: // have a return value and there is no way to read a directory handler Chris@0: // without advancing to the next file. Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Support for closedir(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE on success. Chris@0: * Chris@0: * @see http://php.net/manual/streamwrapper.dir-closedir.php Chris@0: */ Chris@0: public function dir_closedir() { Chris@0: closedir($this->handle); Chris@0: // We do not really have a way to signal a failure as closedir() does not Chris@0: // have a return value. Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: }