Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tcp): add support for TLS/SSL in TCP #451

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat(tcp): support TLS/SSL
Signed-off-by: azjezz <[email protected]>
azjezz committed May 29, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit f78e68ad21fda950d238c489aeb3ffcd767253be
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
"ext-mbstring": "*",
"ext-sodium": "*",
"ext-intl": "*",
"ext-openssl": "*",
"revolt/event-loop": "^1.0.6"
},
"require-dev": {
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -54,6 +54,7 @@
- [Psl\Str\Byte](./component/str-byte.md)
- [Psl\Str\Grapheme](./component/str-grapheme.md)
- [Psl\TCP](./component/tcp.md)
- [Psl\TCP\TLS](./component/tcp-tls.md)
- [Psl\Trait](./component/trait.md)
- [Psl\Unix](./component/unix.md)
- [Psl\Vec](./component/vec.md)
2 changes: 1 addition & 1 deletion docs/component/network.md
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@

#### `Classes`

- [Address](./../../src/Psl/Network/Address.php#L10)
- [Address](./../../src/Psl/Network/Address.php#L12)
- [SocketOptions](./../../src/Psl/Network/SocketOptions.php#L14)

#### `Enums`
21 changes: 21 additions & 0 deletions docs/component/tcp-tls.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!--
This markdown file was generated using `docs/documenter.php`.
Any edits to it will likely be lost.
-->

[*index](./../README.md)

---

### `Psl\TCP\TLS` Component

#### `Classes`

- [Certificate](./../../src/Psl/TCP/TLS/Certificate.php#L16)
- [ClientOptions](./../../src/Psl/TCP/TLS/ClientOptions.php#L34)
- [HashingAlgorithm](./../../src/Psl/TCP/TLS/HashingAlgorithm.php#L15)
- [SecurityLevel](./../../src/Psl/TCP/TLS/SecurityLevel.php#L18)
- [Version](./../../src/Psl/TCP/TLS/Version.php#L10)


6 changes: 3 additions & 3 deletions docs/component/tcp.md
Original file line number Diff line number Diff line change
@@ -12,12 +12,12 @@

#### `Functions`

- [connect](./../../src/Psl/TCP/connect.php#L19)
- [connect](./../../src/Psl/TCP/connect.php#L21)

#### `Classes`

- [ConnectOptions](./../../src/Psl/TCP/ConnectOptions.php#L14)
- [Server](./../../src/Psl/TCP/Server.php#L11)
- [ClientOptions](./../../src/Psl/TCP/ClientOptions.php#L14)
- [Server](./../../src/Psl/TCP/Server.php#L14)
- [ServerOptions](./../../src/Psl/TCP/ServerOptions.php#L15)


1 change: 1 addition & 0 deletions docs/documenter.php
Original file line number Diff line number Diff line change
@@ -223,6 +223,7 @@ function get_all_components(): array
'Psl\\Str\\Byte',
'Psl\\Str\\Grapheme',
'Psl\\TCP',
'Psl\\TCP\\TLS',
'Psl\\Trait',
'Psl\\Unix',
'Psl\\Locale',
43 changes: 31 additions & 12 deletions examples/tcp/basic-http-client.php
Original file line number Diff line number Diff line change
@@ -6,24 +6,43 @@

use Psl\Async;
use Psl\IO;
use Psl\Str;
use Psl\TCP;

require __DIR__ . '/../../vendor/autoload.php';

function fetch(string $host, string $path): string
Async\main(static function(): void {
[$headers, $content] = fetch('https://php-standard-library.github.io');

$output = IO\error_handle() ?? IO\output_handle();

$output->writeAll($headers);
$output->writeAll("\n");
$output->writeAll($content);
});

function fetch(string $url): array
{
$client = TCP\connect($host, 80);
$client->writeAll("GET {$path} HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n");
$response = $client->readAll();
$client->close();
$parsed_url = parse_url($url);
$host = $parsed_url['host'];
$port = $parsed_url['port'] ?? ($parsed_url['scheme'] === 'https' ? 443 : 80);
$path = $parsed_url['path'] ?? '/';

return $response;
}
$options = TCP\ClientOptions::create();
if ($parsed_url['scheme'] === 'https') {
$options = $options->withTlsClientOptions(
TCP\TLS\ClientOptions::default()->withPeerName($host),
);
}

Async\main(static function (): int {
$response = fetch('example.com', '/');
$client = TCP\connect($host, $port, $options);
$client->writeAll("GET $path HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n");

IO\write_error_line($response);
$response = $client->readAll();

return 0;
});
$position = Str\search($response, "\r\n\r\n");
$headers = Str\slice($response, 0, $position);
$content = Str\slice($response, $position + 4);

return [$headers, $content];
}
93 changes: 67 additions & 26 deletions examples/tcp/basic-http-server.php
Original file line number Diff line number Diff line change
@@ -5,46 +5,87 @@
namespace Psl\Example\TCP;

use Psl\Async;
use Psl\File;
use Psl\Html;
use Psl\IO;
use Psl\Iter;
use Psl\Network;
use Psl\Str;
use Psl\TCP;

require __DIR__ . '/../../vendor/autoload.php';

const RESPONSE_FORMAT = <<<HTML
<!DOCTYPE html>
<html lang='en'>
<head>
<title>PHP Standard Library - TCP server</title>
</head>
<body>
<h1>Hello, World!</h1>
<pre><code>%s</code></pre>
</body>
</html>
HTML;

$server = TCP\Server::create('localhost', 3030, TCP\ServerOptions::create(idle_connections: 1024));
/**
* Note: This example is purely for demonstration purposes, and should never be used in a production environment.
*
* Generate a self-signed certificate using the following command:
*
* $ cd examples/tcp/fixtures
* $ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout privatekey.pem -out certificate.pem -config openssl.cnf
*/
$server = TCP\Server::create('localhost', 3030, TCP\ServerOptions::default()
->withTlsServerOptions(
TCP\TLS\ServerOptions::default()
->withMinimumVersion(TCP\TLS\Version::Tls12)
->withAllowSelfSignedCertificates()
->withPeerVerification(false)
->withSecurityLevel(TCP\TLS\SecurityLevel::Level2)
->withDefaultCertificate(TCP\TLS\Certificate::create(
certificate_file: __DIR__ . '/fixtures/certificate.pem',
key_file: __DIR__ . '/fixtures/privatekey.pem',
))
)
);

Async\Scheduler::onSignal(SIGINT, $server->close(...));

IO\write_error_line('Server is listening on http://localhost:3030');
IO\write_error_line('Server is listening on https://localhost:3030');
IO\write_error_line('Click Ctrl+C to stop the server.');

Iter\apply($server->incoming(), static function (Network\StreamSocketInterface $connection): void {
Async\run(static function() use($connection): void {
$request = $connection->read();
while (true) {
try {
$connection = $server->nextConnection();

$connection->writeAll("HTTP/1.1 200 OK\nConnection: close\nContent-Type: text/html; charset=utf-8\n\n");
$connection->writeAll(Str\format(RESPONSE_FORMAT, Html\encode_special_characters($request)));
$connection->close();
})->catch(
static fn(IO\Exception\ExceptionInterface $e) => IO\write_error_line('Error: %s.', $e->getMessage())
)->ignore();
});
Async\Scheduler::defer(static fn() => handle($connection));
} catch (TCP\TLS\Exception\NegotiationException $e) {
IO\write_error_line('[SRV]: error "%s" at %s:%d"', $e->getMessage(), $e->getFile(), $e->getLine());

continue;
} catch (Network\Exception\AlreadyStoppedException $e) {
break;
}
}

IO\write_error_line('');
IO\write_error_line('Goodbye 👋');

function handle(Network\SocketInterface $connection): void
{
try {
$peer = $connection->getPeerAddress();

IO\write_error_line('[SRV]: received a connection from peer "%s".', $peer);

do {
$request = $connection->read();

$template = File\read(__DIR__ . '/templates/index.html');
$content = Str\format($template, Html\encode_special_characters($request));
$length = Str\Byte\length($content);

$connection->writeAll("HTTP/1.1 200 OK\nConnection: keep-alive\nContent-Type: text/html; charset=utf-8\nContent-Length: $length\n\n");
$connection->writeAll($content);
} while(!$connection->reachedEndOfDataSource());

IO\write_error_line('[SRV]: connection dropped by peer "%s".', $peer);
} catch (IO\Exception\ExceptionInterface $e) {
if (!$connection->reachedEndOfDataSource()) {
// If we reached end of data source ( EOF ) and gotten an error, that means that connect was most likely dropped
// by peer while we are performing a write operation, ignore it.
//
// otherwise, log the error:
IO\write_error_line('[SRV]: error "%s" at %s:%d"', $e->getMessage(), $e->getFile(), $e->getLine());
}
} finally {
$connection->close();
}
}
23 changes: 23 additions & 0 deletions examples/tcp/fixtures/certificate.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDyjCCArKgAwIBAgIUKZH353aWdxUxmEabhyie1GbT1wQwDQYJKoZIhvcNAQEL
BQAwbzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1TYW4gRnJh
bmNpc2NvMRIwEAYDVQQKDAlNeUNvbXBhbnkxEzARBgNVBAsMCk15RGl2aXNpb24x
EjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yNDA1MjkxMjIzMjlaFw0yNTA1MjkxMjIz
MjlaMG8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNU2FuIEZy
YW5jaXNjbzESMBAGA1UECgwJTXlDb21wYW55MRMwEQYDVQQLDApNeURpdmlzaW9u
MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCmBzlJGgJNiKvVSQskqh30o8LOApZcsCZf1NlijQ4XggtunMgtV4663EWw
nOXewWD3Hssi/ZAaK4Az+0GQ3iMSGJAGBtOzAPCr0ZIUN42bx6NMx+iqQ37XkLw8
A9uPXK7hwM9EG6uWMVtw5OR7TugFGtyFmTLdxU3uaxtkmRi76haTyBOFpJs4xyFj
7WinGlCJ0EjKibW12xcCYWbRoObeJIBvviJizfve0dK+lVsP4sYP7gd3xwHq4xUO
x2lWeUFmAPL9+jDNfrwd985OAAkWO71q8MySvAVPaGGiu6gN5ReyzNAUxaYErJfd
BYQEVW25q/E6ez0r6lGSmLwbGqc3AgMBAAGjXjBcMDsGA1UdEQQ0MDKCCWxvY2Fs
aG9zdIINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATAdBgNV
HQ4EFgQUV7Hcc0Urg/HSV0jwHA8A1r/+yCUwDQYJKoZIhvcNAQELBQADggEBAFVf
ExPw0NL1vLJq1KeXM7dxazd9Ge0K+8OXELbgtPJfxotHLTUGx25uaxc5lKa3v2Aa
Du1iQMExPQCBWoO2pb9OrfePbagyzbkM2yVR/NNI9cXlk6BMMhluMF5onKkTApH+
QzqaU/VWyBCOgLsuzM1kwXpsJyTJ+pZgXVmwuFMefVsdcMT3Gz6Fnmn04aslOU62
Kgnkfx4rDPH4kqC1Zj4RwenJ03gCC9o2jaV8cZsMmu4tC3/hXHdWCJFV4DOCJ6w0
TFMP83vYgSQoLIZIPo9ka5yELaRSSE27LPEckdVYk4q+X/O2/SSZPgFFFcJ5qXqb
Y9KqGkvmSp//SvEDyV0=
-----END CERTIFICATE-----
26 changes: 26 additions & 0 deletions examples/tcp/fixtures/openssl.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
x509_extensions = v3_req
prompt = no

[ req_distinguished_name ]
C = US
ST = CA
L = San Francisco
O = MyCompany
OU = MyDivision
CN = localhost

[ req_ext ]
subjectAltName = @alt_names

[ v3_req ]
subjectAltName = @alt_names

[ alt_names ]
DNS.1 = localhost
DNS.2 = *.example.com
IP.1 = 127.0.0.1
IP.2 = ::1
28 changes: 28 additions & 0 deletions examples/tcp/fixtures/privatekey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCmBzlJGgJNiKvV
SQskqh30o8LOApZcsCZf1NlijQ4XggtunMgtV4663EWwnOXewWD3Hssi/ZAaK4Az
+0GQ3iMSGJAGBtOzAPCr0ZIUN42bx6NMx+iqQ37XkLw8A9uPXK7hwM9EG6uWMVtw
5OR7TugFGtyFmTLdxU3uaxtkmRi76haTyBOFpJs4xyFj7WinGlCJ0EjKibW12xcC
YWbRoObeJIBvviJizfve0dK+lVsP4sYP7gd3xwHq4xUOx2lWeUFmAPL9+jDNfrwd
985OAAkWO71q8MySvAVPaGGiu6gN5ReyzNAUxaYErJfdBYQEVW25q/E6ez0r6lGS
mLwbGqc3AgMBAAECggEADhWLywM9UcV3yjKVkukpfGDN/Drk9Xzt7HA6dq0/lkfu
X1ZGdu44Cer4sHhG2cQuzRfcJJ489LNe/0nfsIHfmL/jq9c1azh3siOnkDZ8OUxQ
sok82AC8yF2bUj4DiKBUp4r7KixsvGN4fdW0+i7h6Njz/xNVaNG9gC2u17RTEFGy
tCMegse4XYTKxziOpGjhxwuIEOlY5sX3TdqTiiiJCz+PkD9WT2fPqZH9+K9msE1v
ETlCP6IKmt39TrZp6HCMmfDTvcX8tNUdNll/7b2NEAfSH4FJcNRVUVc/Xjw7SXeg
0e51qoYTRMqaO8Q1xDFtk9881LiWVoSPb6rTe17rBQKBgQDhy14xsmzfKeyTYcjF
ucs1NHh1xJBUNyQGE2aYq58Pg9CeuSnv9N+WWZdR2xNpQ5wq1F6ZS96B6FU1DT+/
A2YreGUJoLQan26oI+r3TEOASHZcliqV9Qn9R0vA5GRLixG0LIcv3s6askIqHFLY
3BX2goGIlvCGJiWpvd2MPvWsPQKBgQC8PRUl9Onob8ar8ELlVCeRCJ0wgIKQew95
MWKrfLATLaJJMLlU3JPLnN3gnOZhJ2u5/ClArofzbofhfkMMnoZoxInCs/hn4EwT
W1aeLGCQX/Qb89B2HCDz2t4KPRraq+diEj4vR9H8VvUTdcIvXQ4i9UfM9G1+xVpJ
1oPUytXUgwKBgGKgBArNFsT7ePx/T8Ud/GbG/n7iVvCSDUgiHUQ+YoHSX8OUuX64
hRkVFQWKHZZzE7mZfaCUBSLVKrK7kMaMY4pFUky8Ry8ByMHkvnM6epmEDT8v0HYj
zDM3ex1MJYrhud/rOzlrpu7nQgNGz+EtcOJ16sKQu4q9CuJzrlvd/E05AoGAYeIl
gCJWC78sATajoprbJEjlbFY3DqhfSHcMxv3ElYRyUjra9Kzq0cNVgTo1dinIk+Lz
FKZtHYHJeNFuTj6UyCADPtLVBjcVeC9T4FZVNF4hEvP636AK5qNWON7DexhO7qlr
2qwvHledgywF+Rkbg8QmPQaRdY1sQN8imGGNRb8CgYBoc1MSutl/BFppVs8aNAki
s+P7O0jifYvceqc3NOfQ36WLMS+7tzC5rI0KhVG7MEnkyi4hbtbSzPu74rTINsQc
UmmG14Or7Od/BYnLYgVUFK1LA6Xub37PbNqHPLaQM+41oH8n5139L50Hd6xKZ+7n
RonLBPsLONMcmb4U1ST9oQ==
-----END PRIVATE KEY-----
11 changes: 11 additions & 0 deletions examples/tcp/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<title>PHP Standard Library - TCP server</title>
</head>
<body>
<h1>Hello, World!</h1>
<pre><code>%s</code></pre>
</body>
</html>

8 changes: 1 addition & 7 deletions src/Psl/IO/Internal/ResourceHandle.php
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@
use Revolt\EventLoop\Suspension;

use function error_get_last;
use function fclose;
use function feof;
use function fread;
use function fseek;
@@ -360,13 +359,8 @@ public function close(): void
if ($this->close && is_resource($this->stream)) {
$stream = $this->stream;
$this->stream = null;
$result = @fclose($stream);
if ($result === false) {
/** @var array{message: string} $error */
$error = error_get_last();

throw new Exception\RuntimeException($error['message'] ?? 'unknown error.');
}
namespace\close_resource($stream);
} else {
// Stream could be set to a non-null closed-resource,
// if manually closed using `fclose($handle->getStream)`.
32 changes: 32 additions & 0 deletions src/Psl/IO/Internal/close_resource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Psl\IO\Internal;

use Psl\IO\Exception;

use function error_get_last;
use function fclose;

/**
* ` * Closes a given resource stream.
*
* @param resource $stream
*
* @internal
*
* @codeCoverageIgnore
*
* @throws Exception\RuntimeException If closing the stream fails.
*/
function close_resource(mixed $stream): void
{
$result = @fclose($stream);
if ($result === false) {
/** @var array{message: string} $error */
$error = error_get_last();

throw new Exception\RuntimeException($error['message'] ?? 'unknown error.');
}
}
Loading

Unchanged files with check annotations Beta

*
* @psalm-mutation-free
*/
public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0, ?Timezone $timezone = null): DateTime

Check warning on line 140 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0, ?Timezone $timezone = null) : DateTime + public static function todayAt(int $hours, int $minutes, int $seconds = 1, int $nanoseconds = 0, ?Timezone $timezone = null) : DateTime { return self::now($timezone)->withTime($hours, $minutes, $seconds, $nanoseconds); }

Check warning on line 140 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0, ?Timezone $timezone = null) : DateTime + public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 1, ?Timezone $timezone = null) : DateTime { return self::now($timezone)->withTime($hours, $minutes, $seconds, $nanoseconds); }

Check warning on line 140 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0, ?Timezone $timezone = null) : DateTime + public static function todayAt(int $hours, int $minutes, int $seconds = 1, int $nanoseconds = 0, ?Timezone $timezone = null) : DateTime { return self::now($timezone)->withTime($hours, $minutes, $seconds, $nanoseconds); }

Check warning on line 140 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0, ?Timezone $timezone = null) : DateTime + public static function todayAt(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 1, ?Timezone $timezone = null) : DateTime { return self::now($timezone)->withTime($hours, $minutes, $seconds, $nanoseconds); }
{
return self::now($timezone)->withTime($hours, $minutes, $seconds, $nanoseconds);
}
*
* @psalm-suppress ImpureMethodCall
*/
public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0): self

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 1, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 1, int $seconds = 0, int $nanoseconds = 0) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 1, int $nanoseconds = 0) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 1) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 1, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 1, int $seconds = 0, int $nanoseconds = 0) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 1, int $nanoseconds = 0) : self { if ($month instanceof Month) { $month = $month->value;

Check warning on line 169 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-suppress ImpureMethodCall */ - public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 0) : self + public static function fromParts(Timezone $timezone, int $year, Month|int $month, int $day, int $hours = 0, int $minutes = 0, int $seconds = 0, int $nanoseconds = 1) : self { if ($month instanceof Month) { $month = $month->value;
{
if ($month instanceof Month) {
$month = $month->value;
*/
public static function parse(string $raw_string, null|FormatPattern|string $pattern = null, ?Timezone $timezone = null, null|Locale $locale = null): static
{
$timezone ??= Timezone::default();

Check warning on line 307 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "AssignCoalesce": --- Original +++ New @@ @@ */ public static function parse(string $raw_string, null|FormatPattern|string $pattern = null, ?Timezone $timezone = null, null|Locale $locale = null) : static { - $timezone ??= Timezone::default(); + $timezone = Timezone::default(); return self::fromTimestamp(Timestamp::parse($raw_string, $pattern, $timezone, $locale), $timezone); } /**

Check warning on line 307 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "AssignCoalesce": --- Original +++ New @@ @@ */ public static function parse(string $raw_string, null|FormatPattern|string $pattern = null, ?Timezone $timezone = null, null|Locale $locale = null) : static { - $timezone ??= Timezone::default(); + $timezone = Timezone::default(); return self::fromTimestamp(Timestamp::parse($raw_string, $pattern, $timezone, $locale), $timezone); } /**
return self::fromTimestamp(Timestamp::parse($raw_string, $pattern, $timezone, $locale), $timezone);
}
*
* @psalm-mutation-free
*/
public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0): static

Check warning on line 498 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0) : static + public function withTime(int $hours, int $minutes, int $seconds = 1, int $nanoseconds = 0) : static { return static::fromParts($this->getTimezone(), $this->getYear(), $this->getMonth(), $this->getDay(), $hours, $minutes, $seconds, $nanoseconds); }

Check warning on line 498 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0) : static + public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 1) : static { return static::fromParts($this->getTimezone(), $this->getYear(), $this->getMonth(), $this->getDay(), $hours, $minutes, $seconds, $nanoseconds); }

Check warning on line 498 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0) : static + public function withTime(int $hours, int $minutes, int $seconds = 1, int $nanoseconds = 0) : static { return static::fromParts($this->getTimezone(), $this->getYear(), $this->getMonth(), $this->getDay(), $hours, $minutes, $seconds, $nanoseconds); }

Check warning on line 498 in src/Psl/DateTime/DateTime.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "IncrementInteger": --- Original +++ New @@ @@ * * @psalm-mutation-free */ - public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 0) : static + public function withTime(int $hours, int $minutes, int $seconds = 0, int $nanoseconds = 1) : static { return static::fromParts($this->getTimezone(), $this->getYear(), $this->getMonth(), $this->getDay(), $hours, $minutes, $seconds, $nanoseconds); }
{
return static::fromParts(
$this->getTimezone(),
*/
public function getDate(): array
{
return [$this->getYear(), $this->getMonth(), $this->getDay()];

Check warning on line 184 in src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "ArrayItemRemoval": --- Original +++ New @@ @@ */ public function getDate() : array { - return [$this->getYear(), $this->getMonth(), $this->getDay()]; + return [$this->getMonth(), $this->getDay()]; } /** * Returns the time (hours, minutes, seconds, nanoseconds).

Check warning on line 184 in src/Psl/DateTime/DateTimeConvenienceMethodsTrait.php

GitHub Actions / mutation tests (8.3, ubuntu-latest)

Escaped Mutant for Mutator "ArrayItemRemoval": --- Original +++ New @@ @@ */ public function getDate() : array { - return [$this->getYear(), $this->getMonth(), $this->getDay()]; + return [$this->getMonth(), $this->getDay()]; } /** * Returns the time (hours, minutes, seconds, nanoseconds).
}
/**