Skip to content

Commit 941639b

Browse files
committed
Add NativePipeRunner and PipePipelineService with multi-step pipeline functionality and tests
1 parent 1b2e428 commit 941639b

3 files changed

Lines changed: 163 additions & 0 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypePHP\Tests\Fixtures\Pipes;
6+
7+
class NativePipeRunner
8+
{
9+
public function runPipeline(int $initialId, PipePipelineService $service): string
10+
{
11+
return $initialId
12+
|> $service->doubleId(...)
13+
|> $service->stringify(...)
14+
|> $service->prefixTag(...);
15+
}
16+
17+
public function runStandalonePipeline(int $id): string
18+
{
19+
return $id
20+
|> $this->stepOne(...)
21+
|> $this->stepTwo(...);
22+
}
23+
24+
/**
25+
* @param positive-int $id
26+
*
27+
* @return positive-int
28+
*/
29+
public function stepOne(int $id): int
30+
{
31+
return $id + 10;
32+
}
33+
34+
/**
35+
* @param positive-int $id
36+
*
37+
* @return non-empty-string
38+
*/
39+
public function stepTwo(int $id): string
40+
{
41+
return "piped_user_{$id}";
42+
}
43+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypePHP\Tests\Fixtures\Pipes;
6+
7+
class PipePipelineService
8+
{
9+
/**
10+
* @param positive-int $id
11+
*
12+
* @return positive-int
13+
*/
14+
public function doubleId(int $id): int
15+
{
16+
return $id * 2;
17+
}
18+
19+
/**
20+
* @param positive-int $id
21+
*
22+
* @return non-empty-string
23+
*/
24+
public function stringify(int $id): string
25+
{
26+
return "RECORD_{$id}";
27+
}
28+
29+
/**
30+
* @param non-empty-string $tag
31+
*
32+
* @return non-empty-string
33+
*/
34+
public function prefixTag(string $tag): string
35+
{
36+
return "[TAG] {$tag}";
37+
}
38+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
if (PHP_VERSION_ID < 80500) {
6+
return;
7+
}
8+
9+
use TypePHP\Internal\StreamWrapper;
10+
use TypePHP\Tests\Fixtures\Pipes\NativePipeRunner;
11+
use TypePHP\Tests\Fixtures\Pipes\PipePipelineService;
12+
13+
describe('PHP 8.5+ Pipe Operator (|>) and Multi-Step Pipelines', function () {
14+
describe('Native Pipe Execution (PHP 8.5+)', function () {
15+
test('executes multi-step piped transformation through first-class callables', function () {
16+
$service = new PipePipelineService();
17+
$runner = new NativePipeRunner();
18+
19+
$result = $runner->runPipeline(10, $service);
20+
21+
expect($result)->toBe('[TAG] RECORD_20');
22+
});
23+
24+
test('throws TypeError at the exact pipe step where parameter contract is violated', function () {
25+
$service = new PipePipelineService();
26+
$runner = new NativePipeRunner();
27+
28+
expect(fn () => $runner->runPipeline(-5, $service))
29+
->toThrow(TypeError::class, 'positive-int');
30+
});
31+
32+
test('executes standalone method pipeline with native pipe operator', function () {
33+
$runner = new NativePipeRunner();
34+
35+
$result = $runner->runStandalonePipeline(5);
36+
37+
expect($result)->toBe('piped_user_15');
38+
});
39+
40+
test('throws TypeError when standalone pipe step receives invalid integer', function () {
41+
$runner = new NativePipeRunner();
42+
43+
expect(fn () => $runner->runStandalonePipeline(-50))
44+
->toThrow(TypeError::class, 'positive-int');
45+
});
46+
});
47+
48+
describe('AST Transformation & Zero Line-Drift with Pipe Syntax', function () {
49+
test('transforms code containing multi-line pipe operator chains with zero line-drift', function () {
50+
$source = <<<'PHP'
51+
<?php
52+
53+
declare(strict_types=1);
54+
55+
/**
56+
* @param positive-int $id
57+
* @return non-empty-string
58+
*/
59+
function formatId(int $id): string
60+
{
61+
return "ID_{$id}";
62+
}
63+
64+
$service = new \TypePHP\Tests\Fixtures\Pipes\PipePipelineService();
65+
66+
$targetLine = true;
67+
PHP;
68+
69+
$transformed = StreamWrapper::transformSource($source, 'test_pipe_drift.php');
70+
71+
$origLines = explode("\n", str_replace("\r\n", "\n", $source));
72+
$transLines = explode("\n", str_replace("\r\n", "\n", $transformed));
73+
74+
expect(count($transLines))->toBe(count($origLines));
75+
76+
$origTarget = array_search('$targetLine = true;', array_map('trim', $origLines), true);
77+
$transTarget = array_search('$targetLine = true;', array_map('trim', $transLines), true);
78+
79+
expect($transTarget)->toBe($origTarget);
80+
});
81+
});
82+
});

0 commit comments

Comments
 (0)