Skip to content

Commit

Permalink
Merge pull request #220 from bizurkur/issue-129
Browse files Browse the repository at this point in the history
Add a MultiInstanceFile to allow many file handles to the same resource
  • Loading branch information
bizurkur authored Feb 24, 2020
2 parents 10e92f0 + 62a5ef2 commit 7979e9d
Show file tree
Hide file tree
Showing 6 changed files with 1,030 additions and 4 deletions.
271 changes: 271 additions & 0 deletions src/OpenedFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
<?php

declare(strict_types=1);

/**
* This file is part of vfsStream.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace bovigo\vfs;

use const SEEK_SET;

/**
* Decorator for vfsStreamFile to allow multiple instances of a file to be open.
*
* It works by tracking and restoring the position in the file for each specific
* instance created, even though the underlying file is shared.
*
* @internal
*/
final class OpenedFile
{
/** @var vfsStreamFile */
private $base;

/** @var int */
private $position = 0;

public function __construct(vfsStreamFile $base)
{
$this->base = $base;
}

public function getBaseFile(): vfsStreamFile
{
return $this->base;
}

/**
* simply open the file
*/
public function open(): void
{
$this->base->open();
}

/**
* open file and set pointer to end of file
*/
public function openForAppend(): void
{
$this->base->openForAppend();
$this->savePosition();
}

/**
* open file and truncate content
*/
public function openWithTruncate(): void
{
$this->base->openWithTruncate();
$this->savePosition();
}

/**
* reads the given amount of bytes from content
*/
public function read(int $count): string
{
$this->restorePosition();
$data = $this->base->read($count);
$this->savePosition();

return $data;
}

/**
* returns the content until its end from current offset
*/
public function readUntilEnd(): string
{
$this->restorePosition();
$data = $this->base->readUntilEnd();
$this->savePosition();

return $data;
}

/**
* writes an amount of data
*
* @return int number of bytes written
*/
public function write(string $data): int
{
$this->restorePosition();
$bytes = $this->base->write($data);
$this->savePosition();

return $bytes;
}

/**
* Truncates a file to a given length
*
* @param int $size length to truncate file to
*/
public function truncate(int $size): bool
{
$this->restorePosition();

return $this->base->truncate($size);
}

/**
* checks whether pointer is at end of file
*/
public function eof(): bool
{
$this->restorePosition();

return $this->base->eof();
}

/**
* returns the current position within the file
*/
public function getBytesRead(): int
{
$this->restorePosition();

$this->position = $this->base->getBytesRead();

return $this->position;
}

/**
* seeks to the given offset
*/
public function seek(int $offset, int $whence): bool
{
if ($whence !== SEEK_SET) {
$this->restorePosition();
}

$success = $this->base->seek($offset, $whence);
$this->savePosition();

return $success;
}

/**
* returns size of content
*/
public function size(): int
{
return $this->base->size();
}

/**
* locks file
*
* @param resource|vfsStreamWrapper $resource
*/
public function lock($resource, int $operation): bool
{
return $this->base->lock($resource, $operation);
}

/**
* returns the type of the container
*/
public function getType(): int
{
return $this->base->getType();
}

/**
* returns the last modification time of the stream content
*/
public function filemtime(): int
{
return $this->base->filemtime();
}

/**
* returns the last access time of the stream content
*/
public function fileatime(): int
{
return $this->base->fileatime();
}

/**
* returns the last attribute modification time of the stream content
*/
public function filectime(): int
{
return $this->base->filectime();
}

/**
* returns permissions
*/
public function getPermissions(): int
{
return $this->base->getPermissions();
}

/**
* checks whether content is readable
*
* @param int $user id of user to check for
* @param int $group id of group to check for
*/
public function isReadable(int $user, int $group): bool
{
return $this->base->isReadable($user, $group);
}

/**
* checks whether content is writable
*
* @param int $user id of user to check for
* @param int $group id of group to check for
*/
public function isWritable(int $user, int $group): bool
{
return $this->base->isWritable($user, $group);
}

/**
* checks whether content is executable
*
* @param int $user id of user to check for
* @param int $group id of group to check for
*/
public function isExecutable(int $user, int $group): bool
{
return $this->base->isExecutable($user, $group);
}

/**
* returns owner of file
*/
public function getUser(): int
{
return $this->base->getUser();
}

/**
* returns owner group of file
*/
public function getGroup(): int
{
return $this->base->getGroup();
}

private function restorePosition(): void
{
$this->base->getContentObject()->seek($this->position, SEEK_SET);
}

private function savePosition(): void
{
$this->position = $this->base->getContentObject()->bytesRead();
}
}
10 changes: 10 additions & 0 deletions src/vfsStreamFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ public function getContent(): string
return $this->content->content();
}

/**
* returns the raw content object.
*
* @internal
*/
public function getContentObject(): FileContent
{
return $this->content;
}

/**
* simply open the file
*
Expand Down
8 changes: 4 additions & 4 deletions src/vfsStreamWrapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class vfsStreamWrapper
/**
* shortcut to file container
*
* @var vfsStreamFile|null
* @var OpenedFile|null
*/
protected $content;
/**
Expand Down Expand Up @@ -329,7 +329,7 @@ public function stream_open(string $path, string $mode, int $options, ?string $o
/** @var vfsStreamFile|null $content */
$content = $this->getContentOfType($path, vfsStreamContent::TYPE_FILE);
if ($content !== null) {
$this->content = $content;
$this->content = new OpenedFile($content);
if ($mode === self::WRITE) {
if (($options & STREAM_REPORT_ERRORS) === STREAM_REPORT_ERRORS) {
trigger_error(
Expand Down Expand Up @@ -370,7 +370,7 @@ public function stream_open(string $path, string $mode, int $options, ?string $o
return false;
}

$this->content = $content;
$this->content = new OpenedFile($content);

return true;
}
Expand Down Expand Up @@ -693,7 +693,7 @@ public function stream_stat()

$fileStat = [
'dev' => 0,
'ino' => spl_object_id($this->content),
'ino' => spl_object_id($this->content->getBaseFile()),
'mode' => $this->content->getType() | $this->content->getPermissions(),
'nlink' => 0,
'uid' => $this->content->getUser(),
Expand Down
Loading

0 comments on commit 7979e9d

Please sign in to comment.