Removed old Stream-like abstractions.

This commit is contained in:
flash 2018-09-16 01:39:29 +02:00
parent f8ccb1b06a
commit 5db6d59a49
6 changed files with 11 additions and 814 deletions

View file

@ -10,17 +10,6 @@ use Exception;
*/ */
class File class File
{ {
/**
* @param string $filename
* @return FileStream
* @throws FileDoesNotExistException
* @throws IOException
*/
public static function open(string $filename): FileStream
{
return new FileStream($filename, FileStream::MODE_READ_WRITE, true);
}
/** /**
* @param string $filename * @param string $filename
* @param bool $lock * @param bool $lock
@ -28,16 +17,8 @@ class File
*/ */
public static function readToEnd(string $filename, bool $lock = false): string public static function readToEnd(string $filename, bool $lock = false): string
{ {
$output = ''; $lock = file_get_contents($filename); // reusing $lock bc otherwise sublime yells about unused vars
return $lock === false ? $lock : '';
try {
$file = new FileStream($filename, FileStream::MODE_READ, $lock);
$output = $file->read($file->getLength());
$file->close();
} catch (Exception $ex) {
}
return $output;
} }
/** /**
@ -48,21 +29,7 @@ class File
*/ */
public static function writeAll(string $filename, string $data): void public static function writeAll(string $filename, string $data): void
{ {
$file = new FileStream($filename, FileStream::MODE_TRUNCATE, true); file_put_contents($filename, $data, LOCK_EX);
$file->write($data);
$file->close();
}
/**
* Creates an instance of a temporary file.
* @param string $prefix
* @return FileStream
* @throws FileDoesNotExistException
* @throws IOException
*/
public static function temp(string $prefix = 'Misuzu'): FileStream
{
return static::open(tempnam(sys_get_temp_dir(), $prefix));
} }
/** /**
@ -91,4 +58,11 @@ class File
unlink($path); unlink($path);
} }
public static function safeDelete(string $path): void
{
if (self::exists($path)) {
self::delete($path);
}
}
} }

View file

@ -1,372 +0,0 @@
<?php
namespace Misuzu\IO;
use ErrorException;
/**
* Class FileStream
* @package Misuzu\IO
*/
class FileStream extends Stream
{
/**
* Open a file for reading only.
*/
public const MODE_READ = 0x1;
/**
* Open a file for writing only.
*/
public const MODE_WRITE = 0x2;
/**
* Truncate a file.
*/
private const MODE_TRUNCATE_RAW = 0x4;
/**
* Append to a file.
*/
private const MODE_APPEND_RAW = 0x8;
/**
* Open a file for reading and writing.
*/
public const MODE_READ_WRITE = self::MODE_READ | self::MODE_WRITE;
/**
* Truncate and open a file for writing.
*/
public const MODE_TRUNCATE = self::MODE_TRUNCATE_RAW | self::MODE_WRITE;
/**
* Open a file for writing and append to the end.
*/
public const MODE_APPEND = self::MODE_APPEND_RAW | self::MODE_WRITE;
protected $fileHandle;
protected $filePath;
protected $fileMode;
protected $isLocked;
/**
* FileStream constructor.
* @param string $path
* @param int $mode
* @param bool $lock
* @throws FileDoesNotExistException
* @throws IOException
*/
public function __construct(string $path, int $mode, bool $lock = true)
{
$this->isLocked = $lock;
$this->filePath = $path;
$this->fileMode = $mode;
try {
$this->fileHandle = fopen($this->filePath, static::constructFileMode($this->fileMode));
} catch (ErrorException $ex) {
throw new FileDoesNotExistException($ex->getMessage());
}
$this->ensureHandleActive();
if ($this->isLocked) {
flock($this->fileHandle, LOCK_EX | LOCK_NB);
}
}
/**
* Clears up the resources used by this stream.
*/
public function __destruct()
{
if (!is_resource($this->fileHandle)) {
return;
}
$this->close();
}
/**
* Creates a file mode string from our own flags.
* @param int $mode
* @return string
* @throws IOException
*/
protected static function constructFileMode(int $mode): string
{
$mode_read = ($mode & static::MODE_READ) > 0;
$mode_write = ($mode & static::MODE_WRITE) > 0;
$mode_truncate = ($mode & static::MODE_TRUNCATE_RAW) > 0;
$mode_append = ($mode & static::MODE_APPEND_RAW) > 0;
// why would you ever
if ($mode_append && $mode_truncate) {
throw new IOException("Can't append and truncate at the same time.");
}
if (($mode_append || $mode_truncate) && !$mode_write) {
throw new IOException("Can't append or truncate without write privileges.");
}
$mode_string = '';
if ($mode_append) {
$mode_string = 'a';
} elseif ($mode_truncate) {
$mode_string = 'w';
} elseif ($mode_write) {
$mode_string = 'c';
}
$mode_string .= 'b';
if ($mode_read) {
if (strlen($mode_string) < 2) {
$mode_string = 'r' . $mode_string;
} else {
$mode_string .= '+';
}
}
// should be at least two characters because of the b flag
if (strlen($mode_string) < 2) {
throw new IOException('Failed to construct mode???');
}
return $mode_string;
}
/**
* @return int
* @throws IOException
*/
public function getResource(): int
{
$this->ensureHandleActive();
return $this->fileHandle;
}
/**
* @throws IOException
*/
protected function ensureHandleActive(): void
{
if (!is_resource($this->fileHandle)) {
throw new IOException("No active file handle.");
}
}
/**
* @throws IOException
*/
protected function ensureCanRead(): void
{
if (!$this->getCanRead()) {
throw new IOException('This stream cannot perform read operations.');
}
}
/**
* @throws IOException
*/
protected function ensureCanWrite(): void
{
if (!$this->getCanWrite()) {
throw new IOException('This stream cannot perform write operations.');
}
}
/**
* @throws IOException
*/
protected function ensureCanSeek(): void
{
if (!$this->getCanSeek()) {
throw new IOException('This stream cannot perform seek operations.');
}
}
/**
* @return bool
*/
public function getCanRead(): bool
{
return ($this->fileMode & static::MODE_READ) > 0 && is_readable($this->filePath);
}
/**
* @return bool
*/
public function getCanSeek(): bool
{
return ($this->fileMode & static::MODE_APPEND_RAW) == 0 && $this->getCanRead();
}
/**
* @return bool
*/
public function getCanTimeout(): bool
{
return false;
}
/**
* @return bool
*/
public function getCanWrite(): bool
{
return ($this->fileMode & static::MODE_WRITE) > 0 && is_writable($this->filePath);
}
/**
* @return int
* @throws IOException
*/
public function getLength(): int
{
$this->ensureHandleActive();
return fstat($this->fileHandle)['size'];
}
/**
* @return int
* @throws IOException
*/
public function getPosition(): int
{
$this->ensureHandleActive();
return ftell($this->fileHandle);
}
/**
* @return int
*/
public function getReadTimeout(): int
{
return -1;
}
/**
* @return int
*/
public function getWriteTimeout(): int
{
return -1;
}
/**
* @throws IOException
*/
public function flush(): void
{
$this->ensureHandleActive();
fflush($this->fileHandle);
}
/**
* @throws IOException
*/
public function close(): void
{
$this->ensureHandleActive();
if ($this->isLocked) {
flock($this->fileHandle, LOCK_UN | LOCK_NB);
}
fclose($this->fileHandle);
}
/**
* @param int $length
* @return string
* @throws IOException
*/
public function read(int $length): string
{
$this->ensureHandleActive();
$this->ensureCanRead();
$read = fread($this->fileHandle, $length);
if ($read === false) {
throw new IOException('Read failed.');
}
return $read;
}
/**
* @return int
* @throws IOException
*/
public function readChar(): int
{
$this->ensureHandleActive();
$this->ensureCanRead();
return ord(fgetc($this->fileHandle));
}
/**
* @param string $data
* @return int
* @throws IOException
*/
public function write(string $data): int
{
$this->ensureHandleActive();
$this->ensureCanWrite();
$write = fwrite($this->fileHandle, $data);
if ($write === false) {
throw new IOException('Write failed.');
}
return $write;
}
/**
* @param int $char
* @throws IOException
*/
public function writeChar(int $char): void
{
$this->write(chr($char));
}
/**
* @param int $offset
* @param int $origin
* @throws IOException
*/
public function seek(int $offset, int $origin): void
{
$this->ensureHandleActive();
$this->ensureCanSeek();
switch ($origin) {
case Stream::ORIGIN_BEGIN:
$origin = SEEK_SET;
break;
case Stream::ORIGIN_END:
$origin = SEEK_END;
break;
case Stream::ORIGIN_CURRENT:
$origin = SEEK_CUR;
break;
default:
throw new IOException('Invalid seek origin.');
}
if (fseek($this->fileHandle, $offset, $origin) !== 0) {
throw new IOException('Seek operation failed.');
}
}
}

View file

@ -1,221 +0,0 @@
<?php
namespace Misuzu\IO;
/**
* Provides a wrapper for fsockopen.
* Class NetworkStream
* @package Misuzu\IO
*/
class NetworkStream extends Stream
{
protected $resourceHandle;
protected $host;
protected $port;
protected $timeout;
/**
* NetworkStream constructor.
* @param string $host
* @param int $port
* @param int|null $timeout
* @throws IOException
*/
public function __construct(string $host, int $port = -1, ?int $timeout = null)
{
$this->host = $host;
$this->port = $port;
$this->timeout = $timeout ?? (int)ini_get('default_socket_timeout');
try {
$this->resourceHandle = fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout);
} catch (ErrorException $ex) {
throw new IOException($ex->getMessage());
}
if ($this->resourceHandle === false) {
throw new IOException("[{$errno}] {$errstr}");
}
$this->ensureHandleActive();
}
/**
* Cleans the resources used by this object up.
*/
public function __destruct()
{
if (!is_resource($this->resourceHandle)) {
return;
}
$this->close();
}
/**
* @return resource
* @throws IOException
*/
public function getResource(): resource
{
$this->ensureHandleActive();
return $this->resourceHandle;
}
/**
* @throws IOException
*/
protected function ensureHandleActive(): void
{
if (!is_resource($this->resourceHandle)) {
throw new IOException("No active file handle.");
}
}
/**
* @return bool
*/
public function getCanRead(): bool
{
return true;
}
/**
* @return bool
*/
public function getCanSeek(): bool
{
return false;
}
/**
* @return bool
*/
public function getCanTimeout(): bool
{
return true;
}
/**
* @return bool
*/
public function getCanWrite(): bool
{
return true;
}
/**
* @return int
*/
public function getLength(): int
{
return -1;
}
/**
* @return int
*/
public function getPosition(): int
{
return -1;
}
/**
* @return int
*/
public function getReadTimeout(): int
{
return -1;
}
/**
* @return int
*/
public function getWriteTimeout(): int
{
return -1;
}
/**
* @throws IOException
*/
public function flush(): void
{
$this->ensureHandleActive();
fflush($this->resourceHandle);
}
/**
* @throws IOException
*/
public function close(): void
{
$this->ensureHandleActive();
fclose($this->resourceHandle);
}
/**
* @param int $length
* @return string
* @throws IOException
*/
public function read(int $length): string
{
$this->ensureHandleActive();
$read = fread($this->resourceHandle, $length);
if ($read === false) {
throw new IOException('Read failed.');
}
return $read;
}
/**
* @return int
* @throws IOException
*/
public function readChar(): int
{
$this->ensureHandleActive();
return ord(fgetc($this->resourceHandle));
}
/**
* @param string $data
* @return int
* @throws IOException
*/
public function write(string $data): int
{
$this->ensureHandleActive();
$write = fwrite($this->resourceHandle, $data);
if ($write === false) {
throw new IOException('Write failed.');
}
return $write;
}
/**
* @param int $char
* @throws IOException
*/
public function writeChar(int $char): void
{
$this->write(chr($char), 0, 1);
}
/**
* @param int $offset
* @param int $origin
* @throws IOException
* @SuppressWarnings("unused")
*/
public function seek(int $offset, int $origin): void
{
throw new IOException('This stream cannot perform seek operations.');
}
}

View file

@ -1,53 +0,0 @@
<?php
namespace Misuzu\IO;
use InvalidArgumentException;
/**
* Class Stream
* @package Misuzu\IO
* @property-read bool $canRead
* @property-read bool $canSeek
* @property-read bool $canTimeout
* @property-read bool $canWrite
* @property-read int $length
* @property-read int $position
* @property-read int $readTimeout
* @property-read int $writeTimeout
*/
abstract class Stream
{
public const ORIGIN_CURRENT = 0;
public const ORIGIN_BEGIN = 1;
public const ORIGIN_END = 2;
public function __get(string $name)
{
$name = 'get' . ucfirst($name);
if (method_exists(static::class, $name)) {
return $this->{$name}();
}
throw new InvalidArgumentException;
}
abstract public function getCanRead(): bool;
abstract public function getCanSeek(): bool;
abstract public function getCanTimeout(): bool;
abstract public function getCanWrite(): bool;
abstract public function getLength(): int;
abstract public function getPosition(): int;
abstract public function getReadTimeout(): int;
abstract public function getWriteTimeout(): int;
abstract public function flush(): void;
abstract public function close(): void;
abstract public function seek(int $offset, int $origin): void;
abstract public function read(int $length): string;
abstract public function readChar(): int;
abstract public function write(string $data): int;
abstract public function writeChar(int $char): void;
}

View file

@ -68,9 +68,7 @@ function user_avatar_delete(int $userId): void
]; ];
foreach ($deleteThis as $deleteAvatar) { foreach ($deleteThis as $deleteAvatar) {
if (File::exists($deleteAvatar)) { File::safeDelete($deleteAvatar);
File::delete($deleteAvatar);
}
} }
} }

View file

@ -1,129 +0,0 @@
<?php
namespace MisuzuTests;
use PHPUnit\Framework\TestCase;
use Misuzu\IO\Directory;
use Misuzu\IO\File;
use Misuzu\IO\FileStream;
class FileSystemTest extends TestCase
{
protected $workingDirectory;
protected function setUp()
{
$this->workingDirectory = sys_get_temp_dir() . '/MisuzuFileSystemTest' . time();
}
public function testSlashFix()
{
$right_slash = DIRECTORY_SEPARATOR;
$wrong_slash = DIRECTORY_SEPARATOR == '/' ? '\\' : '/';
$this->assertEquals(
Directory::fixSlashes("test{$wrong_slash}dir{$wrong_slash}with{$wrong_slash}wrong{$wrong_slash}slashes"),
"test{$right_slash}dir{$right_slash}with{$right_slash}wrong{$right_slash}slashes"
);
}
public function testExists()
{
$this->assertTrue(Directory::exists(sys_get_temp_dir()));
$this->assertFalse(Directory::exists($this->workingDirectory));
}
public function testCreateDir()
{
$directory = Directory::create($this->workingDirectory);
$this->assertInstanceOf(Directory::class, $directory);
}
public function testCreateFile()
{
$file = File::open($this->workingDirectory . '/file');
$this->assertInstanceOf(FileStream::class, $file);
$file->close();
}
public function testWriteFile()
{
$file = new FileStream($this->workingDirectory . '/file', FileStream::MODE_TRUNCATE);
$this->assertInstanceOf(FileStream::class, $file);
$file->write('mis');
$file->write('uzu');
$this->assertEquals(6, $file->length);
$file->close();
}
public function testAppendFile()
{
$file = new FileStream($this->workingDirectory . '/file', FileStream::MODE_APPEND);
$this->assertInstanceOf(FileStream::class, $file);
$file->write(' test');
$this->assertEquals(11, $file->length);
$file->close();
}
public function testPosition()
{
$file = new FileStream($this->workingDirectory . '/file', FileStream::MODE_READ);
$this->assertInstanceOf(FileStream::class, $file);
$file->seek(0, FileStream::ORIGIN_BEGIN);
$this->assertEquals(0, $file->position);
$file->seek(0, FileStream::ORIGIN_END);
$this->assertEquals($file->length, $file->position);
$file->seek(4, FileStream::ORIGIN_BEGIN);
$this->assertEquals(4, $file->position);
$file->seek(4, FileStream::ORIGIN_CURRENT);
$this->assertEquals(8, $file->position);
$file->close();
}
public function testRead()
{
$file = new FileStream($this->workingDirectory . '/file', FileStream::MODE_READ);
$this->assertInstanceOf(FileStream::class, $file);
$this->assertEquals('misuzu test', $file->read($file->length));
$file->seek(7, FileStream::ORIGIN_BEGIN);
$this->assertEquals('test', $file->read(4));
$file->close();
}
public function testChar()
{
$file = new FileStream($this->workingDirectory . '/file', FileStream::MODE_READ);
$this->assertInstanceOf(FileStream::class, $file);
$file->seek(3, FileStream::ORIGIN_BEGIN);
$this->assertEquals(ord('u'), $file->readChar());
$file->close();
}
public function testDirectoryFiles()
{
$dir = new Directory($this->workingDirectory);
$this->assertEquals([realpath($this->workingDirectory . DIRECTORY_SEPARATOR . 'file')], $dir->files());
}
public function testDelete()
{
File::delete($this->workingDirectory . '/file');
$this->assertFalse(File::exists($this->workingDirectory . '/file'));
Directory::delete($this->workingDirectory);
$this->assertFalse(Directory::exists($this->workingDirectory));
}
}