Skip to content

Commit 630219e

Browse files
Improved generation of helper files for IDE
1 parent 36e820a commit 630219e

File tree

9 files changed

+135
-77
lines changed

9 files changed

+135
-77
lines changed

src/Commands/GenerateHelperCommand.php

Lines changed: 100 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,28 @@
44

55
namespace DragonCode\LaravelHttpMacros\Commands;
66

7+
use Closure;
78
use DragonCode\LaravelHttpMacros\Macros\Macro;
89
use DragonCode\Support\Facades\Filesystem\Directory;
910
use DragonCode\Support\Facades\Filesystem\File;
1011
use Illuminate\Console\Command;
12+
use Illuminate\Support\Collection;
1113
use Illuminate\Support\Str;
14+
use ReflectionFunction;
15+
use ReflectionNamedType;
16+
use ReflectionParameter;
17+
use ReflectionUnionType;
1218

13-
use function array_map;
1419
use function base_path;
20+
use function class_exists;
1521
use function collect;
1622
use function config;
1723
use function file_get_contents;
18-
use function implode;
19-
use function is_string;
24+
use function is_numeric;
25+
use function Laravel\Prompts\intro;
26+
use function Laravel\Prompts\outro;
2027
use function sprintf;
21-
22-
use const PHP_EOL;
28+
use function var_export;
2329

2430
class GenerateHelperCommand extends Command
2531
{
@@ -29,71 +35,124 @@ class GenerateHelperCommand extends Command
2935

3036
public function handle(): void
3137
{
32-
$names = $this->names();
38+
$this->sections()->map(function (array $macros, string $section) {
39+
intro($section);
40+
41+
return $this->macros($macros);
42+
})
43+
->tap(fn () => outro('storing'))
44+
->tap(fn () => $this->cleanUp())
45+
->each(fn (Collection $blocks, string $section) => $this->store(
46+
$section,
47+
$this->compileBlocks($section, $blocks->flatten())
48+
));
49+
}
50+
51+
protected function cleanUp(): void
52+
{
53+
$this->components->task('clean up', fn () => Directory::ensureDelete($this->directory()));
54+
}
55+
56+
protected function macros(array $macros): Collection
57+
{
58+
return collect($macros)->map(function (Macro|string $macro, int|string $name) {
59+
$name = $this->resolveName($macro, $name);
3360

34-
$static = $this->make($names, true);
35-
$dynamic = $this->make($names);
61+
$this->components->task($name, function () use ($macro, $name, &$result) {
62+
$result = $this->prepare($name, $this->reflectionCallback($macro::callback())->getParameters());
63+
});
3664

37-
$this->cleanUp();
38-
$this->store($static, true);
39-
$this->store($dynamic);
65+
return $result;
66+
});
4067
}
4168

42-
protected function make(array $names, bool $isStatic = false): array
69+
protected function store(string $section, string $content): void
4370
{
44-
return array_map(
45-
fn (string $name) => sprintf(
46-
' * @method %s $this %s(\Closure|string $class, int|string|null $key = null)',
47-
$isStatic ? 'static' : '',
48-
$name
49-
),
50-
$names
51-
);
71+
$this->components->task($section, fn () => File::store($this->helperPath($section), $content));
5272
}
5373

54-
protected function store(array $methods, bool $isStatic = false): void
74+
protected function compileBlocks(string $section, Collection $blocks): string
5575
{
56-
File::store(
57-
$this->path($this->filename($isStatic)),
58-
$this->makeDocBlock($methods)
76+
return Str::replace(
77+
['{class}', '{methods}'],
78+
[
79+
Str::studly($section),
80+
$blocks->implode("\n"),
81+
],
82+
$this->stub()
5983
);
6084
}
6185

62-
protected function makeDocBlock(array $methods): string
86+
protected function prepare(string $name, array $functions): array
6387
{
64-
return Str::replace('{methods}', implode(PHP_EOL, $methods), $this->template());
88+
return $this->docBlock($name, $this->docBlockParameters($functions));
6589
}
6690

67-
protected function names(): array
91+
protected function docBlock(string $name, string $parameters): array
6892
{
69-
return collect($this->macros())->map(
70-
fn (Macro|string $macro, int|string $name) => is_string($name) ? $name : $macro::name()
71-
)->all();
93+
return [
94+
sprintf(' * @method $this %s(%s)', $name, $parameters),
95+
sprintf(' * @method static $this %s(%s)', $name, $parameters),
96+
];
7297
}
7398

74-
protected function path(?string $filename = null): string
99+
/**
100+
* @param array<ReflectionParameter> $functions
101+
*
102+
* @return Collection
103+
*/
104+
protected function docBlockParameters(array $functions): string
75105
{
76-
return base_path('vendor/_http_macros/' . $filename);
106+
return collect($functions)->map(function (ReflectionParameter $parameter) {
107+
$result = $parameter->hasType() ? $this->compactTypes($parameter->getType()) : 'mixed';
108+
109+
$result .= ' $' . $parameter->getName();
110+
111+
if ($parameter->isDefaultValueAvailable()) {
112+
$result .= ' = ' . var_export($parameter->getDefaultValue(), true);
113+
}
114+
115+
return $result;
116+
})->implode(', ');
77117
}
78118

79-
protected function filename(bool $isStatic): string
119+
protected function compactTypes(ReflectionNamedType|ReflectionUnionType $type): string
80120
{
81-
return $isStatic
82-
? '_ide_helper_macro_static.php'
83-
: '_ide_helper_macro.php';
121+
if ($type instanceof ReflectionNamedType) {
122+
return class_exists($type->getName()) ? '\\' . $type->getName() : $type->getName();
123+
}
124+
125+
return collect($type->getTypes())->map(
126+
fn (ReflectionNamedType $type) => $this->compactTypes($type)
127+
)->implode('|');
84128
}
85129

86-
protected function cleanUp(): void
130+
protected function reflectionCallback(Closure $callback): ReflectionFunction
131+
{
132+
return new ReflectionFunction($callback);
133+
}
134+
135+
protected function resolveName(Macro|string $macro, int|string $name): string
136+
{
137+
return is_numeric($name) ? $macro::name() : $name;
138+
}
139+
140+
protected function sections(): Collection
141+
{
142+
return collect(config('http.macros', []));
143+
}
144+
145+
protected function helperPath(string $name): string
87146
{
88-
Directory::ensureDelete($this->path());
147+
return $this->directory() . "/_ide_helper_macro_$name.php";
89148
}
90149

91-
protected function macros(): array
150+
protected function directory(): string
92151
{
93-
return config('http.macros.response', []);
152+
return base_path('vendor/_http_macros');
94153
}
95154

96-
protected function template(): string
155+
protected function stub(): string
97156
{
98157
return file_get_contents(__DIR__ . '/../../stubs/helper.stub');
99158
}

stubs/helper.stub

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ namespace Illuminate\Http\Client {
66
/**
77
{methods}
88
*/
9-
class Response {}
9+
class {class} {}
1010
}

tests/Datasets/types.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,4 @@
22

33
declare(strict_types=1);
44

5-
dataset('types', [
6-
['static', '_static'],
7-
['dynamic', ''],
8-
]);
5+
dataset('types', ['request', 'response']);

tests/Helpers/content.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44

55
function content(string $filename): string
66
{
7-
return trim(file_get_contents($filename));
7+
return file_get_contents($filename);
88
}

tests/Snapshots/dynamic

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/Snapshots/request

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
/** @noinspection all */
4+
5+
namespace Illuminate\Http\Client {
6+
/**
7+
* @method $this withLogger(string $channel)
8+
* @method static $this withLogger(string $channel)
9+
*/
10+
class Request {}
11+
}

tests/Snapshots/response

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
/** @noinspection all */
4+
5+
namespace Illuminate\Http\Client {
6+
/**
7+
* @method $this toData(\Closure|string $class, string|int|null $key = NULL)
8+
* @method static $this toData(\Closure|string $class, string|int|null $key = NULL)
9+
* @method $this toDataCollection(\Closure|string $class, string|int|null $key = NULL)
10+
* @method static $this toDataCollection(\Closure|string $class, string|int|null $key = NULL)
11+
* @method $this toFoo(\Closure|string $class, string|int|null $key = NULL)
12+
* @method static $this toFoo(\Closure|string $class, string|int|null $key = NULL)
13+
*/
14+
class Response {}
15+
}

tests/Snapshots/static

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/Unit/Commands/GenerateHelperTest.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
fn () => Directory::ensureDelete(base_path('vendor/_http_macros'))
1313
);
1414

15-
test('new generation', closure: function (string $type, string $name) {
15+
test('new generation', function (string $type) {
1616
$directory = base_path('vendor/_http_macros');
17-
$filename = $directory . "/_ide_helper_macro$name.php";
17+
$filename = $directory . "/_ide_helper_macro_$type.php";
1818
$snapshot = __DIR__ . "/../../Snapshots/$type";
1919

2020
expect($directory)->not->toBeDirectory();
@@ -25,9 +25,9 @@
2525
expect(content($filename))->toBe(content($snapshot));
2626
})->with('types');
2727

28-
test('replace', closure: function (string $type, string $name) {
28+
test('replace', function (string $type) {
2929
$directory = base_path('vendor/_http_macros');
30-
$filename = $directory . "/_ide_helper_macro$name.php";
30+
$filename = $directory . "/_ide_helper_macro_$type.php";
3131
$snapshot = __DIR__ . "/../../Snapshots/$type";
3232

3333
expect($directory)->not->toBeDirectory();
@@ -40,9 +40,9 @@
4040
expect(content($filename))->toBe(content($snapshot));
4141
})->with('types');
4242

43-
test('clean up', closure: function (string $type, string $name) {
43+
test('clean up', function (string $type) {
4444
$directory = base_path('vendor/_http_macros');
45-
$filename = $directory . "/_ide_helper_macro$name.php";
45+
$filename = $directory . "/_ide_helper_macro_$type.php";
4646
$snapshot = __DIR__ . "/../../Snapshots/$type";
4747

4848
expect($directory)->not->toBeDirectory();

0 commit comments

Comments
 (0)