Skip to content

Commit

Permalink
fix: fix windows support
Browse files Browse the repository at this point in the history
Signed-off-by: azjezz <azjezz@protonmail.com>
  • Loading branch information
azjezz committed Nov 10, 2021
1 parent 740c8e3 commit f1227e1
Show file tree
Hide file tree
Showing 21 changed files with 191 additions and 32 deletions.
1 change: 1 addition & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
operating-system:
- "macos-latest"
- "ubuntu-latest"
- "windows-latest"

steps:
- name: "checkout"
Expand Down
4 changes: 2 additions & 2 deletions docs/component/io.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
- [error_handle](./../../src/Psl/IO/error_handle.php#L17)
- [input_handle](./../../src/Psl/IO/input_handle.php#L17)
- [output_handle](./../../src/Psl/IO/output_handle.php#L17)
- [pipe](./../../src/Psl/IO/pipe.php#L22)
- [pipe](./../../src/Psl/IO/pipe.php#L24)

#### `Interfaces`

Expand All @@ -39,7 +39,7 @@
#### `Classes`

- [MemoryHandle](./../../src/Psl/IO/MemoryHandle.php#L15)
- [Reader](./../../src/Psl/IO/Reader.php#L15)
- [Reader](./../../src/Psl/IO/Reader.php#L17)

#### `Traits`

Expand Down
2 changes: 1 addition & 1 deletion docs/component/shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@

- [escape_argument](./../../src/Psl/Shell/escape_argument.php#L17)
- [escape_command](./../../src/Psl/Shell/escape_command.php#L14)
- [execute](./../../src/Psl/Shell/execute.php#L39)
- [execute](./../../src/Psl/Shell/execute.php#L44)


2 changes: 1 addition & 1 deletion docs/component/unix.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@

#### `Classes`

- [Server](./../../src/Psl/Unix/Server.php#L15)
- [Server](./../../src/Psl/Unix/Server.php#L16)


6 changes: 3 additions & 3 deletions src/Psl/Filesystem/create_temporary_file.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,16 @@ function create_temporary_file(?string $directory = null, ?string $prefix = null

if (null !== $prefix) {
Psl\invariant(
!Str\contains($prefix, SEPARATOR),
!Str\contains($prefix, ((string)SEPARATOR)),
'$prefix should not contain a directory separator ( "%s" ).',
SEPARATOR
((string)SEPARATOR)
);
} else {
$prefix = '';
}

try {
$filename = $directory . '/' . $prefix . SecureRandom\string(8);
$filename = $directory . ((string)SEPARATOR) . $prefix . SecureRandom\string(8);
// @codeCoverageIgnoreStart
} catch (SecureRandom\Exception\InsufficientEntropyException $e) {
throw new Exception\RuntimeException('Unable to gather enough entropy to generate filename.', 0, $e);
Expand Down
4 changes: 3 additions & 1 deletion src/Psl/IO/Reader.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use function strpos;
use function substr;

use const PHP_EOL;

final class Reader implements ReadHandleInterface
{
use ReadHandleConvenienceMethodsTrait;
Expand Down Expand Up @@ -139,7 +141,7 @@ public function readByte(?float $timeout = null): string
*/
public function readLine(): ?string
{
$line = $this->readUntil("\n");
$line = $this->readUntil(PHP_EOL);
if (null !== $line) {
return $line;
}
Expand Down
5 changes: 4 additions & 1 deletion src/Psl/IO/pipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
use function error_get_last;
use function stream_socket_pair;

use const PHP_OS_FAMILY;
use const STREAM_IPPROTO_IP;
use const STREAM_PF_INET;
use const STREAM_PF_UNIX;
use const STREAM_SOCK_STREAM;

Expand All @@ -26,7 +28,8 @@ function pipe(): array
* @return array{0: resource, 1: resource}
*/
static function (): array {
$sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$domain = PHP_OS_FAMILY === 'Windows' ? STREAM_PF_INET : STREAM_PF_UNIX;
$sockets = stream_socket_pair($domain, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
// @codeCoverageIgnoreStart
if ($sockets === false) {
$error = error_get_last();
Expand Down
1 change: 1 addition & 0 deletions src/Psl/Shell/escape_argument.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function escape_argument(string $argument): string
}

if ('\\' !== DIRECTORY_SEPARATOR) {

$argument = Byte\replace($argument, "'", "'\\''");

return "'" . $argument . "'";
Expand Down
88 changes: 81 additions & 7 deletions src/Psl/Shell/execute.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@
use Psl\Env;
use Psl\IO;
use Psl\IO\Stream;
use Psl\Regex;
use Psl\SecureRandom;
use Psl\Str;
use Psl\Vec;

use function is_dir;
use function is_resource;
use function proc_close;
use function proc_open;
use function strpbrk;

use const PHP_OS_FAMILY;

/**
* Execute an external program.
Expand Down Expand Up @@ -65,18 +70,87 @@ function execute(
throw new Exception\PossibleAttackException('NULL byte detected.');
}

$descriptor = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];

$environment = Dict\merge(Env\get_vars(), $environment);
$working_directory = $working_directory ?? Env\current_dir();
$working_directory ??= Env\current_dir();
if (!is_dir($working_directory)) {
throw new Exception\RuntimeException('$working_directory does not exist.');
}

$process = proc_open($commandline, $descriptor, $pipes, $working_directory, $environment);
$options = [];
// @codeCoverageIgnoreStart
if (PHP_OS_FAMILY === 'Windows') {
$variable_cache = [];
$variable_count = 0;
/** @psalm-suppress MissingThrowsDocblock */
$identifier = 'PHP_STANDARD_LIBRARY_TMP_ENV_' . SecureRandom\string(6);
/** @psalm-suppress MissingThrowsDocblock */
$commandline = Regex\replace_with(
$commandline,
'/"(?:([^"%!^]*+(?:(?:!LF!|"(?:\^[%!^])?+")[^"%!^]*+)++)|[^"]*+ )"/x',
/**
* @param array<array-key, string> $m
*
* @return string
*/
static function (array $m) use (
&$environment,
&$variable_cache,
&$variable_count,
$identifier
): string {
if (!isset($m[1])) {
return $m[0];
}

/** @var array<string, string> $variable_cache */
if (isset($variable_cache[$m[0]])) {
/** @var string */
return $variable_cache[$m[0]];
}

$value = $m[1];
if (Str\Byte\contains($value, "\0")) {
$value = Str\Byte\replace($value, "\0", '?');
}

if (false === strpbrk($value, "\"%!\n")) {
return '"' . $value . '"';
}

$value = Str\Byte\replace_every($value, ['!LF!' => "\n", '"^!"' => '!', '"^%"' => '%', '"^^"' => '^', '""' => '"']);
$value = '"' . Regex\replace($value, '/(\\\\*)"/', '$1$1\\"') . '"';
/**
* @psalm-suppress MixedAssignment
* @psalm-suppress MixedOperand
*/
$var = $identifier . ++$variable_count;

/**
* @psalm-suppress MixedArrayAssignment
*/
$environment[$var] = $value;

/**
* @psalm-suppress MixedArrayOffset
* @psalm-suppress MixedArrayAssignment
*/
return $variable_cache[$m[0]] = '!' . $var . '!';
},
);

$commandline = 'cmd /V:ON /E:ON /D /C (' . Str\Byte\replace($commandline, "\n", ' ') . ')';
$options = [
'bypass_shell' => true,
'blocking_pipes' => false,
];
}
// @codeCoverageIgnoreEnd
$descriptor = [
1 => ['pipe', 'w'],
2 => ['pipe', 'w'],
];
/** @var array<string, string> $environment */
$process = proc_open($commandline, $descriptor, $pipes, $working_directory, $environment, $options);
// @codeCoverageIgnoreStart
// not sure how to replicate this, but it can happen \_o.o_/
if (!is_resource($process)) {
Expand Down
11 changes: 9 additions & 2 deletions src/Psl/Unix/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

namespace Psl\Unix;

use Psl;
use Psl\Network;
use Revolt\EventLoop;

use function error_get_last;
use function fclose;
use function stream_socket_accept;

use const PHP_OS_FAMILY;

final class Server implements Network\ServerInterface
{
/**
Expand Down Expand Up @@ -62,10 +63,16 @@ static function (string $_watcher, mixed $resource) use (&$suspension): void {
*
* @param non-empty-string $file
*
* @throws Psl\Network\Exception\RuntimeException In case failed to listen to on given address.
* @throws Network\Exception\RuntimeException In case failed to listen to on given address.
*/
public static function create(string $file): self
{
// @codeCoverageIgnoreStart
if (PHP_OS_FAMILY === 'Windows') {
throw new Network\Exception\RuntimeException('Unix server is not supported on Windows platform.');
}
// @codeCoverageIgnoreEnd

$socket = Network\Internal\server_listen("unix://{$file}");

return new self($socket);
Expand Down
6 changes: 6 additions & 0 deletions src/Psl/Unix/connect.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
*/
function connect(string $path, ?float $timeout = null): SocketInterface
{
// @codeCoverageIgnoreStart
if (PHP_OS_FAMILY === 'Windows') {
throw new Network\Exception\RuntimeException('Unix socket is not supported on Windows platform.');
}
// @codeCoverageIgnoreEnd

$socket = Network\Internal\socket_connect("unix://{$path}", timeout: $timeout);

/** @psalm-suppress MissingThrowsDocblock */
Expand Down
9 changes: 8 additions & 1 deletion tests/unit/Async/AwaitReadableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@
use function fwrite;
use function stream_socket_pair;

use const PHP_OS_FAMILY;
use const STREAM_IPPROTO_IP;
use const STREAM_PF_INET;
use const STREAM_PF_UNIX;
use const STREAM_SOCK_STREAM;

final class AwaitReadableTest extends TestCase
{
public function testAwaitReadable(): void
{
$sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$domain = PHP_OS_FAMILY === 'Windows' ? STREAM_PF_INET : STREAM_PF_UNIX;
$sockets = stream_socket_pair($domain, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
$write_socket = $sockets[0];
$read_socket = $sockets[1];

Expand Down
8 changes: 7 additions & 1 deletion tests/unit/Env/CurrentExecTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@
use Psl\Env;
use Psl\Filesystem;

use const PHP_OS_FAMILY;

final class CurrentExecTest extends TestCase
{
public function testCurrentExe(): void
{
if ('Windows' === PHP_OS_FAMILY) {
static::markTestSkipped('I do not want to bother :)');
}

$phpunit = __DIR__ . '/../../../vendor/bin/phpunit';
$phpunit = Filesystem\canonicalize($phpunit);
if (PHP_OS_FAMILY !== 'Windows' && Filesystem\is_symbolic_link($phpunit)) {
if (Filesystem\is_symbolic_link($phpunit)) {
$phpunit = Filesystem\read_symbolic_link($phpunit);
}

Expand Down
6 changes: 6 additions & 0 deletions tests/unit/Filesystem/CopyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Psl\Filesystem;
use Psl\Str;

use const PHP_OS_FAMILY;

final class CopyTest extends AbstractFilesystemTest
{
protected string $function = 'copy';
Expand Down Expand Up @@ -42,6 +44,10 @@ public function testCopyOverwrite(): void

public function testCopyExecutableBits(): void
{
if (PHP_OS_FAMILY === 'Windows') {
static::markTestSkipped('Test can only be executed under *nix OS.');
}

$shell_file = Str\join([$this->directory, 'hello.sh'], Filesystem\SEPARATOR);

Filesystem\create_file($shell_file);
Expand Down
11 changes: 8 additions & 3 deletions tests/unit/Filesystem/FileTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,17 @@ public function testWriteFileThrowsForNonWritableFiles(): void
{
$file = Str\join([$this->directory, 'write.txt'], Filesystem\SEPARATOR);
Filesystem\create_file($file);
$permissions = Filesystem\get_permissions($file) & 0777;
Filesystem\change_permissions($file, 0111);

$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('File "' . $file . '" is not writable.');
try {
$this->expectException(InvariantViolationException::class);
$this->expectExceptionMessage('File "' . $file . '" is not writable.');

Filesystem\write_file($file, 'hello');
Filesystem\write_file($file, 'hello');
} finally {
Filesystem\change_permissions($file, $permissions);
}
}

public function testReadFile(): void
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/Filesystem/PermissionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
use Psl\Filesystem;
use Psl\Str;

use const PHP_OS_FAMILY;

final class PermissionsTest extends AbstractFilesystemTest
{
protected string $function = 'permissions';

public function testChangePermissions(): void
{
if (PHP_OS_FAMILY === 'Windows') {
// executable bit on windows.
static::markTestSkipped('Test can only be executed under *nix OS.');
}

$filename = Str\join([$this->directory, 'foo.txt'], Filesystem\SEPARATOR);

Filesystem\create_file($filename);
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/Filesystem/ReadDirectoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
use Psl\Str;
use Psl\Vec;

use const PHP_OS_FAMILY;

final class ReadDirectoryTest extends AbstractFilesystemTest
{
protected string $function = 'read_directory';
Expand Down Expand Up @@ -57,6 +59,11 @@ public function testReadDirectoryThrowsIfNotDirectory(): void

public function testReadDirectoryThrowsIfNotReadable(): void
{
if (PHP_OS_FAMILY === 'Windows') {
// executable bit on windows.
static::markTestSkipped('Test can only be executed under *nix OS.');
}

Filesystem\change_permissions($this->directory, 0077);

$this->expectException(InvariantViolationException::class);
Expand Down
Loading

0 comments on commit f1227e1

Please sign in to comment.