Skip to content

Commit 85517d6

Browse files
committed
test: increase coverage of LogicStreamWrapper
1 parent e522f23 commit 85517d6

File tree

6 files changed

+212
-44
lines changed

6 files changed

+212
-44
lines changed

phpcs.xml

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
<rule ref="Generic.Files.EndFileNewline" />
2020
<rule ref="Generic.Files.InlineHTML" />
2121
<rule ref="Generic.Files.LineEndings" />
22-
<rule ref="Generic.Files.LineLength" />
22+
<rule ref="Generic.Files.LineLength">
23+
<properties>
24+
<property name="lineLimit" value="100"/>
25+
</properties>
26+
</rule>
2327
<rule ref="Generic.Files.OneClassPerFile" />
2428
<rule ref="Generic.Files.OneInterfacePerFile" />
2529
<rule ref="Generic.Files.OneObjectStructurePerFile" />

phpunit.xml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
colors="true"
66
cacheDirectory="test/phpunit/.phpunit.cache"
77
bootstrap="vendor/autoload.php"
8+
displayDetailsOnTestsThatTriggerDeprecations="true"
89
>
910
<coverage />
1011

src/LogicStream/LogicStreamWrapper.php

+14-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace Gt\Routing\LogicStream;
33

44
use Exception;
5+
use Gt\Routing\RoutingException;
56
use SplFileObject;
67

78
class LogicStreamWrapper {
@@ -11,6 +12,8 @@ class LogicStreamWrapper {
1112
private int $position;
1213
private string $path;
1314
private string $contents;
15+
/** @var resource */
16+
public $context;
1417

1518
function stream_open(string $path):bool {
1619
$this->position = 0;
@@ -62,10 +65,18 @@ private function loadContents(SplFileObject $file):void {
6265
while(!$file->eof() && !$foundNamespace) {
6366
$line = $file->fgets();
6467
if($lineNumber === 0) {
65-
// TODO: Allow hashbangs before <?php
66-
// Maybe this is possible by just skipping while the first character is a hash, and not increasing the line number.
68+
if($line[0] === "#") {
69+
$lineNumber++;
70+
continue;
71+
}
72+
6773
if(!str_starts_with($line, "<?php")) {
68-
throw new Exception("Logic file at " . $this->path . " must start by opening a PHP tag. See https://www.php.gt/routing/logic-stream-wrapper");
74+
throw new RoutingException(
75+
"Logic file at "
76+
. $this->path
77+
. " must start by opening a PHP tag. "
78+
. " See https://www.php.gt/routing/logic-stream-wrapper"
79+
);
6980
}
7081
}
7182
$trimmedLine = trim($line);

src/Path/DynamicPath.php

+56-31
Original file line numberDiff line numberDiff line change
@@ -19,44 +19,69 @@ public function get(?string $key = null, bool $extra = false):?string {
1919

2020
foreach($this->assemblyList as $assembly) {
2121
foreach($assembly as $filePath) {
22-
$filePathParts = explode("/", $filePath);
23-
foreach($filePathParts as $i => $f) {
24-
$f = strtok($f, ".");
25-
if($f[0] !== "@") {
26-
continue;
27-
}
28-
29-
if(is_null($key)) {
30-
if($extra) {
31-
$test = "";
32-
for($ppi = count($filePathParts), $len = count($requestPathParts); $ppi < $len; $ppi++) {
33-
$test .= $requestPathParts[$ppi];
34-
$test .= "/";
35-
}
36-
return rtrim($test, "/");
37-
}
38-
else {
39-
return $requestPathParts[count($filePathParts) - 1] ?? null;
40-
}
41-
}
42-
43-
if(ltrim($f, "@") !== $key) {
44-
continue;
45-
}
46-
47-
$r = $requestPathParts[$i] ?? null;
48-
if(!$r) {
49-
continue;
50-
}
51-
52-
return $r;
22+
$result = $this->processFilePath($filePath, $requestPathParts, $key, $extra);
23+
if(!is_null($result)) {
24+
return $result;
5325
}
5426
}
5527
}
5628

5729
return null;
5830
}
5931

32+
/** @param array<string> $requestPathParts */
33+
private function processFilePath(
34+
string $filePath,
35+
array $requestPathParts,
36+
?string $key,
37+
bool $extra,
38+
):?string {
39+
$filePathParts = explode("/", $filePath);
40+
41+
foreach($filePathParts as $i => $f) {
42+
$f = strtok($f, ".");
43+
if($f[0] !== "@") {
44+
continue;
45+
}
46+
47+
if(is_null($key)) {
48+
return $this->processNullKey($filePathParts, $requestPathParts, $extra);
49+
}
50+
51+
if(ltrim($f, "@") !== $key) {
52+
continue;
53+
}
54+
55+
$r = $requestPathParts[$i] ?? null;
56+
if($r) {
57+
return $r;
58+
}
59+
}
60+
61+
return null;
62+
}
63+
64+
/**
65+
* @param array<string> $filePathParts
66+
* @param array<string> $requestPathParts
67+
*/
68+
private function processNullKey(
69+
array $filePathParts,
70+
array $requestPathParts,
71+
bool $extra,
72+
):?string {
73+
if($extra) {
74+
$test = "";
75+
for($ppi = count($filePathParts), $len = count($requestPathParts); $ppi < $len; $ppi++) {
76+
$test .= $requestPathParts[$ppi];
77+
$test .= "/";
78+
}
79+
return rtrim($test, "/");
80+
}
81+
82+
return $requestPathParts[count($filePathParts) - 1] ?? null;
83+
}
84+
6085
public function getUrl(string $viewBasePath):string {
6186
$path = "";
6287

src/RoutingException.php

-9
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,4 @@
55
use Throwable;
66

77
class RoutingException extends RuntimeException {
8-
const DEFAULT_MESSAGE = "";
9-
10-
public function __construct(
11-
string $message = self::DEFAULT_MESSAGE,
12-
int $code = 0,
13-
?Throwable $previous = null
14-
) {
15-
parent::__construct($message, $code, $previous);
16-
}
178
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
namespace Gt\Routing\Test\LogicStream;
3+
4+
use Gt\Routing\LogicStream\LogicStreamWrapper;
5+
use Gt\Routing\RoutingException;
6+
use PHPUnit\Framework\TestCase;
7+
use RuntimeException;
8+
9+
class LogicStreamWrapperTest extends TestCase {
10+
public function testFileDoesNotExist():void {
11+
$sut = new LogicStreamWrapper();
12+
self::expectException(RuntimeException::class);
13+
self::expectExceptionMessage("Failed to open stream: No such file or directory");
14+
$sut->stream_open("/does/not/exist");
15+
}
16+
17+
public function testStreamRead_withNamespace():void {
18+
stream_wrapper_register("gt-routing-test", LogicStreamWrapper::class);
19+
$sourcePath = "TmpFiles/" . uniqid() . ".php";
20+
$source = <<<PHP
21+
<?php
22+
23+
namespace Example;
24+
25+
class Test {
26+
}
27+
PHP;
28+
if(!is_dir(dirname($sourcePath))) {
29+
mkdir(dirname($sourcePath), recursive: true);
30+
}
31+
32+
file_put_contents($sourcePath, $source);
33+
34+
$actualContents = file_get_contents("gt-routing-test://$sourcePath");
35+
unlink($sourcePath);
36+
rmdir(dirname($sourcePath));
37+
self::assertSame($source, $actualContents);
38+
}
39+
40+
public function testStreamRead_withoutNamespace():void {
41+
stream_wrapper_register("gt-routing-test", LogicStreamWrapper::class);
42+
$uniqid = uniqid();
43+
$sourcePath = "TmpFiles/$uniqid.php";
44+
$source = <<<PHP
45+
<?php
46+
47+
function example() {
48+
// This is just an example that should be wrapped in a class.
49+
}
50+
PHP;
51+
if(!is_dir($sourcePath)) {
52+
mkdir(dirname($sourcePath), recursive: true);
53+
}
54+
file_put_contents($sourcePath, $source);
55+
56+
$actualContents = file_get_contents("gt-routing-test://$sourcePath");
57+
58+
unlink($sourcePath);
59+
rmdir(dirname($sourcePath));
60+
61+
$expectedContents = <<<PHP
62+
<?php
63+
64+
namespace Gt\AppLogic\TmpFiles\\${uniqid}_php; function example() {
65+
// This is just an example that should be wrapped in a class.
66+
}
67+
PHP;
68+
69+
70+
self::assertSame($expectedContents, $actualContents);
71+
}
72+
73+
public function testStreamRead_withComment():void {
74+
stream_wrapper_register("gt-routing-test", LogicStreamWrapper::class);
75+
$uniqid = uniqid();
76+
$sourcePath = "TmpFiles/$uniqid.php";
77+
$source = <<<PHP
78+
<?php
79+
/**
80+
* This is an example docblock comment!
81+
*/
82+
function example() {
83+
// This is just an example that should be wrapped in a class.
84+
}
85+
PHP;
86+
if(!is_dir($sourcePath)) {
87+
mkdir(dirname($sourcePath), recursive: true);
88+
}
89+
file_put_contents($sourcePath, $source);
90+
91+
$actualContents = file_get_contents("gt-routing-test://$sourcePath");
92+
93+
unlink($sourcePath);
94+
rmdir(dirname($sourcePath));
95+
96+
$expectedContents = <<<PHP
97+
<?php
98+
/**
99+
* This is an example docblock comment!
100+
*/
101+
namespace Gt\AppLogic\TmpFiles\\${uniqid}_php; function example() {
102+
// This is just an example that should be wrapped in a class.
103+
}
104+
PHP;
105+
106+
self::assertSame($expectedContents, $actualContents);
107+
}
108+
109+
public function testStreamRead_notPhp():void {
110+
stream_wrapper_register("gt-routing-test", LogicStreamWrapper::class);
111+
$uniqid = uniqid();
112+
$sourcePath = "TmpFiles/$uniqid.php";
113+
$source = <<<PHP
114+
/**
115+
* This is a PHP file that doesn't start with the opening tags.
116+
*/
117+
<h1>Hello, World!</h1>
118+
PHP;
119+
if(!is_dir($sourcePath)) {
120+
mkdir(dirname($sourcePath), recursive: true);
121+
}
122+
file_put_contents($sourcePath, $source);
123+
124+
$exception = null;
125+
try {
126+
file_get_contents("gt-routing-test://$sourcePath");
127+
}
128+
catch(RoutingException $exception) {}
129+
130+
unlink($sourcePath);
131+
rmdir(dirname($sourcePath));
132+
133+
self::assertInstanceOf(RoutingException::class, $exception);
134+
self::assertStringContainsString("Logic file at TmpFiles/$uniqid.php must start by opening a PHP tag", $exception->getMessage());
135+
}
136+
}

0 commit comments

Comments
 (0)