Skip to content

Commit

Permalink
Merge pull request #3 from ambimax/rename
Browse files Browse the repository at this point in the history
feat: added rename function
  • Loading branch information
FabianKoehnen authored Nov 4, 2022
2 parents 95906c1 + 60c30da commit d4abfb3
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 16 deletions.
106 changes: 97 additions & 9 deletions src/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,49 @@

namespace Ambimax\File;

use RuntimeException;
use Symfony\Component\Filesystem\Path;

class File implements FileInterface
{
public const MODE_READ = 'r';
public const MODE_READ_PLUS = 'r+';
public const MODE_WRITE = 'w';
public const MODE_WRITE_PLUS = 'w+';
/**
* @var resource
*/
protected $fileHandle;

protected string $filePath;
protected string $mode;

/**
* @param self::MODE_* $mode
*/
public function __construct(string $filePath, string $mode)
{
$this->openStream($filePath, $mode);
$this->filePath = $filePath;
}

protected function openStream(string $filePath, string $mode): void
{
if (!file_exists($filePath)) {
throw new RuntimeException("File '$filePath' does not exist.");
if (
!in_array($mode, [
self::MODE_WRITE,
self::MODE_WRITE_PLUS,
]) && !file_exists($filePath)
) {
throw new \RuntimeException("File '$filePath' does not exist.");
}

$tmpFileHandle = fopen($filePath, $mode);
if (false === $tmpFileHandle) {
throw new RuntimeException("Could not open file '$filePath'.");
throw new \RuntimeException("Could not open file '$filePath'.");
}

$this->fileHandle = $tmpFileHandle;
$this->filePath = $filePath;
$this->mode = $mode;
}

public function __destruct()
Expand All @@ -53,19 +66,94 @@ public function getBasename(): string
return basename($this->filePath);
}

/**
* @return resource
*/
public function getFileHandle()
{
return $this->fileHandle;
}

/**
* @return false|string
*/
public function getContent()
{
return stream_get_contents($this->fileHandle);
$pointerLocation = ftell($this->fileHandle);
if (false === $pointerLocation) {
throw new \RuntimeException('unable to get current location of the file pointer. exception thrown to prevent unpredictable pointer jumping');
}
rewind($this->fileHandle);

$content = stream_get_contents($this->fileHandle);
fseek($this->fileHandle, $pointerLocation);

return $content;
}

/**
* @return resource
* Changes relative path to an absolut path relative to the current file location.
* This is to prevent confusion of e.g. rename() where the path would be relative to the php working directory.
* If the path is already absolute or has a protocol defined the path won't be affected by this.
*
* If the path ends with the directory separator ("/") the current file name will get appended
*/
public function getFileHandle()
protected function ensureAbsolutePath(string $path): string
{
return $this->fileHandle;
if (
true === Path::isLocal($path) &&
false === Path::isAbsolute($path)
) {
$path = Path::join(dirname($this->filePath), $path);
}

if (DIRECTORY_SEPARATOR === $path[-1]) {
$path = Path::join($path, $this->getBasename());
}

return $path;
}

public function rename(string $newPath): bool
{
$newPath = $this->ensureAbsolutePath($newPath);

fclose($this->fileHandle);

$success = rename($this->filePath, $newPath);
$this->openStream($newPath, $this->mode);

return $success;
}

public function move(string $newPath): bool
{
return $this->rename($newPath);
}

/**
* @param int<0, max>|null $length
*/
public function fwrite(string $data, ?int $length = null): int|false
{
return fwrite($this->fileHandle, $data, $length);
}

/**
* @param int<0, max>|null $length
*/
public function fread(?int $length = null): string|false
{
return fread($this->fileHandle, $length);
}

public function ftell(): int|false
{
return ftell($this->fileHandle);
}

public function fseek(int $offset, int $whence = SEEK_SET): int
{
return fseek($this->fileHandle, $offset, $whence);
}
}
22 changes: 17 additions & 5 deletions src/FileInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@

interface FileInterface
{
/**
* @return false|string
*/
public function getContent();

public function getPath(): string;

public function getBasename(): string;
Expand All @@ -19,4 +14,21 @@ public function getBasename(): string;
* @return resource
*/
public function getFileHandle();

/**
* @return false|string
*/
public function getContent();

public function rename(string $newPath): bool;

public function move(string $newPath): bool;

public function fwrite(string $data, ?int $length = null): int|false;

public function fread(?int $length = null): string|false;

public function ftell(): int|false;

public function fseek(int $offset, int $whence = SEEK_SET): int;
}
24 changes: 22 additions & 2 deletions src/Remote/SftpFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@
namespace Ambimax\File\Remote;

use Ambimax\File\File;
use phpseclib3\Net\SFTP;
use phpseclib3\Net\SFTP\Stream;
use RuntimeException;

class SftpFile extends File
{
protected string $hostname;
protected string $username;
protected string $password;
protected SFTP $sftp;

/**
* @param File::MODE_* $mode
*/
public function __construct(string $hostname, string $username, string $password, string $filePath, string $mode)
{
$this->hostname = $hostname;
$this->username = $username;
$this->password = $password;
$this->sftp = new SFTP($hostname);
$this->sftp->login($username, $password);
parent::__construct($filePath, $mode);
}

Expand All @@ -34,9 +40,23 @@ protected function openStream(string $filePath, string $mode): void
$mode);

if (false === $tmpFileHandle) {
throw new RuntimeException(sprintf('Could not open file \'%s\'.', $filePath));
throw new \RuntimeException(sprintf('Could not open file \'%s\'.', $filePath));
}

$this->fileHandle = $tmpFileHandle;
$this->filePath = $filePath;
$this->mode = $mode;
}

public function rename(string $newPath): bool
{
$newPath = $this->ensureAbsolutePath($newPath);

fclose($this->fileHandle);

$success = $this->sftp->rename($this->filePath, $newPath);
$this->openStream($newPath, $this->mode);

return $success;
}
}
32 changes: 32 additions & 0 deletions src/Test/FileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,36 @@ public function testGetFileHandle(): void
$this->assertIsResource($this->readableFile->getFileHandle());
$this->assertIsNotClosedResource($this->readableFile->getFileHandle());
}

public function testRename(): void
{
$oldFileHandle = $this->readableFile->getFileHandle();

$this->readableFile->rename('newtest');
$this->assertSame($this->root->url().'/newtest', $this->readableFile->getPath());

$newFileHandle = $this->readableFile->getFileHandle();
$this->assertSame('testContent', stream_get_contents($newFileHandle));

$this->assertIsClosedResource($oldFileHandle);
}

public function testMoveToDifferentFolderAbsolutePath(): void
{
$oldFileHandle = $this->readableFile->getFileHandle();

mkdir($this->root->url().'/testFolder');
$this->readableFile->rename($this->root->url().'/testFolder/');
$this->assertSame($this->root->url().'/testFolder/test', $this->readableFile->getPath());
$this->assertFileExists($this->root->url().'/testFolder/test');

$this->readableFile->rename('../testFile');
$this->assertSame($this->root->url().'/testFile', $this->readableFile->getPath());
$this->assertFileExists($this->root->url().'/testFile');

$newFileHandle = $this->readableFile->getFileHandle();
$this->assertSame('testContent', stream_get_contents($newFileHandle));

$this->assertIsClosedResource($oldFileHandle);
}
}

0 comments on commit d4abfb3

Please sign in to comment.