-
-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #112 from WyriHaximus-labs/0.2.x-reointroduce-eio-…
…adapter Reintroduce the ext-eio adapter
- Loading branch information
Showing
13 changed files
with
564 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
|
||
namespace React\Filesystem\Eio; | ||
|
||
use React\EventLoop\Loop; | ||
use React\EventLoop\LoopInterface; | ||
use React\Filesystem\AdapterInterface; | ||
use React\Filesystem\ModeTypeDetector; | ||
use React\Filesystem\Node; | ||
use React\Filesystem\PollInterface; | ||
use React\Filesystem\Stat; | ||
use React\Promise\PromiseInterface; | ||
|
||
final class Adapter implements AdapterInterface | ||
{ | ||
use StatTrait; | ||
|
||
private LoopInterface $loop; | ||
private PollInterface $poll; | ||
|
||
public function __construct() | ||
{ | ||
$this->loop = Loop::get(); | ||
$this->poll = new Poll($this->loop); | ||
} | ||
|
||
public function detect(string $path): PromiseInterface | ||
{ | ||
return $this->internalStat($path)->then(function (?Stat $stat) use ($path) { | ||
if ($stat === null) { | ||
return new NotExist($this->poll, $this, $this->loop, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); | ||
} | ||
|
||
switch (ModeTypeDetector::detect($stat->mode())) { | ||
case Node\FileInterface::class: | ||
return $this->file($stat->path()); | ||
break; | ||
case Node\DirectoryInterface::class: | ||
return $this->directory($stat->path()); | ||
break; | ||
default: | ||
return new Node\Unknown($stat->path(), $stat->path()); | ||
break; | ||
} | ||
}); | ||
} | ||
|
||
public function directory(string $path): Node\DirectoryInterface | ||
{ | ||
return new Directory($this->poll, $this, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); | ||
} | ||
|
||
public function file(string $path): Node\FileInterface | ||
{ | ||
return new File($this->poll, dirname($path) . DIRECTORY_SEPARATOR, basename($path)); | ||
} | ||
|
||
protected function activate(): void | ||
{ | ||
$this->poll->activate(); | ||
} | ||
|
||
protected function deactivate(): void | ||
{ | ||
$this->poll->deactivate(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
<?php | ||
|
||
namespace React\Filesystem\Eio; | ||
|
||
use React\Filesystem\AdapterInterface; | ||
use React\Filesystem\Node; | ||
use React\Filesystem\PollInterface; | ||
use React\Promise\Promise; | ||
use React\Promise\PromiseInterface; | ||
use function React\Promise\all; | ||
|
||
final class Directory implements Node\DirectoryInterface | ||
{ | ||
use StatTrait; | ||
|
||
private PollInterface $poll; | ||
private AdapterInterface $filesystem; | ||
private string $path; | ||
private string $name; | ||
|
||
public function __construct(PollInterface $poll, AdapterInterface $filesystem, string $path, string $name) | ||
{ | ||
$this->poll = $poll; | ||
$this->filesystem = $filesystem; | ||
$this->path = $path; | ||
$this->name = $name; | ||
} | ||
|
||
public function stat(): PromiseInterface | ||
{ | ||
return $this->internalStat($this->path . $this->name); | ||
} | ||
|
||
public function ls(): PromiseInterface | ||
{ | ||
$this->activate(); | ||
return new Promise(function (callable $resolve): void { | ||
\eio_readdir($this->path . $this->name . DIRECTORY_SEPARATOR, \EIO_READDIR_STAT_ORDER | \EIO_READDIR_DIRS_FIRST, \EIO_PRI_DEFAULT, function ($_, $contents) use ($resolve): void { | ||
$this->deactivate(); | ||
$list = []; | ||
foreach ($contents['dents'] as $node) { | ||
$fullPath = $this->path . $this->name . DIRECTORY_SEPARATOR . $node['name']; | ||
switch ($node['type'] ?? null) { | ||
case EIO_DT_DIR: | ||
$list[] = $this->filesystem->directory($fullPath); | ||
break; | ||
case EIO_DT_REG : | ||
$list[] = $this->filesystem->file($fullPath); | ||
break; | ||
default: | ||
$list[] = $this->filesystem->detect($this->path . $this->name . DIRECTORY_SEPARATOR . $node['name']); | ||
break; | ||
} | ||
} | ||
|
||
$resolve(all($list)); | ||
}); | ||
}); | ||
} | ||
|
||
public function unlink(): PromiseInterface | ||
{ | ||
$this->activate(); | ||
return new Promise(function (callable $resolve): void { | ||
\eio_readdir($this->path . $this->name . DIRECTORY_SEPARATOR, \EIO_READDIR_STAT_ORDER | \EIO_READDIR_DIRS_FIRST, \EIO_PRI_DEFAULT, function ($_, $contents) use ($resolve): void { | ||
$this->deactivate(); | ||
if (count($contents['dents']) > 0) { | ||
$resolve(false); | ||
|
||
return; | ||
} | ||
|
||
$this->activate(); | ||
\eio_rmdir($this->path . $this->name, function () use ($resolve): void { | ||
$this->deactivate(); | ||
$resolve(true); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
public function path(): string | ||
{ | ||
return $this->path; | ||
} | ||
|
||
public function name(): string | ||
{ | ||
return $this->name; | ||
} | ||
|
||
protected function activate(): void | ||
{ | ||
$this->poll->activate(); | ||
} | ||
|
||
protected function deactivate(): void | ||
{ | ||
$this->poll->deactivate(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace React\Filesystem\Eio; | ||
|
||
/** | ||
* Singleton to make sure we always only have one file descriptor for the ext-eio event stream. | ||
* Creating more than one will invalidate the previous ones and make anything still using those fail. | ||
* | ||
* @internal | ||
*/ | ||
final class EventStream | ||
{ | ||
private static $fd = null; | ||
|
||
public static function get() | ||
{ | ||
if (self::$fd !== null) { | ||
return self::$fd; | ||
} | ||
|
||
self::$fd = eio_get_event_stream(); | ||
|
||
return self::$fd; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
<?php | ||
|
||
namespace React\Filesystem\Eio; | ||
|
||
use React\Filesystem\Node\FileInterface; | ||
use React\Filesystem\PollInterface; | ||
use React\Promise\Promise; | ||
use React\Promise\PromiseInterface; | ||
use function React\Promise\resolve; | ||
|
||
final class File implements FileInterface | ||
{ | ||
use StatTrait; | ||
|
||
private PollInterface $poll; | ||
private string $path; | ||
private string $name; | ||
|
||
public function __construct(PollInterface $poll, string $path, string $name) | ||
{ | ||
$this->poll = $poll; | ||
$this->path = $path; | ||
$this->name = $name; | ||
} | ||
|
||
public function stat(): PromiseInterface | ||
{ | ||
return $this->internalStat($this->path . $this->name); | ||
} | ||
|
||
public function getContents(int $offset = 0 , ?int $maxlen = null): PromiseInterface | ||
{ | ||
$this->activate(); | ||
return $this->openFile( | ||
$this->path . DIRECTORY_SEPARATOR . $this->name, | ||
\EIO_O_RDONLY, | ||
0, | ||
)->then( | ||
function ($fileDescriptor) use ($offset, $maxlen): PromiseInterface { | ||
if ($maxlen === null) { | ||
$sizePromise = $this->statFileDescriptor($fileDescriptor)->then(static function ($stat): int { | ||
return (int)$stat['size']; | ||
}); | ||
} else { | ||
$sizePromise = resolve($maxlen); | ||
} | ||
return $sizePromise->then(function ($length) use ($fileDescriptor, $offset): PromiseInterface { | ||
return new Promise (function (callable $resolve) use ($fileDescriptor, $offset, $length): void { | ||
\eio_read($fileDescriptor, $length, $offset, \EIO_PRI_DEFAULT, function ($fileDescriptor, string $buffer) use ($resolve): void { | ||
$resolve($this->closeOpenFile($fileDescriptor)->then(function () use ($buffer): string { | ||
return $buffer; | ||
})); | ||
}, $fileDescriptor); | ||
}); | ||
}); | ||
} | ||
); | ||
} | ||
|
||
public function putContents(string $contents, int $flags = 0) | ||
{ | ||
$this->activate(); | ||
return $this->openFile( | ||
$this->path . DIRECTORY_SEPARATOR . $this->name, | ||
(($flags & \FILE_APPEND) == \FILE_APPEND) ? \EIO_O_RDWR | \EIO_O_APPEND : \EIO_O_RDWR | \EIO_O_CREAT, | ||
0644 | ||
)->then( | ||
function ($fileDescriptor) use ($contents, $flags): PromiseInterface { | ||
return new Promise (function (callable $resolve) use ($contents, $fileDescriptor): void { | ||
\eio_write($fileDescriptor, $contents, strlen($contents), 0, \EIO_PRI_DEFAULT, function ($fileDescriptor, int $bytesWritten) use ($resolve): void { | ||
$resolve($this->closeOpenFile($fileDescriptor)->then(function () use ($bytesWritten): int { | ||
return $bytesWritten; | ||
})); | ||
}, $fileDescriptor); | ||
}); | ||
} | ||
); | ||
} | ||
|
||
private function statFileDescriptor($fileDescriptor): PromiseInterface | ||
{ | ||
return new Promise(function (callable $resolve, callable $reject) use ($fileDescriptor) { | ||
\eio_fstat($fileDescriptor, \EIO_PRI_DEFAULT, function ($_, $stat) use ($resolve): void { | ||
$resolve($stat); | ||
}, $fileDescriptor); | ||
}); | ||
} | ||
|
||
private function openFile(string $path, int $flags, int $mode): PromiseInterface | ||
{ | ||
return new Promise(function (callable $resolve, callable $reject) use ($path, $flags, $mode): void { | ||
\eio_open( | ||
$path, | ||
$flags, | ||
$mode, | ||
\EIO_PRI_DEFAULT, | ||
function ($_, $fileDescriptor) use ($resolve): void { | ||
$resolve($fileDescriptor); | ||
} | ||
); | ||
}); | ||
} | ||
|
||
private function closeOpenFile($fileDescriptor): PromiseInterface | ||
{ | ||
return new Promise(function (callable $resolve) use ($fileDescriptor) { | ||
try { | ||
\eio_close($fileDescriptor, \EIO_PRI_DEFAULT, function () use ($resolve): void { | ||
$this->deactivate(); | ||
$resolve(); | ||
}); | ||
} catch (\Throwable $error) { | ||
$this->deactivate(); | ||
throw $error; | ||
} | ||
}); | ||
} | ||
|
||
public function unlink(): PromiseInterface | ||
{ | ||
$this->activate(); | ||
return new Promise(function (callable $resolve): void { | ||
\eio_unlink($this->path . DIRECTORY_SEPARATOR . $this->name, \EIO_PRI_DEFAULT, function () use ($resolve): void { | ||
$this->deactivate(); | ||
$resolve(true); | ||
}); | ||
}); | ||
} | ||
|
||
public function path(): string | ||
{ | ||
return $this->path; | ||
} | ||
|
||
public function name(): string | ||
{ | ||
return $this->name; | ||
} | ||
|
||
protected function activate(): void | ||
{ | ||
$this->poll->activate(); | ||
} | ||
|
||
protected function deactivate(): void | ||
{ | ||
$this->poll->deactivate(); | ||
} | ||
} |
Oops, something went wrong.