View source
<?php
namespace org\bovigo\vfs;
class vfsStreamWrapper {
const READ = 'r';
const TRUNCATE = 'w';
const APPEND = 'a';
const WRITE = 'x';
const WRITE_NEW = 'c';
const READONLY = 0;
const WRITEONLY = 1;
const ALL = 2;
protected static $registered = false;
protected static $root;
private static $quota;
protected $mode;
protected $content;
protected $dir;
protected $dirIterator;
public static function register() {
self::$root = null;
self::$quota = Quota::unlimited();
if (true === self::$registered) {
return;
}
if (@stream_wrapper_register(vfsStream::SCHEME, __CLASS__) === false) {
throw new vfsStreamException('A handler has already been registered for the ' . vfsStream::SCHEME . ' protocol.');
}
self::$registered = true;
}
public static function unregister() {
if (!self::$registered) {
if (in_array(vfsStream::SCHEME, stream_get_wrappers())) {
throw new vfsStreamException('The URL wrapper for the protocol ' . vfsStream::SCHEME . ' was not registered with this version of vfsStream.');
}
return;
}
if (!@stream_wrapper_unregister(vfsStream::SCHEME)) {
throw new vfsStreamException('Failed to unregister the URL wrapper for the ' . vfsStream::SCHEME . ' protocol.');
}
self::$registered = false;
}
public static function setRoot(vfsStreamContainer $root) {
self::$root = $root;
clearstatcache();
return self::$root;
}
public static function getRoot() {
return self::$root;
}
public static function setQuota(Quota $quota) {
self::$quota = $quota;
}
protected function getContent($path) {
if (null === self::$root) {
return null;
}
if (self::$root
->getName() === $path) {
return self::$root;
}
if ($this
->isInRoot($path) && self::$root
->hasChild($path) === true) {
return self::$root
->getChild($path);
}
return null;
}
private function isInRoot($path) {
return substr($path, 0, strlen(self::$root
->getName())) === self::$root
->getName();
}
protected function getContentOfType($path, $type) {
$content = $this
->getContent($path);
if (null !== $content && $content
->getType() === $type) {
return $content;
}
return null;
}
protected function splitPath($path) {
$lastSlashPos = strrpos($path, '/');
if (false === $lastSlashPos) {
return array(
'dirname' => '',
'basename' => $path,
);
}
return array(
'dirname' => substr($path, 0, $lastSlashPos),
'basename' => substr($path, $lastSlashPos + 1),
);
}
protected function resolvePath($path) {
$newPath = array();
foreach (explode('/', $path) as $pathPart) {
if ('.' !== $pathPart) {
if ('..' !== $pathPart) {
$newPath[] = $pathPart;
}
elseif (count($newPath) > 1) {
array_pop($newPath);
}
}
}
return implode('/', $newPath);
}
public function stream_open($path, $mode, $options, $opened_path) {
$extended = strstr($mode, '+') !== false ? true : false;
$mode = str_replace(array(
't',
'b',
'+',
), '', $mode);
if (in_array($mode, array(
'r',
'w',
'a',
'x',
'c',
)) === false) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('Illegal mode ' . $mode . ', use r, w, a, x or c, flavoured with t, b and/or +', E_USER_WARNING);
}
return false;
}
$this->mode = $this
->calculateMode($mode, $extended);
$path = $this
->resolvePath(vfsStream::path($path));
$this->content = $this
->getContentOfType($path, vfsStreamContent::TYPE_FILE);
if (null !== $this->content) {
if (self::WRITE === $mode) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('File ' . $path . ' already exists, can not open with mode x', E_USER_WARNING);
}
return false;
}
if ((self::TRUNCATE === $mode || self::APPEND === $mode) && $this->content
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return false;
}
if (self::TRUNCATE === $mode) {
$this->content
->openWithTruncate();
}
elseif (self::APPEND === $mode) {
$this->content
->openForAppend();
}
else {
if (!$this->content
->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('Permission denied', E_USER_WARNING);
}
return false;
}
$this->content
->open();
}
return true;
}
$content = $this
->createFile($path, $mode, $options);
if (false === $content) {
return false;
}
$this->content = $content;
return true;
}
private function createFile($path, $mode = null, $options = null) {
$names = $this
->splitPath($path);
if (empty($names['dirname']) === true) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('File ' . $names['basename'] . ' does not exist', E_USER_WARNING);
}
return false;
}
$dir = $this
->getContentOfType($names['dirname'], vfsStreamContent::TYPE_DIR);
if (null === $dir) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('Directory ' . $names['dirname'] . ' does not exist', E_USER_WARNING);
}
return false;
}
elseif ($dir
->hasChild($names['basename']) === true) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('Directory ' . $names['dirname'] . ' already contains a director named ' . $names['basename'], E_USER_WARNING);
}
return false;
}
if (self::READ === $mode) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('Can not open non-existing file ' . $path . ' for reading', E_USER_WARNING);
}
return false;
}
if ($dir
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error('Can not create new file in non-writable path ' . $names['dirname'], E_USER_WARNING);
}
return false;
}
return vfsStream::newFile($names['basename'])
->at($dir);
}
protected function calculateMode($mode, $extended) {
if (true === $extended) {
return self::ALL;
}
if (self::READ === $mode) {
return self::READONLY;
}
return self::WRITEONLY;
}
public function stream_close() {
$this->content
->lock($this, LOCK_UN);
}
public function stream_read($count) {
if (self::WRITEONLY === $this->mode) {
return '';
}
if ($this->content
->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return '';
}
return $this->content
->read($count);
}
public function stream_write($data) {
if (self::READONLY === $this->mode) {
return 0;
}
if ($this->content
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return 0;
}
if (self::$quota
->isLimited()) {
$data = substr($data, 0, self::$quota
->spaceLeft(self::$root
->sizeSummarized()));
}
return $this->content
->write($data);
}
public function stream_truncate($size) {
if (self::READONLY === $this->mode) {
return false;
}
if ($this->content
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return false;
}
if ($this->content
->getType() !== vfsStreamContent::TYPE_FILE) {
return false;
}
if (self::$quota
->isLimited() && $this->content
->size() < $size) {
$maxSize = self::$quota
->spaceLeft(self::$root
->sizeSummarized());
if (0 === $maxSize) {
return false;
}
if ($size > $maxSize) {
$size = $maxSize;
}
}
return $this->content
->truncate($size);
}
public function stream_metadata($path, $option, $var) {
$path = $this
->resolvePath(vfsStream::path($path));
$content = $this
->getContent($path);
switch ($option) {
case STREAM_META_TOUCH:
if (null === $content) {
$content = $this
->createFile($path, null, STREAM_REPORT_ERRORS);
if (false === $content) {
return false;
}
}
$currentTime = time();
$content
->lastModified(isset($var[0]) ? $var[0] : $currentTime)
->lastAccessed(isset($var[1]) ? $var[1] : $currentTime);
return true;
case STREAM_META_OWNER_NAME:
return false;
case STREAM_META_OWNER:
if (null === $content) {
return false;
}
return $this
->doPermChange($path, $content, function () use ($content, $var) {
$content
->chown($var);
});
case STREAM_META_GROUP_NAME:
return false;
case STREAM_META_GROUP:
if (null === $content) {
return false;
}
return $this
->doPermChange($path, $content, function () use ($content, $var) {
$content
->chgrp($var);
});
case STREAM_META_ACCESS:
if (null === $content) {
return false;
}
return $this
->doPermChange($path, $content, function () use ($content, $var) {
$content
->chmod($var);
});
default:
return false;
}
}
private function doPermChange($path, vfsStreamAbstractContent $content, \Closure $change) {
if (!$content
->isOwnedByUser(vfsStream::getCurrentUser())) {
return false;
}
if (self::$root
->getName() !== $path) {
$names = $this
->splitPath($path);
$parent = $this
->getContent($names['dirname']);
if (!$parent
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
return false;
}
}
$change();
return true;
}
public function stream_eof() {
return $this->content
->eof();
}
public function stream_tell() {
return $this->content
->getBytesRead();
}
public function stream_seek($offset, $whence) {
return $this->content
->seek($offset, $whence);
}
public function stream_flush() {
return true;
}
public function stream_stat() {
$fileStat = array(
'dev' => 0,
'ino' => 0,
'mode' => $this->content
->getType() | $this->content
->getPermissions(),
'nlink' => 0,
'uid' => $this->content
->getUser(),
'gid' => $this->content
->getGroup(),
'rdev' => 0,
'size' => $this->content
->size(),
'atime' => $this->content
->fileatime(),
'mtime' => $this->content
->filemtime(),
'ctime' => $this->content
->filectime(),
'blksize' => -1,
'blocks' => -1,
);
return array_merge(array_values($fileStat), $fileStat);
}
public function stream_cast($cast_as) {
return false;
}
public function stream_lock($operation) {
if ((LOCK_NB & $operation) == LOCK_NB) {
$operation = $operation - LOCK_NB;
}
return $this->content
->lock($this, $operation);
}
public function stream_set_option($option, $arg1, $arg2) {
switch ($option) {
case STREAM_OPTION_BLOCKING:
case STREAM_OPTION_READ_TIMEOUT:
case STREAM_OPTION_WRITE_BUFFER:
default:
}
return false;
}
public function unlink($path) {
$realPath = $this
->resolvePath(vfsStream::path($path));
$content = $this
->getContent($realPath);
if (null === $content) {
trigger_error('unlink(' . $path . '): No such file or directory', E_USER_WARNING);
return false;
}
if ($content
->getType() !== vfsStreamContent::TYPE_FILE) {
trigger_error('unlink(' . $path . '): Operation not permitted', E_USER_WARNING);
return false;
}
return $this
->doUnlink($realPath);
}
protected function doUnlink($path) {
if (self::$root
->getName() === $path) {
self::$root = null;
clearstatcache();
return true;
}
$names = $this
->splitPath($path);
$content = $this
->getContent($names['dirname']);
if (!$content
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
return false;
}
clearstatcache();
return $content
->removeChild($names['basename']);
}
public function rename($path_from, $path_to) {
$srcRealPath = $this
->resolvePath(vfsStream::path($path_from));
$dstRealPath = $this
->resolvePath(vfsStream::path($path_to));
$srcContent = $this
->getContent($srcRealPath);
if (null == $srcContent) {
trigger_error(' No such file or directory', E_USER_WARNING);
return false;
}
$dstNames = $this
->splitPath($dstRealPath);
$dstParentContent = $this
->getContent($dstNames['dirname']);
if (null == $dstParentContent) {
trigger_error('No such file or directory', E_USER_WARNING);
return false;
}
if (!$dstParentContent
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup())) {
trigger_error('Permission denied', E_USER_WARNING);
return false;
}
if ($dstParentContent
->getType() !== vfsStreamContent::TYPE_DIR) {
trigger_error('Target is not a directory', E_USER_WARNING);
return false;
}
if (!$this
->doUnlink($srcRealPath)) {
return false;
}
$dstContent = $srcContent;
$dstContent
->rename($dstNames['basename']);
$dstParentContent
->addChild($dstContent);
return true;
}
public function mkdir($path, $mode, $options) {
$umask = vfsStream::umask();
if (0 < $umask) {
$permissions = $mode & ~$umask;
}
else {
$permissions = $mode;
}
$path = $this
->resolvePath(vfsStream::path($path));
if (null !== $this
->getContent($path)) {
trigger_error('mkdir(): Path vfs://' . $path . ' exists', E_USER_WARNING);
return false;
}
if (null === self::$root) {
self::$root = vfsStream::newDirectory($path, $permissions);
return true;
}
$maxDepth = count(explode('/', $path));
$names = $this
->splitPath($path);
$newDirs = $names['basename'];
$dir = null;
$i = 0;
while ($dir === null && $i < $maxDepth) {
$dir = $this
->getContent($names['dirname']);
$names = $this
->splitPath($names['dirname']);
if (null == $dir) {
$newDirs = $names['basename'] . '/' . $newDirs;
}
$i++;
}
if (null === $dir || $dir
->getType() !== vfsStreamContent::TYPE_DIR || $dir
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return false;
}
$recursive = (STREAM_MKDIR_RECURSIVE & $options) !== 0 ? true : false;
if (strpos($newDirs, '/') !== false && false === $recursive) {
return false;
}
vfsStream::newDirectory($newDirs, $permissions)
->at($dir);
return true;
}
public function rmdir($path, $options) {
$path = $this
->resolvePath(vfsStream::path($path));
$child = $this
->getContentOfType($path, vfsStreamContent::TYPE_DIR);
if (null === $child) {
return false;
}
if (count($child
->getChildren()) > 0) {
return false;
}
if (self::$root
->getName() === $path) {
self::$root = null;
clearstatcache();
return true;
}
$names = $this
->splitPath($path);
$dir = $this
->getContentOfType($names['dirname'], vfsStreamContent::TYPE_DIR);
if ($dir
->isWritable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return false;
}
clearstatcache();
return $dir
->removeChild($child
->getName());
}
public function dir_opendir($path, $options) {
$path = $this
->resolvePath(vfsStream::path($path));
$this->dir = $this
->getContentOfType($path, vfsStreamContent::TYPE_DIR);
if (null === $this->dir || $this->dir
->isReadable(vfsStream::getCurrentUser(), vfsStream::getCurrentGroup()) === false) {
return false;
}
$this->dirIterator = $this->dir
->getIterator();
return true;
}
public function dir_readdir() {
$dir = $this->dirIterator
->current();
if (null === $dir) {
return false;
}
$this->dirIterator
->next();
return $dir
->getName();
}
public function dir_rewinddir() {
return $this->dirIterator
->rewind();
}
public function dir_closedir() {
$this->dirIterator = null;
return true;
}
public function url_stat($path, $flags) {
$content = $this
->getContent($this
->resolvePath(vfsStream::path($path)));
if (null === $content) {
if (($flags & STREAM_URL_STAT_QUIET) != STREAM_URL_STAT_QUIET) {
trigger_error(' No such file or directory: ' . $path, E_USER_WARNING);
}
return false;
}
$fileStat = array(
'dev' => 0,
'ino' => 0,
'mode' => $content
->getType() | $content
->getPermissions(),
'nlink' => 0,
'uid' => $content
->getUser(),
'gid' => $content
->getGroup(),
'rdev' => 0,
'size' => $content
->size(),
'atime' => $content
->fileatime(),
'mtime' => $content
->filemtime(),
'ctime' => $content
->filectime(),
'blksize' => -1,
'blocks' => -1,
);
return array_merge(array_values($fileStat), $fileStat);
}
}