PK ,AV}\ phpunit.xml.distnu W+A
test
lib
PK ,AV< lib/ParallelDriver.phpnu W+A pool = $pool ?: Worker\pool();
}
/**
* {@inheritdoc}
*/
public function open(string $path, string $mode): Promise
{
return call(function () use ($path, $mode) {
$worker = $this->pool->getWorker();
try {
list($id, $size, $mode) = yield $worker->enqueue(new Internal\FileTask("fopen", [$path, $mode]));
} catch (TaskException $exception) {
throw new FilesystemException("Could not open file", $exception);
} catch (WorkerException $exception) {
throw new FilesystemException("Could not send open request to worker", $exception);
}
return new ParallelHandle($worker, $id, $path, $size, $mode);
});
}
private function runFileTask(Internal\FileTask $task): \Generator
{
try {
return yield $this->pool->enqueue($task);
} catch (TaskException $exception) {
throw new FilesystemException("The file operation failed", $exception);
} catch (WorkerException $exception) {
throw new FilesystemException("Could not send the file task to worker", $exception);
}
}
/**
* {@inheritdoc}
*/
public function unlink(string $path): Promise
{
return call(function () use ($path) {
$result = yield from $this->runFileTask(new Internal\FileTask("unlink", [$path]));
StatCache::clear($path);
return $result;
});
}
/**
* {@inheritdoc}
*/
public function stat(string $path): Promise
{
if ($stat = StatCache::get($path)) {
return new Success($stat);
}
return call(function () use ($path) {
$stat = yield from $this->runFileTask(new Internal\FileTask("stat", [$path]));
if (!empty($stat)) {
StatCache::set($path, $stat);
}
return $stat;
});
}
/**
* {@inheritdoc}
*/
public function rename(string $from, string $to): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("rename", [$from, $to])));
}
/**
* {@inheritdoc}
*/
public function isfile(string $path): Promise
{
return call(function () use ($path) {
$stat = yield $this->stat($path);
if (empty($stat)) {
return false;
}
if ($stat["mode"] & 0100000) {
return true;
}
return false;
});
}
/**
* {@inheritdoc}
*/
public function isdir(string $path): Promise
{
return call(function () use ($path) {
$stat = yield $this->stat($path);
if (empty($stat)) {
return false;
}
if ($stat["mode"] & 0040000) {
return true;
}
return false;
});
}
/**
* {@inheritdoc}
*/
public function link(string $target, string $link): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("link", [$target, $link])));
}
/**
* {@inheritdoc}
*/
public function symlink(string $target, string $link): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("symlink", [$target, $link])));
}
/**
* {@inheritdoc}
*/
public function readlink(string $path): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("readlink", [$path])));
}
/**
* {@inheritdoc}
*/
public function mkdir(string $path, int $mode = 0777, bool $recursive = false): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("mkdir", [$path, $mode, $recursive])));
}
/**
* {@inheritdoc}
*/
public function scandir(string $path): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("scandir", [$path])));
}
/**
* {@inheritdoc}
*/
public function rmdir(string $path): Promise
{
return call(function () use ($path) {
$result = yield from $this->runFileTask(new Internal\FileTask("rmdir", [$path]));
StatCache::clear($path);
return $result;
});
}
/**
* {@inheritdoc}
*/
public function chmod(string $path, int $mode): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("chmod", [$path, $mode])));
}
/**
* {@inheritdoc}
*/
public function chown(string $path, int $uid, int $gid): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("chown", [$path, $uid, $gid])));
}
/**
* {@inheritdoc}
*/
public function exists(string $path): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("exists", [$path])));
}
/**
* {@inheritdoc}
*/
public function size(string $path): Promise
{
return call(function () use ($path) {
$stat = yield $this->stat($path);
if (empty($stat)) {
throw new FilesystemException("Specified path does not exist");
}
if ($stat["mode"] & 0100000) {
return $stat["size"];
}
throw new FilesystemException("Specified path is not a regular file");
});
}
/**
* {@inheritdoc}
*/
public function mtime(string $path): Promise
{
return call(function () use ($path) {
$stat = yield $this->stat($path);
if (empty($stat)) {
throw new FilesystemException("Specified path does not exist");
}
return $stat["mtime"];
});
}
/**
* {@inheritdoc}
*/
public function atime(string $path): Promise
{
return call(function () use ($path) {
$stat = yield $this->stat($path);
if (empty($stat)) {
throw new FilesystemException("Specified path does not exist");
}
return $stat["atime"];
});
}
/**
* {@inheritdoc}
*/
public function ctime(string $path): Promise
{
return call(function () use ($path) {
$stat = yield $this->stat($path);
if (empty($stat)) {
throw new FilesystemException("Specified path does not exist");
}
return $stat["ctime"];
});
}
/**
* {@inheritdoc}
*/
public function lstat(string $path): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("lstat", [$path])));
}
/**
* {@inheritdoc}
*/
public function touch(string $path, int $time = null, int $atime = null): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("touch", [$path, $time, $atime])));
}
/**
* {@inheritdoc}
*/
public function get(string $path): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("get", [$path])));
}
/**
* {@inheritdoc}
*/
public function put(string $path, string $contents): Promise
{
return new Coroutine($this->runFileTask(new Internal\FileTask("put", [$path, $contents])));
}
}
PK ,AVÒ lib/Driver.phpnu W+A
*/
public function open(string $path, string $mode): Promise;
/**
* Execute a file stat operation.
*
* If the requested path does not exist the resulting Promise will resolve to NULL.
*
* @param string $path The file system path to stat
* @return \Amp\Promise
*/
public function stat(string $path): Promise;
/**
* Does the specified path exist?
*
* This function should never resolve as a failure -- only a successfull bool value
* indicating the existence of the specified path.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function exists(string $path): Promise;
/**
* Retrieve the size in bytes of the file at the specified path.
*
* If the path does not exist or is not a regular file this
* function's returned Promise WILL resolve as a failure.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function size(string $path): Promise;
/**
* Does the specified path exist and is it a directory?
*
* If the path does not exist the returned Promise will resolve
* to FALSE and will not reject with an error.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function isdir(string $path): Promise;
/**
* Does the specified path exist and is it a file?
*
* If the path does not exist the returned Promise will resolve
* to FALSE and will not reject with an error.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function isfile(string $path): Promise;
/**
* Retrieve the path's last modification time as a unix timestamp.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function mtime(string $path): Promise;
/**
* Retrieve the path's last access time as a unix timestamp.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function atime(string $path): Promise;
/**
* Retrieve the path's creation time as a unix timestamp.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function ctime(string $path): Promise;
/**
* Same as stat() except if the path is a link then the link's data is returned.
*
* @param string $path The file system path to stat
* @return \Amp\Promise A promise resolving to an associative array upon successful resolution
*/
public function lstat(string $path): Promise;
/**
* Create a symlink $link pointing to the file/directory located at $target.
*
* @param string $target
* @param string $link
* @return \Amp\Promise
*/
public function symlink(string $target, string $link): Promise;
/**
* Create a hard link $link pointing to the file/directory located at $target.
*
* @param string $target
* @param string $link
* @return \Amp\Promise
*/
public function link(string $target, string $link): Promise;
/**
* Read the symlink at $path.
*
* @param string $target
* @return \Amp\Promise
*/
public function readlink(string $target): Promise;
/**
* Rename a file or directory.
*
* @param string $from
* @param string $to
* @return \Amp\Promise
*/
public function rename(string $from, string $to): Promise;
/**
* Delete a file.
*
* @param string $path
* @return \Amp\Promise
*/
public function unlink(string $path): Promise;
/**
* Create a director.
*
* @param string $path
* @param int $mode
* @param bool $recursive
* @return \Amp\Promise
*/
public function mkdir(string $path, int $mode = 0777, bool $recursive = false): Promise;
/**
* Delete a directory.
*
* @param string $path
* @return \Amp\Promise
*/
public function rmdir(string $path): Promise;
/**
* Retrieve an array of files and directories inside the specified path.
*
* Dot entries are not included in the resulting array (i.e. "." and "..").
*
* @param string $path
* @return \Amp\Promise
*/
public function scandir(string $path): Promise;
/**
* chmod a file or directory.
*
* @param string $path
* @param int $mode
* @return \Amp\Promise
*/
public function chmod(string $path, int $mode): Promise;
/**
* chown a file or directory.
*
* @param string $path
* @param int $uid
* @param int $gid
* @return \Amp\Promise
*/
public function chown(string $path, int $uid, int $gid): Promise;
/**
* Update the access and modification time of the specified path.
*
* If the file does not exist it will be created automatically.
*
* @param string $path
* @param int $time The touch time. If $time is not supplied, the current system time is used.
* @param int $atime The access time. If $atime is not supplied, value passed to the $time parameter is used.
* @return \Amp\Promise
*/
public function touch(string $path, int $time = null, int $atime = null): Promise;
/**
* Buffer the specified file's contents.
*
* @param string $path The file path from which to buffer contents
* @return \Amp\Promise A promise resolving to a string upon successful resolution
*/
public function get(string $path): Promise;
/**
* Write the contents string to the specified path.
*
* @param string $path The file path to which to $contents should be written
* @param string $contents The data to write to the specified $path
* @return \Amp\Promise A promise resolving to the integer length written upon success
*/
public function put(string $path, string $contents): Promise;
}
PK ,AVV`f lib/Handle.phpnu W+A
*/
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise;
/**
* Write $data to the open file handle starting at $offset.
*
* @param string $data
* @return \Amp\Promise
*/
public function write(string $data): Promise;
/**
* Write $data to the open file handle and close the handle once the write completes.
*
* @param string $data
*
* @return \Amp\Promise
*/
public function end(string $data = ""): Promise;
/**
* Close the file handle.
*
* Applications are not required to manually close handles -- they will
* be unloaded automatically when the object is garbage collected.
*
* @return \Amp\Promise
*/
public function close(): Promise;
/**
* Set the handle's internal pointer position.
*
* $whence values:
*
* SEEK_SET - Set position equal to offset bytes.
* SEEK_CUR - Set position to current location plus offset.
* SEEK_END - Set position to end-of-file plus offset.
*
* @param int $position
* @param int $whence
* @return \Amp\Promise New offset position.
*/
public function seek(int $position, int $whence = \SEEK_SET): Promise;
/**
* Return the current internal offset position of the file handle.
*
* @return int
*/
public function tell(): int;
/**
* Test for "end-of-file" on the file handle.
*
* @return bool
*/
public function eof(): bool;
/**
* Retrieve the path used when opening the file handle.
*
* @return string
*/
public function path(): string;
/**
* Retrieve the mode used when opening the file handle.
*
* @return string
*/
public function mode(): string;
}
PK ,AVh
lib/Internal/EioPoll.phpnu W+A onDone = $this->callableFromInstanceMethod("done");
if (!self::$stream) {
\eio_init();
self::$stream = \eio_get_event_stream();
}
$this->watcher = Loop::onReadable(self::$stream, static function () {
while (\eio_npending()) {
\eio_poll();
}
});
Loop::disable($this->watcher);
Loop::setState(self::class, new class($this->watcher) {
private $watcher;
public function __construct(string $watcher)
{
$this->watcher = $watcher;
}
public function __destruct()
{
Loop::cancel($this->watcher);
// Ensure there are no active operations anymore. This is a safe-guard as some operations might not be
// finished on loop exit due to not being yielded. This also ensures a clean shutdown for these if PHP
// exists.
\eio_event_loop();
}
});
}
public function listen(Promise $promise)
{
if ($this->requests++ === 0) {
Loop::enable($this->watcher);
}
$promise->onResolve($this->onDone);
}
private function done()
{
if (--$this->requests === 0) {
Loop::disable($this->watcher);
}
\assert($this->requests >= 0);
}
}
PK ,AV. lib/Internal/UvPoll.phpnu W+A onDone = $this->callableFromInstanceMethod("done");
$this->watcher = Loop::repeat(\PHP_INT_MAX / 2, function () {
// do nothing, it's a dummy watcher
});
Loop::disable($this->watcher);
Loop::setState(self::class, new class($this->watcher) {
private $watcher;
public function __construct(string $watcher)
{
$this->watcher = $watcher;
}
public function __destruct()
{
Loop::cancel($this->watcher);
}
});
}
public function listen(Promise $promise)
{
if ($this->requests++ === 0) {
Loop::enable($this->watcher);
}
$promise->onResolve($this->onDone);
}
private function done()
{
if (--$this->requests === 0) {
Loop::disable($this->watcher);
}
\assert($this->requests >= 0);
}
}
PK ,AV lib/Internal/FileTask.phpnu W+A operation = $operation;
$this->args = $args;
$this->id = $id;
}
/**
* {@inheritdoc}
*
* @throws \Amp\File\FilesystemException
* @throws \Error
*/
public function run(Environment $environment)
{
if ('f' === $this->operation[0]) {
if ("fopen" === $this->operation) {
$path = $this->args[0];
$mode = \str_replace(['b', 't', 'e'], '', $this->args[1]);
switch ($mode) {
case "r":
case "r+":
case "w":
case "w+":
case "a":
case "a+":
case "x":
case "x+":
case "c":
case "c+":
break;
default:
throw new \Error("Invalid file mode");
}
$handle = @\fopen($path, $mode . 'be');
if (!$handle) {
$message = 'Could not open the file.';
if ($error = \error_get_last()) {
$message .= \sprintf(" Errno: %d; %s", $error["type"], $error["message"]);
}
throw new FilesystemException($message);
}
$file = new BlockingHandle($handle, $path, $mode);
$id = (int) $handle;
$size = \fstat($handle)["size"];
$environment->set(self::makeId($id), $file);
return [$id, $size, $mode];
}
if ($this->id === null) {
throw new FilesystemException("No file ID provided");
}
$id = self::makeId($this->id);
if (!$environment->exists($id)) {
throw new FilesystemException(\sprintf("No file handle with the ID %d has been opened on the worker", $this->id));
}
/** @var \Amp\File\BlockingHandle $file */
if (!($file = $environment->get($id)) instanceof BlockingHandle) {
throw new FilesystemException("File storage found in inconsistent state");
}
switch ($this->operation) {
case "fread":
case "fwrite":
case "fseek":
return ([$file, \substr($this->operation, 1)])(...$this->args);
case "fclose":
$environment->delete($id);
$file->close();
return;
default:
throw new \Error('Invalid operation');
}
}
StatCache::clear();
switch ($this->operation) {
case "stat":
case "unlink":
case "rename":
case "link":
case "symlink":
case "readlink":
case "lstat":
case "exists":
case "mkdir":
case "scandir":
case "rmdir":
case "chmod":
case "chown":
case "touch":
case "get":
case "put":
return ([new BlockingDriver, $this->operation])(...$this->args);
default:
throw new \Error("Invalid operation");
}
}
/**
* @param int $id
*
* @return string
*/
private static function makeId(int $id): string
{
return self::ENV_PREFIX . $id;
}
}
PK ,AV3RyM M lib/ParallelHandle.phpnu W+A worker = $worker;
$this->id = $id;
$this->path = $path;
$this->size = $size;
$this->mode = $mode;
$this->position = $this->mode[0] === 'a' ? $this->size : 0;
}
public function __destruct()
{
if ($this->id !== null) {
$this->close();
}
}
/**
* {@inheritdoc}
*/
public function path(): string
{
return $this->path;
}
/**
* {@inheritdoc}
*/
public function close(): Promise
{
if ($this->closing) {
return $this->closing;
}
$this->writable = false;
if ($this->worker->isRunning()) {
$this->closing = $this->worker->enqueue(new Internal\FileTask('fclose', [], $this->id));
$this->id = null;
} else {
$this->closing = new Success;
}
return $this->closing;
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
return $this->pendingWrites === 0 && $this->size <= $this->position;
}
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise
{
if ($this->id === null) {
throw new ClosedException("The file has been closed");
}
if ($this->busy) {
throw new PendingOperationError;
}
return new Coroutine($this->doRead($length));
}
private function doRead(int $length): \Generator
{
$this->busy = true;
try {
$data = yield $this->worker->enqueue(new Internal\FileTask('fread', [$length], $this->id));
$this->position += \strlen($data);
return $data;
} catch (TaskException $exception) {
throw new StreamException("Reading from the file failed", 0, $exception);
} catch (WorkerException $exception) {
throw new StreamException("Sending the task to the worker failed", 0, $exception);
} finally {
$this->busy = false;
}
}
/**
* {@inheritdoc}
*/
public function write(string $data): Promise
{
if ($this->id === null) {
throw new ClosedException("The file has been closed");
}
if ($this->busy && $this->pendingWrites === 0) {
throw new PendingOperationError;
}
if (!$this->writable) {
throw new ClosedException("The file is no longer writable");
}
return new Coroutine($this->doWrite($data));
}
/**
* {@inheritdoc}
*/
public function end(string $data = ""): Promise
{
return call(function () use ($data) {
$promise = $this->write($data);
$this->writable = false;
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
private function doWrite(string $data): \Generator
{
++$this->pendingWrites;
$this->busy = true;
try {
$length = yield $this->worker->enqueue(new Internal\FileTask('fwrite', [$data], $this->id));
} catch (TaskException $exception) {
throw new StreamException("Writing to the file failed", 0, $exception);
} catch (WorkerException $exception) {
throw new StreamException("Sending the task to the worker failed", 0, $exception);
} finally {
if (--$this->pendingWrites === 0) {
$this->busy = false;
}
}
$this->position += $length;
return $length;
}
/**
* {@inheritdoc}
*/
public function seek(int $offset, int $whence = SEEK_SET): Promise
{
if ($this->id === null) {
throw new ClosedException("The file has been closed");
}
if ($this->busy) {
throw new PendingOperationError;
}
return new Coroutine($this->doSeek($offset, $whence));
}
private function doSeek(int $offset, int $whence)
{
switch ($whence) {
case \SEEK_SET:
case \SEEK_CUR:
case \SEEK_END:
try {
$this->position = yield $this->worker->enqueue(
new Internal\FileTask('fseek', [$offset, $whence], $this->id)
);
if ($this->position > $this->size) {
$this->size = $this->position;
}
return $this->position;
} catch (TaskException $exception) {
throw new StreamException('Seeking in the file failed.', 0, $exception);
} catch (WorkerException $exception) {
throw new StreamException("Sending the task to the worker failed", 0, $exception);
}
default:
throw new \Error('Invalid whence value. Use SEEK_SET, SEEK_CUR, or SEEK_END.');
}
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function size(): int
{
return $this->size;
}
/**
* {@inheritdoc}
*/
public function mode(): string
{
return $this->mode;
}
}
PK ,AVR lib/UvHandle.phpnu W+A poll = $poll;
$this->fh = $fh;
$this->path = $path;
$this->mode = $mode;
$this->size = $size;
$this->loop = $driver->getHandle();
$this->position = ($mode[0] === "a") ? $size : 0;
$this->queue = new \SplQueue;
}
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise
{
if ($this->isActive) {
throw new PendingOperationError;
}
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$this->isActive = true;
$onRead = function ($fh, $result, $buffer) use ($deferred) {
$this->isActive = false;
if ($result < 0) {
$error = \uv_strerror($result);
if ($error === "bad file descriptor") {
$deferred->fail(new ClosedException("Reading from the file failed due to a closed handle"));
} else {
$deferred->fail(new StreamException("Reading from the file failed: " . $error));
}
} else {
$length = \strlen($buffer);
$this->position = $this->position + $length;
$deferred->resolve($length ? $buffer : null);
}
};
\uv_fs_read($this->loop, $this->fh, $this->position, $length, $onRead);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function write(string $data): Promise
{
if ($this->isActive && $this->queue->isEmpty()) {
throw new PendingOperationError;
}
if (!$this->writable) {
throw new ClosedException("The file is no longer writable");
}
$this->isActive = true;
if ($this->queue->isEmpty()) {
$promise = $this->push($data);
} else {
$promise = $this->queue->top();
$promise = call(function () use ($promise, $data) {
yield $promise;
return yield $this->push($data);
});
}
$this->queue->push($promise);
return $promise;
}
/**
* {@inheritdoc}
*/
public function end(string $data = ""): Promise
{
return call(function () use ($data) {
$promise = $this->write($data);
$this->writable = false;
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
private function push(string $data): Promise
{
$length = \strlen($data);
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$onWrite = function ($fh, $result) use ($deferred, $length) {
if ($this->queue->isEmpty()) {
$deferred->fail(new ClosedException('No pending write, the file may have been closed'));
}
$this->queue->shift();
if ($this->queue->isEmpty()) {
$this->isActive = false;
}
if ($result < 0) {
$error = \uv_strerror($result);
if ($error === "bad file descriptor") {
$deferred->fail(new ClosedException("Writing to the file failed due to a closed handle"));
} else {
$deferred->fail(new StreamException("Writing to the file failed: " . $error));
}
} else {
StatCache::clear($this->path);
$this->position += $length;
if ($this->position > $this->size) {
$this->size = $this->position;
}
$deferred->resolve($length);
}
};
\uv_fs_write($this->loop, $this->fh, $data, $this->position, $onWrite);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function seek(int $offset, int $whence = \SEEK_SET): Promise
{
if ($this->isActive) {
throw new PendingOperationError;
}
$offset = (int) $offset;
switch ($whence) {
case \SEEK_SET:
$this->position = $offset;
break;
case \SEEK_CUR:
$this->position = $this->position + $offset;
break;
case \SEEK_END:
$this->position = $this->size + $offset;
break;
default:
throw new \Error(
"Invalid whence parameter; SEEK_SET, SEEK_CUR or SEEK_END expected"
);
}
return new Success($this->position);
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
return !$this->queue->isEmpty() ? false : ($this->size <= $this->position);
}
/**
* {@inheritdoc}
*/
public function path(): string
{
return $this->path;
}
/**
* {@inheritdoc}
*/
public function mode(): string
{
return $this->mode;
}
/**
* {@inheritdoc}
*/
public function close(): Promise
{
if ($this->closing) {
return $this->closing;
}
$deferred = new Deferred;
$this->poll->listen($this->closing = $deferred->promise());
\uv_fs_close($this->loop, $this->fh, function ($fh) use ($deferred) {
// Ignore errors when closing file, as the handle will become invalid anyway.
$deferred->resolve();
});
return $deferred->promise();
}
}
PK ,AV
ܿ[$ [$ lib/BlockingDriver.phpnu W+A
*/
public function size(string $path): Promise
{
if (!@\file_exists($path)) {
return new Failure(new FilesystemException(
"Path does not exist"
));
}
if (!@\is_file($path)) {
return new Failure(new FilesystemException(
"Path is not a regular file"
));
}
if (($size = @\filesize($path)) === false) {
return new Failure(new FilesystemException(
\error_get_last()["message"]
));
}
\clearstatcache(true, $path);
return new Success($size);
}
/**
* Does the specified path exist and is it a directory?
*
* If the path does not exist the returned Promise will resolve
* to FALSE. It will NOT reject with an error.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function isdir(string $path): Promise
{
if (!@\file_exists($path)) {
return new Success(false);
}
$isDir = @\is_dir($path);
\clearstatcache(true, $path);
return new Success($isDir);
}
/**
* Does the specified path exist and is it a file?
*
* If the path does not exist the returned Promise will resolve
* to FALSE. It will NOT reject with an error.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function isfile(string $path): Promise
{
if (!@\file_exists($path)) {
return new Success(false);
}
$isFile = @\is_file($path);
\clearstatcache(true, $path);
return new Success($isFile);
}
/**
* Retrieve the path's last modification time as a unix timestamp.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function mtime(string $path): Promise
{
if (!@\file_exists($path)) {
return new Failure(new FilesystemException(
"Path does not exist"
));
}
$mtime = @\filemtime($path);
\clearstatcache(true, $path);
return new Success($mtime);
}
/**
* Retrieve the path's last access time as a unix timestamp.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function atime(string $path): Promise
{
if (!@\file_exists($path)) {
return new Failure(new FilesystemException(
"Path does not exist"
));
}
$atime = @\fileatime($path);
\clearstatcache(true, $path);
return new Success($atime);
}
/**
* Retrieve the path's creation time as a unix timestamp.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
public function ctime(string $path): Promise
{
if (!@\file_exists($path)) {
return new Failure(new FilesystemException(
"Path does not exist"
));
}
$ctime = @\filectime($path);
\clearstatcache(true, $path);
return new Success($ctime);
}
/**
* {@inheritdoc}
*/
public function lstat(string $path): Promise
{
if ($stat = @\lstat($path)) {
\clearstatcache(true, $path);
} else {
$stat = null;
}
return new Success($stat);
}
/**
* {@inheritdoc}
*/
public function symlink(string $target, string $link): Promise
{
if (!@\symlink($target, $link)) {
return new Failure(new FilesystemException("Could not create symbolic link"));
}
return new Success(true);
}
/**
* {@inheritdoc}
*/
public function link(string $target, string $link): Promise
{
if (!@\link($target, $link)) {
return new Failure(new FilesystemException("Could not create hard link"));
}
return new Success(true);
}
/**
* {@inheritdoc}
*/
public function readlink(string $path): Promise
{
if (!($result = @\readlink($path))) {
return new Failure(new FilesystemException("Could not read symbolic link"));
}
return new Success($result);
}
/**
* {@inheritdoc}
*/
public function rename(string $from, string $to): Promise
{
if (!@\rename($from, $to)) {
return new Failure(new FilesystemException("Could not rename file"));
}
return new Success(true);
}
/**
* {@inheritdoc}
*/
public function unlink(string $path): Promise
{
StatCache::clear($path);
return new Success((bool) @\unlink($path));
}
/**
* {@inheritdoc}
*/
public function mkdir(string $path, int $mode = 0777, bool $recursive = false): Promise
{
return new Success((bool) @\mkdir($path, $mode, $recursive));
}
/**
* {@inheritdoc}
*/
public function rmdir(string $path): Promise
{
StatCache::clear($path);
return new Success((bool) @\rmdir($path));
}
/**
* {@inheritdoc}
*/
public function scandir(string $path): Promise
{
if (!@\is_dir($path)) {
return new Failure(new FilesystemException(
"Not a directory"
));
} elseif ($arr = @\scandir($path)) {
$arr = \array_values(\array_filter($arr, function ($el) {
return !($el === "." || $el === "..");
}));
\clearstatcache(true, $path);
return new Success($arr);
}
return new Failure(new FilesystemException(
"Failed reading contents from {$path}"
));
}
/**
* {@inheritdoc}
*/
public function chmod(string $path, int $mode): Promise
{
return new Success((bool) @\chmod($path, $mode));
}
/**
* {@inheritdoc}
*/
public function chown(string $path, int $uid, int $gid): Promise
{
if ($uid !== -1 && !@\chown($path, $uid)) {
return new Failure(new FilesystemException(
\error_get_last()["message"]
));
}
if ($gid !== -1 && !@\chgrp($path, $gid)) {
return new Failure(new FilesystemException(
\error_get_last()["message"]
));
}
return new Success;
}
/**
* {@inheritdoc}
*/
public function touch(string $path, int $time = null, int $atime = null): Promise
{
$time = $time ?? \time();
$atime = $atime ?? $time;
return new Success((bool) \touch($path, $time, $atime));
}
/**
* {@inheritdoc}
*/
public function get(string $path): Promise
{
$result = @\file_get_contents($path);
return ($result === false)
? new Failure(new FilesystemException(\error_get_last()["message"]))
: new Success($result);
}
/**
* {@inheritdoc}
*/
public function put(string $path, string $contents): Promise
{
$result = @\file_put_contents($path, $contents);
return ($result === false)
? new Failure(new FilesystemException(\error_get_last()["message"]))
: new Success($result);
}
}
PK ,AV ;H ;H lib/UvDriver.phpnu W+A driver = $driver;
$this->loop = $driver->getHandle();
$this->poll = new UvPoll;
}
/**
* {@inheritdoc}
*/
public function open(string $path, string $mode): Promise
{
$flags = $this->parseMode($mode);
$chmod = ($flags & \UV::O_CREAT) ? 0644 : 0;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$openArr = [$mode, $path, $deferred];
\uv_fs_open($this->loop, $path, $flags, $chmod, function ($fh) use ($openArr) {
if ($fh) {
$this->onOpenHandle($fh, $openArr);
} else {
list(, $path, $deferred) = $openArr;
$deferred->fail(new FilesystemException(
"Failed opening file handle to $path"
));
}
});
return $deferred->promise();
}
private function parseMode(string $mode): int
{
$mode = \str_replace(['b', 't', 'e'], '', $mode);
switch ($mode) {
case "r": return \UV::O_RDONLY;
case "r+": return \UV::O_RDWR;
case "w": return \UV::O_WRONLY | \UV::O_CREAT;
case "w+": return \UV::O_RDWR | \UV::O_CREAT;
case "a": return \UV::O_WRONLY | \UV::O_CREAT | \UV::O_APPEND;
case "a+": return \UV::O_RDWR | \UV::O_CREAT | \UV::O_APPEND;
case "x": return \UV::O_WRONLY | \UV::O_CREAT | \UV::O_EXCL;
case "x+": return \UV::O_RDWR | \UV::O_CREAT | \UV::O_EXCL;
case "c": return \UV::O_WRONLY | \UV::O_CREAT;
case "c+": return \UV::O_RDWR | \UV::O_CREAT;
default:
throw new \Error('Invalid file mode');
}
}
private function onOpenHandle($fh, array $openArr)
{
list($mode) = $openArr;
if ($mode[0] === "w") {
\uv_fs_ftruncate($this->loop, $fh, $length = 0, function ($fh) use ($openArr) {
if ($fh) {
$this->finalizeHandle($fh, $size = 0, $openArr);
} else {
list(, $path, $deferred) = $openArr;
$deferred->fail(new FilesystemException(
"Failed truncating file $path"
));
}
});
} else {
\uv_fs_fstat($this->loop, $fh, function ($fh, $stat) use ($openArr) {
if ($fh) {
StatCache::set($openArr[1], $stat);
$this->finalizeHandle($fh, $stat["size"], $openArr);
} else {
list(, $path, $deferred) = $openArr;
$deferred->fail(new FilesystemException(
"Failed reading file size from open handle pointing to $path"
));
}
});
}
}
private function finalizeHandle($fh, $size, array $openArr)
{
list($mode, $path, $deferred) = $openArr;
$handle = new UvHandle($this->driver, $this->poll, $fh, $path, $mode, $size);
$deferred->resolve($handle);
}
/**
* {@inheritdoc}
*/
public function stat(string $path): Promise
{
if ($stat = StatCache::get($path)) {
return new Success($stat);
}
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_stat($this->loop, $path, function ($fh, $stat) use ($deferred, $path) {
if (empty($fh)) {
$stat = null;
} else {
// link is not a valid stat type but returned by the uv extension
// change link to nlink
if (isset($stat['link'])) {
$stat['nlink'] = $stat['link'];
unset($stat['link']);
}
StatCache::set($path, $stat);
}
$deferred->resolve($stat);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function exists(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
$deferred->resolve((bool) $result);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function isdir(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve(!($result["mode"] & \UV::S_IFREG));
} else {
$deferred->resolve(false);
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function isfile(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve((bool) ($result["mode"] & \UV::S_IFREG));
} else {
$deferred->resolve(false);
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function size(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if (empty($result)) {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
} elseif (($result["mode"] & \UV::S_IFREG)) {
$deferred->resolve($result["size"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path is not a regular file"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function mtime(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve($result["mtime"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function atime(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve($result["atime"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function ctime(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve($result["ctime"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function lstat(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_lstat($this->loop, $path, function ($fh, $stat) use ($deferred) {
if (empty($fh)) {
$stat = null;
}
$deferred->resolve($stat);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function symlink(string $target, string $link): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_symlink($this->loop, $target, $link, \UV::S_IRWXU | \UV::S_IRUSR, function ($fh) use ($deferred) {
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function link(string $target, string $link): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_link($this->loop, $target, $link, \UV::S_IRWXU | \UV::S_IRUSR, function ($fh) use ($deferred) {
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function readlink(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_readlink($this->loop, $path, function ($fh, $target) use ($deferred) {
if (!(bool) $fh) {
$deferred->fail(new FilesystemException("Could not read symbolic link"));
return;
}
$deferred->resolve($target);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function rename(string $from, string $to): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_rename($this->loop, $from, $to, function ($fh) use ($deferred, $from) {
StatCache::clear($from);
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function unlink(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_unlink($this->loop, $path, function ($fh) use ($deferred, $path) {
StatCache::clear($path);
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function mkdir(string $path, int $mode = 0777, bool $recursive = false): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
if ($recursive) {
$path = \str_replace("/", DIRECTORY_SEPARATOR, $path);
$arrayPath = \explode(DIRECTORY_SEPARATOR, $path);
$tmpPath = "";
$callback = function () use (
&$callback, &$arrayPath, &$tmpPath, $mode, $deferred
) {
$tmpPath .= DIRECTORY_SEPARATOR . \array_shift($arrayPath);
if (empty($arrayPath)) {
\uv_fs_mkdir($this->loop, $tmpPath, $mode, function ($fh) use ($deferred) {
$deferred->resolve((bool) $fh);
});
} else {
$this->isdir($tmpPath)->onResolve(function ($error, $result) use (
$callback, $tmpPath, $mode
) {
if ($result) {
$callback();
} else {
\uv_fs_mkdir($this->loop, $tmpPath, $mode, $callback);
}
});
}
};
$callback();
} else {
\uv_fs_mkdir($this->loop, $path, $mode, function ($fh) use ($deferred) {
$deferred->resolve((bool) $fh);
});
}
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function rmdir(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_rmdir($this->loop, $path, function ($fh) use ($deferred, $path) {
StatCache::clear($path);
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function scandir(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_readdir($this->loop, $path, 0, function ($fh, $data) use ($deferred, $path) {
if (empty($fh) && $data !== 0) {
$deferred->fail(new FilesystemException("Failed reading contents from {$path}"));
} elseif ($data === 0) {
$deferred->resolve([]);
} else {
$deferred->resolve($data);
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function chmod(string $path, int $mode): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_chmod($this->loop, $path, $mode, function ($fh) use ($deferred) {
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function chown(string $path, int $uid, int $gid): Promise
{
// @TODO Return a failure in windows environments
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_chown($this->loop, $path, $uid, $gid, function ($fh) use ($deferred) {
$deferred->resolve((bool) $fh);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function touch(string $path, int $time = null, int $atime = null): Promise
{
$time = $time ?? \time();
$atime = $atime ?? $time;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\uv_fs_utime($this->loop, $path, $time, $atime, function () use ($deferred) {
// The uv_fs_utime() callback does not receive any args at this time
$deferred->resolve(true);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function get(string $path): Promise
{
$promise = new Coroutine($this->doGet($path));
$this->poll->listen($promise);
return $promise;
}
private function doGet($path): \Generator
{
$promise = $this->doFsOpen($path, $flags = \UV::O_RDONLY, $mode = 0);
if (!$fh = yield $promise) {
throw new FilesystemException("Failed opening file handle: {$path}");
}
$deferred = new Deferred;
$stat = yield $this->doFsStat($fh);
if (empty($stat)) {
$deferred->fail(new FilesystemException("stat operation failed on open file handle"));
} elseif (!$stat["isfile"]) {
\uv_fs_close($this->loop, $fh, function () use ($deferred) {
$deferred->fail(new FilesystemException("cannot buffer contents: path is not a file"));
});
} else {
$buffer = yield $this->doFsRead($fh, $offset = 0, $stat["size"]);
if ($buffer === false) {
\uv_fs_close($this->loop, $fh, function () use ($deferred) {
$deferred->fail(new FilesystemException("read operation failed on open file handle"));
});
} else {
\uv_fs_close($this->loop, $fh, function () use ($deferred, $buffer) {
$deferred->resolve($buffer);
});
}
}
return yield $deferred->promise();
}
private function doFsOpen($path, $flags, $mode)
{
$deferred = new Deferred;
\uv_fs_open($this->loop, $path, $flags, $mode, function ($fh) use ($deferred, $path) {
$deferred->resolve($fh);
});
return $deferred->promise();
}
private function doFsStat($fh)
{
$deferred = new Deferred;
\uv_fs_fstat($this->loop, $fh, function ($fh, $stat) use ($deferred) {
if ($fh) {
$stat["isdir"] = (bool) ($stat["mode"] & \UV::S_IFDIR);
$stat["isfile"] = !$stat["isdir"];
$deferred->resolve($stat);
} else {
$deferred->resolve();
}
});
return $deferred->promise();
}
private function doFsRead($fh, $offset, $len)
{
$deferred = new Deferred;
\uv_fs_read($this->loop, $fh, $offset, $len, function ($fh, $nread, $buffer) use ($deferred) {
$deferred->resolve($nread < 0 ? false : $buffer);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function put(string $path, string $contents): Promise
{
$promise = new Coroutine($this->doPut($path, $contents));
$this->poll->listen($promise);
return $promise;
}
private function doPut($path, $contents): \Generator
{
$flags = \UV::O_WRONLY | \UV::O_CREAT;
$mode = \UV::S_IRWXU | \UV::S_IRUSR;
$promise = $this->doFsOpen($path, $flags, $mode);
if (!$fh = yield $promise) {
throw new FilesystemException("Failed opening write file handle");
}
$deferred = new Deferred;
$len = \strlen($contents);
\uv_fs_write($this->loop, $fh, $contents, $offset = 0, function ($fh, $result) use ($deferred, $len) {
\uv_fs_close($this->loop, $fh, function () use ($deferred, $result, $len) {
if ($result < 0) {
$deferred->fail(new FilesystemException(\uv_strerror($result)));
} else {
$deferred->resolve($len);
}
});
});
return yield $deferred->promise();
}
}
PK ,AV?ŗ lib/functions.phpnu W+A
*/
function open(string $path, string $mode): Promise
{
return filesystem()->open($path, $mode);
}
/**
* Execute a file stat operation.
*
* If the requested path does not exist the resulting Promise will resolve to NULL.
* The returned Promise whould never resolve as a failure.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
function stat(string $path): Promise
{
return filesystem()->stat($path);
}
/**
* Does the specified path exist?
*
* This function should never resolve as a failure -- only a successfull bool value
* indicating the existence of the specified path.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
function exists(string $path): Promise
{
return filesystem()->exists($path);
}
/**
* Retrieve the size in bytes of the file at the specified path.
*
* If the path does not exist or is not a regular file this
* function's returned Promise WILL resolve as a failure.
*
* @param string $path An absolute file system path
* @fails \Amp\Files\FilesystemException If the path does not exist or is not a file
* @return \Amp\Promise
*/
function size(string $path): Promise
{
return filesystem()->size($path);
}
/**
* Does the specified path exist and is it a directory?
*
* If the path does not exist the returned Promise will resolve
* to FALSE and will not reject with an error.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
function isdir(string $path): Promise
{
return filesystem()->isdir($path);
}
/**
* Does the specified path exist and is it a file?
*
* If the path does not exist the returned Promise will resolve
* to FALSE and will not reject with an error.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
function isfile(string $path): Promise
{
return filesystem()->isfile($path);
}
/**
* Retrieve the path's last modification time as a unix timestamp.
*
* @param string $path An absolute file system path
* @fails \Amp\Files\FilesystemException If the path does not exist
* @return \Amp\Promise
*/
function mtime(string $path): Promise
{
return filesystem()->mtime($path);
}
/**
* Retrieve the path's last access time as a unix timestamp.
*
* @param string $path An absolute file system path
* @fails \Amp\Files\FilesystemException If the path does not exist
* @return \Amp\Promise
*/
function atime($path)
{
return filesystem()->atime($path);
}
/**
* Retrieve the path's creation time as a unix timestamp.
*
* @param string $path An absolute file system path
* @fails \Amp\Files\FilesystemException If the path does not exist
* @return \Amp\Promise
*/
function ctime(string $path): Promise
{
return filesystem()->ctime($path);
}
/**
* Same as stat() except if the path is a link then the link's data is returned.
*
* If the requested path does not exist the resulting Promise will resolve to NULL.
* The returned Promise whould never resolve as a failure.
*
* @param string $path An absolute file system path
* @return \Amp\Promise
*/
function lstat(string $path): Promise
{
return filesystem()->lstat($path);
}
/**
* Create a symlink $link pointing to the file/directory located at $original.
*
* @param string $original
* @param string $link
* @fails \Amp\Files\FilesystemException If the operation fails
* @return \Amp\Promise
*/
function symlink(string $original, string $link): Promise
{
return filesystem()->symlink($original, $link);
}
/**
* Create a hard link $link pointing to the file/directory located at $original.
*
* @param string $original
* @param string $link
* @fails \Amp\Files\FilesystemException If the operation fails
* @return \Amp\Promise
*/
function link(string $original, string $link): Promise
{
return filesystem()->symlink($original, $link);
}
/**
* Read the symlink at $path.
*
* @param string $path
* @fails \Amp\Files\FilesystemException If the operation fails
* @return \Amp\Promise
*/
function readlink(string $path): Promise
{
return filesystem()->readlink($path);
}
/**
* Rename a file or directory.
*
* @param string $from
* @param string $to
* @fails \Amp\Files\FilesystemException If the operation fails
* @return \Amp\Promise
*/
function rename(string $from, string $to): Promise
{
return filesystem()->rename($from, $to);
}
/**
* Delete a file.
*
* @param string $path
* @return \Amp\Promise
*/
function unlink(string $path): Promise
{
return filesystem()->unlink($path);
}
/**
* Create a director.
*
* @param string $path
* @param int $mode
* @param bool $recursive
* @return \Amp\Promise
*/
function mkdir(string $path, int $mode = 0777, bool $recursive = false): Promise
{
return filesystem()->mkdir($path, $mode, $recursive);
}
/**
* Delete a directory.
*
* @param string $path
* @return \Amp\Promise
*/
function rmdir(string $path): Promise
{
return filesystem()->rmdir($path);
}
/**
* Retrieve an array of files and directories inside the specified path.
*
* Dot entries are not included in the resulting array (i.e. "." and "..").
*
* @param string $path
* @return \Amp\Promise
*/
function scandir(string $path): Promise
{
return filesystem()->scandir($path);
}
/**
* chmod a file or directory.
*
* @param string $path
* @param int $mode
* @return \Amp\Promise
*/
function chmod(string $path, int $mode): Promise
{
return filesystem()->chmod($path, $mode);
}
/**
* chown a file or directory.
*
* @param string $path
* @param int $uid -1 to ignore
* @param int $gid -1 to ignore
* @return \Amp\Promise
*/
function chown(string $path, int $uid, int $gid = -1): Promise
{
return filesystem()->chown($path, $uid, $gid);
}
/**
* Update the access and modification time of the specified path.
*
* If the file does not exist it will be created automatically.
*
* @param string $path
* @param int $time The touch time. If $time is not supplied, the current system time is used.
* @param int $atime The access time. If $atime is not supplied, value passed to the $time parameter is used.
* @return \Amp\Promise
*/
function touch(string $path, int $time = null, int $atime = null): Promise
{
return filesystem()->touch($path, $time, $atime);
}
/**
* Buffer the specified file's contents.
*
* @param string $path The file path from which to buffer contents
* @return \Amp\Promise
*/
function get(string $path): Promise
{
return filesystem()->get($path);
}
/**
* Write the contents string to the specified path.
*
* @param string $path The file path to which to $contents should be written
* @param string $contents The data to write to the specified $path
* @return \Amp\Promise A promise resolving to the integer length written upon success
*/
function put(string $path, string $contents): Promise
{
return filesystem()->put($path, $contents);
}
PK ,AV}ҀD D lib/EioDriver.phpnu W+A poll = new Internal\EioPoll;
}
/**
* {@inheritdoc}
*/
public function open(string $path, string $mode): Promise
{
$flags = \EIO_O_NONBLOCK | \EIO_O_FSYNC | $this->parseMode($mode);
$chmod = ($flags & \EIO_O_CREAT) ? 0644 : 0;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$openArr = [$mode, $path, $deferred];
\eio_open($path, $flags, $chmod, \EIO_PRI_DEFAULT, [$this, "onOpenHandle"], $openArr);
return $deferred->promise();
}
private function parseMode(string $mode): int
{
$mode = \str_replace(['b', 't', 'e'], '', $mode);
switch ($mode) {
case 'r': return \EIO_O_RDONLY;
case 'r+': return \EIO_O_RDWR;
case 'w': return \EIO_O_WRONLY | \EIO_O_TRUNC | \EIO_O_CREAT;
case 'w+': return \EIO_O_RDWR | \EIO_O_TRUNC | \EIO_O_CREAT;
case 'a': return \EIO_O_WRONLY | \EIO_O_APPEND | \EIO_O_CREAT;
case 'a+': return \EIO_O_RDWR | \EIO_O_APPEND | \EIO_O_CREAT;
case 'x': return \EIO_O_WRONLY | \EIO_O_CREAT | \EIO_O_EXCL;
case 'x+': return \EIO_O_RDWR | \EIO_O_CREAT | \EIO_O_EXCL;
case 'c': return \EIO_O_WRONLY | \EIO_O_CREAT;
case 'c+': return \EIO_O_RDWR | \EIO_O_CREAT;
default:
throw new \Error('Invalid file mode');
}
}
private function onOpenHandle($openArr, $result, $req)
{
list($mode, $path, $deferred) = $openArr;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} elseif ($mode[0] === "a") {
\array_unshift($openArr, $result);
\eio_ftruncate($result, $offset = 0, \EIO_PRI_DEFAULT, [$this, "onOpenFtruncate"], $openArr);
} else {
\array_unshift($openArr, $result);
\eio_fstat($result, \EIO_PRI_DEFAULT, [$this, "onOpenFstat"], $openArr);
}
}
private function onOpenFtruncate($openArr, $result, $req)
{
list($fh, $mode, $path, $deferred) = $openArr;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$handle = new EioHandle($this->poll, $fh, $path, $mode, $size = 0);
$deferred->resolve($handle);
}
}
private function onOpenFstat($openArr, $result, $req)
{
list($fh, $mode, $path, $deferred) = $openArr;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
StatCache::set($path, $result);
$handle = new EioHandle($this->poll, $fh, $path, $mode, $result["size"]);
$deferred->resolve($handle);
}
}
/**
* {@inheritdoc}
*/
public function stat(string $path): Promise
{
if ($stat = StatCache::get($path)) {
return new Success($stat);
}
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
$data = [$deferred, $path];
\eio_stat($path, $priority, [$this, "onStat"], $data);
return $deferred->promise();
}
private function onStat($data, $result, $req)
{
list($deferred, $path) = $data;
if ($result === -1) {
$deferred->resolve(null);
} else {
StatCache::set($path, $result);
$deferred->resolve($result);
}
}
/**
* {@inheritdoc}
*/
public function exists(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
$deferred->resolve((bool) $result);
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function isdir(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve(!($result["mode"] & \EIO_S_IFREG));
} else {
$deferred->resolve(false);
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function isfile(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve((bool) ($result["mode"] & \EIO_S_IFREG));
} else {
$deferred->resolve(false);
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function size(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if (empty($result)) {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
} elseif ($result["mode"] & \EIO_S_IFREG) {
$deferred->resolve($result["size"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path is not a regular file"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function mtime(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve($result["mtime"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function atime(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve($result["atime"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function ctime(string $path): Promise
{
$deferred = new Deferred;
$this->stat($path)->onResolve(function ($error, $result) use ($deferred) {
if ($result) {
$deferred->resolve($result["ctime"]);
} else {
$deferred->fail(new FilesystemException(
"Specified path does not exist"
));
}
});
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function lstat(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_lstat($path, $priority, [$this, "onLstat"], $deferred);
return $deferred->promise();
}
private function onLstat($deferred, $result, $req)
{
if ($result === -1) {
$deferred->resolve(null);
} else {
$deferred->resolve($result);
}
}
/**
* {@inheritdoc}
*/
public function symlink(string $target, string $link): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_symlink($target, $link, $priority, [$this, "onGenericResult"], $deferred);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function link(string $target, string $link): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_link($target, $link, $priority, [$this, "onGenericResult"], $deferred);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function readlink(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_readlink($path, $priority, [$this, "onReadlink"], $deferred);
return $deferred->promise();
}
private function onReadlink($deferred, $result, $req)
{
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$deferred->resolve($result);
}
}
private function onGenericResult($deferred, $result, $req)
{
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$deferred->resolve(true);
}
}
/**
* {@inheritdoc}
*/
public function rename(string $from, string $to): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_rename($from, $to, $priority, [$this, "onGenericResult"], $deferred);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function unlink(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
$data = [$deferred, $path];
\eio_unlink($path, $priority, [$this, "onUnlink"], $data);
return $deferred->promise();
}
private function onUnlink($data, $result, $req)
{
list($deferred, $path) = $data;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
StatCache::clear($path);
$deferred->resolve(true);
}
}
/**
* {@inheritdoc}
*/
public function mkdir(string $path, int $mode = 0777, bool $recursive = false): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
if ($recursive) {
$path = \str_replace("/", DIRECTORY_SEPARATOR, $path);
$arrayPath = \explode(DIRECTORY_SEPARATOR, $path);
$tmpPath = "";
$callback = function () use (
&$callback, &$arrayPath, &$tmpPath, $mode, $priority, $deferred
) {
$tmpPath .= DIRECTORY_SEPARATOR . \array_shift($arrayPath);
if (empty($arrayPath)) {
\eio_mkdir($tmpPath, $mode, $priority, [$this, "onGenericResult"], $deferred);
} else {
$this->isdir($tmpPath)->onResolve(function ($error, $result) use (
$callback, $tmpPath, $mode, $priority
) {
if ($result) {
$callback();
} else {
\eio_mkdir($tmpPath, $mode, $priority, $callback);
}
});
}
};
$callback();
} else {
\eio_mkdir($path, $mode, $priority, [$this, "onGenericResult"], $deferred);
}
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function rmdir(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
$data = [$deferred, $path];
\eio_rmdir($path, $priority, [$this, "onRmdir"], $data);
return $deferred->promise();
}
private function onRmdir($data, $result, $req)
{
list($deferred, $path) = $data;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
StatCache::clear($path);
$deferred->resolve(true);
}
}
/**
* {@inheritdoc}
*/
public function scandir(string $path): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$flags = \EIO_READDIR_STAT_ORDER | \EIO_READDIR_DIRS_FIRST;
$priority = \EIO_PRI_DEFAULT;
\eio_readdir($path, $flags, $priority, [$this, "onScandir"], $deferred);
return $deferred->promise();
}
private function onScandir($deferred, $result, $req)
{
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$result = $result["names"];
\sort($result);
$deferred->resolve($result);
}
}
/**
* {@inheritdoc}
*/
public function chmod(string $path, int $mode): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_chmod($path, $mode, $priority, [$this, "onGenericResult"], $deferred);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function chown(string $path, int $uid, int $gid): Promise
{
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_chown($path, $uid, $gid, $priority, [$this, "onGenericResult"], $deferred);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function touch(string $path, int $time = null, int $atime = null): Promise
{
$time = $time ?? \time();
$atime = $atime ?? $time;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$priority = \EIO_PRI_DEFAULT;
\eio_utime($path, $atime, $time, $priority, [$this, "onGenericResult"], $deferred);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function get(string $path): Promise
{
$flags = $flags = \EIO_O_RDONLY;
$mode = 0;
$priority = \EIO_PRI_DEFAULT;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\eio_open($path, $flags, $mode, $priority, [$this, "onGetOpen"], $deferred);
return $deferred->promise();
}
private function onGetOpen($deferred, $result, $req)
{
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$priority = \EIO_PRI_DEFAULT;
\eio_fstat($result, $priority, [$this, "onGetFstat"], [$result, $deferred]);
}
}
private function onGetFstat($fhAndPromisor, $result, $req)
{
list($fh, $deferred) = $fhAndPromisor;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
return;
}
$offset = 0;
$length = $result["size"];
$priority = \EIO_PRI_DEFAULT;
\eio_read($fh, $length, $offset, $priority, [$this, "onGetRead"], $fhAndPromisor);
}
private function onGetRead($fhAndPromisor, $result, $req)
{
list($fh, $deferred) = $fhAndPromisor;
\eio_close($fh);
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$deferred->resolve($result);
}
}
/**
* {@inheritdoc}
*/
public function put(string $path, string $contents): Promise
{
$flags = \EIO_O_RDWR | \EIO_O_CREAT;
$mode = \EIO_S_IRUSR | \EIO_S_IWUSR | \EIO_S_IXUSR;
$priority = \EIO_PRI_DEFAULT;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
$data = [$contents, $deferred];
\eio_open($path, $flags, $mode, $priority, [$this, "onPutOpen"], $data);
return $deferred->promise();
}
private function onPutOpen($data, $result, $req)
{
list($contents, $deferred) = $data;
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$length = \strlen($contents);
$offset = 0;
$priority = \EIO_PRI_DEFAULT;
$callback = [$this, "onPutWrite"];
$fhAndPromisor = [$result, $deferred];
\eio_write($result, $contents, $length, $offset, $priority, $callback, $fhAndPromisor);
}
}
private function onPutWrite($fhAndPromisor, $result, $req)
{
list($fh, $deferred) = $fhAndPromisor;
\eio_close($fh);
if ($result === -1) {
$deferred->fail(new FilesystemException(\eio_get_last_error($req)));
} else {
$deferred->resolve($result);
}
}
}
PK ,AV^
lib/EioHandle.phpnu W+A poll = $poll;
$this->fh = $fh;
$this->path = $path;
$this->mode = $mode;
$this->size = $size;
$this->position = ($mode[0] === "a") ? $size : 0;
$this->queue = new \SplQueue;
}
/**
* {@inheritdoc}
*/
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise
{
if ($this->isActive) {
throw new PendingOperationError;
}
$this->isActive = true;
$remaining = $this->size - $this->position;
$length = $length > $remaining ? $remaining : $length;
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\eio_read(
$this->fh,
$length,
$this->position,
\EIO_PRI_DEFAULT,
[$this, "onRead"],
$deferred
);
return $deferred->promise();
}
private function onRead(Deferred $deferred, $result, $req)
{
$this->isActive = false;
if ($result === -1) {
$error = \eio_get_last_error($req);
if ($error === "Bad file descriptor") {
$deferred->fail(new ClosedException("Reading from the file failed due to a closed handle"));
} else {
$deferred->fail(new StreamException("Reading from the file failed:" . $error));
}
} else {
$this->position += \strlen($result);
$deferred->resolve(\strlen($result) ? $result : null);
}
}
/**
* {@inheritdoc}
*/
public function write(string $data): Promise
{
if ($this->isActive && $this->queue->isEmpty()) {
throw new PendingOperationError;
}
if (!$this->writable) {
throw new ClosedException("The file is no longer writable");
}
$this->isActive = true;
if ($this->queue->isEmpty()) {
$promise = $this->push($data);
} else {
$promise = $this->queue->top();
$promise = call(function () use ($promise, $data) {
yield $promise;
return yield $this->push($data);
});
}
$this->queue->push($promise);
return $promise;
}
private function push(string $data): Promise
{
$length = \strlen($data);
$deferred = new Deferred;
$this->poll->listen($deferred->promise());
\eio_write(
$this->fh,
$data,
$length,
$this->position,
\EIO_PRI_DEFAULT,
[$this, "onWrite"],
$deferred
);
return $deferred->promise();
}
/**
* {@inheritdoc}
*/
public function end(string $data = ""): Promise
{
return call(function () use ($data) {
$promise = $this->write($data);
$this->writable = false;
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
private function onWrite(Deferred $deferred, $result, $req)
{
if ($this->queue->isEmpty()) {
$deferred->fail(new ClosedException('No pending write, the file may have been closed'));
}
$this->queue->shift();
if ($this->queue->isEmpty()) {
$this->isActive = false;
}
if ($result === -1) {
$error = \eio_get_last_error($req);
if ($error === "Bad file descriptor") {
$deferred->fail(new ClosedException("Writing to the file failed due to a closed handle"));
} else {
$deferred->fail(new StreamException("Writing to the file failed: " . $error));
}
} else {
$this->position += $result;
if ($this->position > $this->size) {
$this->size = $this->position;
}
$deferred->resolve($result);
}
}
/**
* {@inheritdoc}
*/
public function close(): Promise
{
if ($this->closing) {
return $this->closing;
}
$deferred = new Deferred;
$this->poll->listen($this->closing = $deferred->promise());
\eio_close($this->fh, \EIO_PRI_DEFAULT, [$this, "onClose"], $deferred);
return $deferred->promise();
}
private function onClose(Deferred $deferred, $result, $req)
{
// Ignore errors when closing file, as the handle will become invalid anyway.
$deferred->resolve();
}
/**
* {@inheritdoc}
*/
public function seek(int $offset, int $whence = \SEEK_SET): Promise
{
if ($this->isActive) {
throw new PendingOperationError;
}
switch ($whence) {
case \SEEK_SET:
$this->position = $offset;
break;
case \SEEK_CUR:
$this->position = $this->position + $offset;
break;
case \SEEK_END:
$this->position = $this->size + $offset;
break;
default:
throw new \Error(
"Invalid whence parameter; SEEK_SET, SEEK_CUR or SEEK_END expected"
);
}
return new Success($this->position);
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
return $this->position;
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
return !$this->queue->isEmpty() ? false : ($this->size <= $this->position);
}
/**
* {@inheritdoc}
*/
public function path(): string
{
return $this->path;
}
/**
* {@inheritdoc}
*/
public function mode(): string
{
return $this->mode;
}
}
PK ,AVxͺS S lib/PendingOperationError.phpnu W+A fh = $fh;
$this->path = $path;
$this->mode = $mode;
}
public function __destruct()
{
if ($this->fh !== null) {
\fclose($this->fh);
}
}
/**
* {@inheritdoc}
*/
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise
{
if ($this->fh === null) {
throw new ClosedException("The file has been closed");
}
$data = \fread($this->fh, $length);
if ($data !== false) {
return new Success(\strlen($data) ? $data : null);
}
return new Failure(new StreamException(
"Failed reading from file handle"
));
}
/**
* {@inheritdoc}
*/
public function write(string $data): Promise
{
if ($this->fh === null) {
throw new ClosedException("The file has been closed");
}
$len = \fwrite($this->fh, $data);
if ($len !== false) {
return new Success($len);
}
return new Failure(new StreamException(
"Failed writing to file handle"
));
}
/**
* {@inheritdoc}
*/
public function end(string $data = ""): Promise
{
return call(function () use ($data) {
$promise = $this->write($data);
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
/**
* {@inheritdoc}
*/
public function close(): Promise
{
if ($this->fh === null) {
return new Success;
}
$fh = $this->fh;
$this->fh = null;
if (@\fclose($fh)) {
return new Success;
}
return new Failure(new StreamException(
"Failed closing file handle"
));
}
/**
* {@inheritdoc}
*/
public function seek(int $position, int $whence = \SEEK_SET): Promise
{
if ($this->fh === null) {
throw new ClosedException("The file has been closed");
}
switch ($whence) {
case \SEEK_SET:
case \SEEK_CUR:
case \SEEK_END:
if (@\fseek($this->fh, $position, $whence) === -1) {
return new Failure(new StreamException("Could not seek in file"));
}
return new Success($this->tell());
default:
throw new \Error(
"Invalid whence parameter; SEEK_SET, SEEK_CUR or SEEK_END expected"
);
}
}
/**
* {@inheritdoc}
*/
public function tell(): int
{
if ($this->fh === null) {
throw new ClosedException("The file has been closed");
}
return \ftell($this->fh);
}
/**
* {@inheritdoc}
*/
public function eof(): bool
{
if ($this->fh === null) {
throw new ClosedException("The file has been closed");
}
return \feof($this->fh);
}
/**
* {@inheritdoc}
*/
public function path(): string
{
return $this->path;
}
/**
* {@inheritdoc}
*/
public function mode(): string
{
return $this->mode;
}
}
PK ,AV" lib/StatCache.phpnu W+A $expiry) {
if ($now > $expiry) {
unset(
self::$cache[$path],
self::$timeouts[$path]
);
} else {
break;
}
}
});
Loop::unreference($watcher);
Loop::setState(self::class, new class($watcher) {
private $watcher;
private $driver;
public function __construct(string $watcher)
{
$this->watcher = $watcher;
$this->driver = Loop::get();
}
public function __destruct()
{
$this->driver->cancel($this->watcher);
}
});
}
public static function get(string $path)
{
return isset(self::$cache[$path]) ? self::$cache[$path] : null;
}
public static function set(string $path, array $stat)
{
if (self::$ttl <= 0) {
return;
}
if (Loop::getState(self::class) === null) {
self::init();
}
self::$cache[$path] = $stat;
self::$timeouts[$path] = self::$now + self::$ttl;
}
public static function ttl(int $seconds)
{
self::$ttl = $seconds;
}
public static function clear(string $path = null)
{
if (isset($path)) {
unset(
self::$cache[$path],
self::$timeouts[$path]
);
} else {
self::$cache = [];
self::$timeouts = [];
}
}
}
PK ,AVg6 6 LICENSEnu W+A The MIT License (MIT)
Copyright (c) 2015-2017 amphp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
PK ,AVS CONTRIBUTING.mdnu W+A ## Submitting useful bug reports
Please search existing issues first to make sure this is not a duplicate.
Every issue report has a cost for the developers required to field it; be
respectful of others' time and ensure your report isn't spurious prior to
submission. Please adhere to [sound bug reporting principles](http://www.chiark.greenend.org.uk/~sgtatham/bugs.html).
## Development ideology
Truths which we believe to be self-evident:
- **It's an asynchronous world.** Be wary of anything that undermines
async principles.
- **The answer is not more options.** If you feel compelled to expose
new preferences to the user it's very possible you've made a wrong
turn somewhere.
- **There are no power users.** The idea that some users "understand"
concepts better than others has proven to be, for the most part, false.
If anything, "power users" are more dangerous than the rest, and we
should avoid exposing dangerous functionality to them.
## Code style
The amphp project adheres to the PSR-2 style guide with the exception that
opening braces for classes and methods must appear on the same line as
the declaration:
https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.mdPK ,AV;O7 .php_cs.distnu W+A getFinder()
->in(__DIR__ . '/lib')
->in(__DIR__ . '/test');
$cacheDir = getenv('TRAVIS') ? getenv('HOME') . '/.php-cs-fixer' : __DIR__;
$config->setCacheFile($cacheDir . '/.php_cs.cache');
return $config;
PK ,AVd2 2
composer.jsonnu W+A {
"name": "amphp/file",
"homepage": "https://github.com/amphp/file",
"description": "Allows non-blocking access to the filesystem for Amp.",
"support": {
"issues": "https://github.com/amphp/file/issues"
},
"keywords": [
"file",
"disk",
"static",
"async",
"non-blocking",
"amp",
"amphp",
"io",
"filesystem"
],
"license": "MIT",
"authors": [
{
"name": "Daniel Lowrey",
"email": "rdlowrey@php.net"
},
{
"name": "Aaron Piotrowski",
"email": "aaron@trowski.com"
},
{
"name": "Niklas Keller",
"email": "me@kelunik.com"
}
],
"require": {
"amphp/amp": "^2",
"amphp/byte-stream": "^1",
"amphp/parallel": "^1"
},
"require-dev": {
"amphp/phpunit-util": "^1",
"phpunit/phpunit": "^6",
"amphp/php-cs-fixer-config": "dev-master"
},
"autoload": {
"psr-4": {
"Amp\\File\\": "lib"
},
"files": ["lib/functions.php"]
},
"autoload-dev": {
"psr-4": {
"Amp\\File\\Test\\": "test"
}
},
"config": {
"platform": {
"php": "7.0.0"
}
}
}
PK ,AVN N Makefilenu W+A PHP_BIN := php
COMPOSER_BIN := composer
COVERAGE = coverage
SRCS = lib test
find_php_files = $(shell find $(1) -type f -name "*.php")
src = $(foreach d,$(SRCS),$(call find_php_files,$(d)))
.PHONY: test
test: setup phpunit code-style
.PHONY: clean
clean: clean-coverage clean-vendor
.PHONY: clean-coverage
clean-coverage:
test ! -e coverage || rm -r coverage
.PHONY: clean-vendor
clean-vendor:
test ! -e vendor || rm -r vendor
.PHONY: setup
setup: vendor/autoload.php
.PHONY: deps-update
deps-update:
$(COMPOSER_BIN) update
.PHONY: phpunit
phpunit: setup
$(PHP_BIN) vendor/bin/phpunit
.PHONY: code-style
code-style: setup
PHP_CS_FIXER_IGNORE_ENV=1 $(PHP_BIN) vendor/bin/php-cs-fixer --diff -v fix
composer.lock: composer.json
$(COMPOSER_BIN) install
touch $@
vendor/autoload.php: composer.lock
$(COMPOSER_BIN) install
touch $@PK ,AVSV V CHANGELOGnu W+A ### 0.1.3
- Add recursive mkdir()
- EioDriver and UvDriver now pass O_TRUNC in file\put()
### 0.1.2
- Make UvHandle throw FilesystemException instead of \RuntimeException
### 0.1.1
- UID / GID must be ignored when -1
- A bugfix in php-uv extension regarding lengths read was fixed; removed ugly workaround
0.1.0
-----
- Initial releasePK ,AVba README.mdnu W+A # file
[![Build Status](https://img.shields.io/travis/amphp/file/master.svg?style=flat-square)](https://travis-ci.org/amphp/file)
[![CoverageStatus](https://img.shields.io/coveralls/amphp/file/master.svg?style=flat-square)](https://coveralls.io/github/amphp/file?branch=master)
![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)
`amphp/file` allows non-blocking access to the filesystem for [Amp](https://github.com/amphp/amp).
## Installation
This package can be installed as a [Composer](https://getcomposer.org/) dependency.
```bash
composer require amphp/file
```
## Optional Extension Backends
Extensions allow to use threading in the background instead of using multiple processes.
- [eio](https://pecl.php.net/package/eio)
- [php-uv](https://github.com/bwoebi/php-uv)
- [pthreads](https://github.com/krakjoe/pthreads)
`amphp/file` works out of the box without any PHP extensions. It uses multi-processing by default, but also comes with a blocking driver that just uses PHP's blocking functions in the current process.
## Versioning
`amphp/file` follows the [semver](http://semver.org/) semantic versioning specification like all other `amphp` packages.
## Security
If you discover any security related issues, please email [`me@kelunik.com`](mailto:me@kelunik.com) instead of using the issue tracker.
## License
The MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information.PK ,AVS( ( travis/install-uv.shnu W+A ../vendor/amphp/amp/travis/install-uv.shPK ,AV1f travis/install-eio.shnu W+A #!/usr/bin/env bash
git clone https://github.com/rosmanov/pecl-eio
pushd pecl-eio;
phpize;
./configure;
make;
make install;
popd;
echo "extension=eio.so" >> "$(php -r 'echo php_ini_loaded_file();')";
PK ,AV}\ phpunit.xml.distnu W+A PK ,AV< lib/ParallelDriver.phpnu W+A PK ,AVÒ B# lib/Driver.phpnu W+A PK ,AVV`f b<