Skip to content

Commit 346adf8

Browse files
committed
Refactor command logic for improved reliability and readability, update dependencies, and add new tests for edge cases.
1 parent f6da127 commit 346adf8

6 files changed

Lines changed: 80 additions & 10 deletions

File tree

app/.gitkeep

Whitespace-only changes.

composer.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
},
2121
"require": {
2222
"php": "^8.2",
23-
"laravel/framework": ">=9.0"
23+
"laravel/framework": "^11.0|^12.0"
2424
},
2525
"require-dev": {
2626
"larastan/larastan": "^2.0|^3.0",
27-
"orchestra/testbench": "^7.0|^8.0|^9.0|^10.0",
27+
"orchestra/testbench": "^9.0|^10.0",
2828
"php-parallel-lint/php-parallel-lint": "^1.4",
2929
"phpstan/extension-installer": "^1.4",
3030
"phpunit/phpunit": "^9.0|^10.0|^11.0",
@@ -79,4 +79,4 @@
7979
"phpstan/extension-installer": true
8080
}
8181
}
82-
}
82+
}

src/Commands/BaseCommand.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ public function __construct(
2020
protected function preprocess(): void
2121
{
2222
$path = $this->option('output');
23+
$basePath = $this->base();
2324

24-
$this->files->ensureDirectoryExists(dirname($this->option('input')));
25+
if (!$this->files->isDirectory($basePath) || !$this->files->isReadable($basePath)) {
26+
throw new \RuntimeException("Input path [{$basePath}] does not exist or is not readable.");
27+
}
2528

2629
if ($this->files->exists($path)) {
2730
$this->files->deleteDirectory($path);
@@ -114,7 +117,7 @@ protected function classInfoFromPath(string $path): array
114117
return [
115118
'namespace' => '',
116119
'class' => '',
117-
'fqcn' => '\\',
120+
'fqcn' => '',
118121
'extends' => '',
119122
'implements' => [],
120123
];
@@ -133,7 +136,7 @@ protected function classInfoFromPath(string $path): array
133136
return [
134137
'namespace' => '',
135138
'class' => '',
136-
'fqcn' => '\\',
139+
'fqcn' => '',
137140
'extends' => '',
138141
'implements' => [],
139142
];
@@ -163,7 +166,7 @@ protected function classInfoFromPath(string $path): array
163166
return [
164167
'namespace' => $namespace,
165168
'class' => $class,
166-
'fqcn' => $namespace . '\\' . $class,
169+
'fqcn' => trim($namespace . '\\' . $class, '\\'),
167170
'extends' => $extends,
168171
'implements' => $implements,
169172
];
@@ -219,6 +222,10 @@ protected function readClassDeclaration(string $path): string
219222

220223
$handle = fopen($path, 'r');
221224

225+
if ($handle === false) {
226+
return '';
227+
}
228+
222229
while (!feof($handle)) {
223230
$buffer .= fread($handle, 512);
224231

@@ -457,11 +464,18 @@ protected function appendImport(array &$imports, string $prefix, string $name, s
457464

458465
$resolvedAlias = $alias !== ''
459466
? $alias
460-
: substr($fqcn, strrpos($fqcn, '\\') + 1);
467+
: $this->defaultImportAlias($fqcn);
461468

462469
$imports[$resolvedAlias] = $fqcn;
463470
}
464471

472+
protected function defaultImportAlias(string $fqcn): string
473+
{
474+
$position = strrpos($fqcn, '\\');
475+
476+
return $position === false ? $fqcn : substr($fqcn, $position + 1);
477+
}
478+
465479
protected function parseImplementedInterfaces(array $tokens, int $index, string $namespace, array $imports): array
466480
{
467481
$implements = [];

src/Commands/GenerateInterfaceUnionsCommand.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,20 @@ protected function readInterfaces(string $path, array $interfaces)
5656
$rootNamespace = $this->determineRootNamespace($interfaces);
5757

5858
$classes = collect($paths)
59-
->reject(fn ($i) => !$i->isFile() || !str_ends_with($i->getRealPath(), '.php'))
60-
->map(fn ($item) => $this->classInfoFromPath($item->getRealPath()))
59+
->map(function ($item) {
60+
if (!$item->isFile()) {
61+
return null;
62+
}
63+
64+
$realPath = $item->getRealPath();
65+
66+
if ($realPath === false || !str_ends_with($realPath, '.php')) {
67+
return null;
68+
}
69+
70+
return $this->classInfoFromPath($realPath);
71+
})
72+
->filter(fn ($info) => is_array($info) && $info['fqcn'] !== '')
6173
->keyBy('fqcn');
6274

6375
return collect($interfaces)

tests/Commands/GenerateInterfaceUnionsCommandTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,24 @@ public function testParserExtractsInheritedParentClass(): void
104104
$this->assertSame([], $info['implements']);
105105
}
106106

107+
public function testParserResolvesSingleSegmentImports(): void
108+
{
109+
$info = $this->makeParserCommand()->classInfo('./workbench/app/Models/GlobalImportArrayAccessible.php');
110+
111+
$this->assertSame(['ArrayAccess'], $info['implements']);
112+
}
113+
114+
public function testCommandFailsForInvalidInputPath(): void
115+
{
116+
$this->expectException(\RuntimeException::class);
117+
$this->expectExceptionMessage('does not exist or is not readable');
118+
119+
$command = $this->artisan('synergi-types:interface-unions --input=app/DoesNotExist --output=invalid-models');
120+
121+
$this->assertInstanceOf(PendingCommand::class, $command);
122+
$command->run();
123+
}
124+
107125
protected function makeParserCommand(): TestableGenerateInterfaceUnionsCommand
108126
{
109127
return new TestableGenerateInterfaceUnionsCommand(new Filesystem());
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use ArrayAccess;
6+
7+
class GlobalImportArrayAccessible implements ArrayAccess
8+
{
9+
public function offsetExists(mixed $offset): bool
10+
{
11+
return false;
12+
}
13+
14+
public function offsetGet(mixed $offset): mixed
15+
{
16+
return null;
17+
}
18+
19+
public function offsetSet(mixed $offset, mixed $value): void
20+
{
21+
}
22+
23+
public function offsetUnset(mixed $offset): void
24+
{
25+
}
26+
}

0 commit comments

Comments
 (0)