-
Notifications
You must be signed in to change notification settings - Fork 1
Home
Sara Golemon edited this page Mar 24, 2018
·
16 revisions
- Object based streams
- User wrapper/filter/transport via Interfaces
- Make wrapper/transport separation clear
- "Better" contexts
- Filter attachment "not stupid"
- Support for async/concurrency
- async
- objects allow for cloning and immutability
- filtering working properly with added features
- error handling fixes
interface Stream {
/* e.g. new TCP('tcp://localhost:80'); */
public function __construct(string $uri, string $mode, ?Stream\Context $ctx = null);
public function getName(): string;
public function getUri(): string; // "opened path"
public function isOpen(): bool;
public function read(int $maxbytes = 0): string;
public function write(string $data): int; /* bytes written */
public function flush(): bool;
public function close(): void;
public function seek(int $pos, int $whence = Stream::SEEK_SET): bool;
public function isSeekable(): bool;
public function stat(): Stream\StatBuf;
public function setOption(int $option, $value): bool;
public function getStream(): Stream; /* For proxy transports */
}
namespace Stream {
const SEEK_SET = 0;
const SEEK_CUR = 1;
const SEEK_END = 2;
function Open(string $uri, string $mode,
int $options, ?Stream\Context $ctx = null): Stream;
interface Transport {
// TBD: connect/accept/listen/bind/sendto/recvfrom/oob/etc...
}
namespace Transport {
function register(string $scheme,
(function (string $uri, ?Stream\Context $ctx): Stream) $factory): bool;
function unregister(string $scheme): bool;
function exists(string $scheme): bool;
} // namespace Transport
namespace Wrapper {
function register(string $scheme,
(function (string $uri, string $mode, ?\Stream\Context $ctx = null): Stream\Transport)): bool;
function unregister(string $scheme): bool;
function exists(string $scheme): bool;
function getWrapper(string $scheme): Stream\Wrapper;
} // namespace Wrapper
interface Wrapper {
public function getName(): string;
public function open(string $uri, string $mode, ?Stream\Context $ctx = null): Stream;
public function opendir(string $uri, int $flags = 0, ?Stream\Context $ctx = null): Stream;
public function stat(string $uri, int $flags = 0, ?Stream\Context $ctx = null): Stream\StatBuf;
public function unlink(string $uri): bool;
public function move(string $from, string $to): bool;
public function copy(string $from, string $to): bool;
public function mkdir(string $uri, bool $recursive = true, int $mode = 0644): bool;
public function rmdir(string $uri): bool;
public function touch(string $uri): bool;
public function chmod(string $uri, int $mode): bool;
public function chusr(string $uri, string $user): bool;
public function chgrp(string $uri, string $group): bool;
}
namespace Context {
const READ_FILTER = 'read_filter';
const WRITE_FILTER = 'write_filter';
const NOTIFIER = 'notifier';
} // namespace Context
interface Filter {
// This method can't be named "filter" because of PHP4 constructors. *sigh*
public function doFilter(string $str): string;
}
interface NotificationListener {
// Provisional API based on current notifiers; Subject to change.
public function notify(int $code, int $sev, string $msg, int $msg_code, int $bytes_xferred, int $bytes_max): void;
}
trait TContext {
public function getOption(string $scheme, string $option): mixed {
$opts = $this->getSchemeOptions($sceme);
if (!array_key_exists($option, $opts)) {
throw new NotFoundException("Unknown option $opt for scheme $scheme");
}
retur $opts[$option];
}
public function getSchemeOptions(string $scheme): array<mixed> {
$opts = $this->getAllOptions();
if (!array_key_exists($scheme, $opts)) {
throw new NotFoundException("Unknown scheme $scheme");
}
return $opts[$scheme];
}
public function getAllOptions(): array<string, array<string, mixed>> {
return [];
}
public function getParam(string $param): mixed {
$params = $this->getAllParams();
if (!array_key_exists($param, $params)) {
throw new NotFoundException("Unknown context param $param");
}
return $params[$param];
}
public function getAllParams(): array<string, mixed> {
return [];
}
}
interface Context {
// Options apply to specific wrappers/transports.
// Example options: [ 'http' => [ 'user_agent' => 'Mozilla' ] ]
public function getOption(string $scheme, string $option): mixed;
public function getSchemeOptions(string $scheme): array<string, mixed>;
public function getAllOptions(): array<string, array<string, mixed>>;
// Params apply to all streams equally.
// Example params: [ Context::READ_FILTER => Stream\Filter ]
// : [ Context::WRITE_FILTER => Stream\Filter ]
// : [ Context::NOTIFIER => Stream\NotificationListener ]
public function getParam(string $param): mixed;
public function getAllParams(): array<string, mixed>;
}
interface StatBuf {}
interface StatBuf\Sizable {
public function getSize(): int;
}
interface StatBuf\File {
public function getCTime(): int; // Epochs? DateTime?
public function getMTime(): int;
public function getATime(): int;
}
interface StatBuf\PosixFile extends StatBuf\File {
public function getMode(): int;
public function getDevice(): int;
public function getInode(): int;
public function getNLinks(): int;
public function getUid(): int;
public function getGid(): int;
public function getRdev(): int;
public function getBlockSize(): int;
public function getBlocks(): int;
}
interface StatBuf\NTFSFile extends StatBuf\File {
// TBD
}
// Base exception for all Streams ops.
interface Exception extends \Exception {}
// Provisional exception specializations.
interface NotFoundException extends Exception {} // File not found, wrapper unknown, etc...
interface ConnectionResetException extends Exception {} // Disconnected.
interface AccessException extends Exception {} // File exists, but you can't have it.
interface StorageException extends Exception {} // Disk full, OOM, etc...
interface LockException extends Exception {} // Unable to acquire lock, or resource currently locked.
} // namespace Stream