Skip to content
Merged
Show file tree
Hide file tree
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
141 changes: 100 additions & 41 deletions src/Commands/GenerateHelperCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@

namespace DragonCode\LaravelHttpMacros\Commands;

use Closure;
use DragonCode\LaravelHttpMacros\Macros\Macro;
use DragonCode\Support\Facades\Filesystem\Directory;
use DragonCode\Support\Facades\Filesystem\File;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use ReflectionFunction;
use ReflectionNamedType;
use ReflectionParameter;
use ReflectionUnionType;

use function array_map;
use function base_path;
use function class_exists;
use function collect;
use function config;
use function file_get_contents;
use function implode;
use function is_string;
use function is_numeric;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\outro;
use function sprintf;

use const PHP_EOL;
use function var_export;

class GenerateHelperCommand extends Command
{
Expand All @@ -29,71 +35,124 @@ class GenerateHelperCommand extends Command

public function handle(): void
{
$names = $this->names();
$this->sections()->map(function (array $macros, string $section) {
intro($section);

return $this->macros($macros);
})
->tap(fn () => outro('storing'))
->tap(fn () => $this->cleanUp())
->each(fn (Collection $blocks, string $section) => $this->store(
$section,
$this->compileBlocks($section, $blocks->flatten())
));
}

protected function cleanUp(): void
{
$this->components->task('clean up', fn () => Directory::ensureDelete($this->directory()));
}

protected function macros(array $macros): Collection
{
return collect($macros)->map(function (Macro|string $macro, int|string $name) {
$name = $this->resolveName($macro, $name);

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

$this->cleanUp();
$this->store($static, true);
$this->store($dynamic);
return $result;
});
}

protected function make(array $names, bool $isStatic = false): array
protected function store(string $section, string $content): void
{
return array_map(
fn (string $name) => sprintf(
' * @method %s $this %s(\Closure|string $class, int|string|null $key = null)',
$isStatic ? 'static' : '',
$name
),
$names
);
$this->components->task($section, fn () => File::store($this->helperPath($section), $content));
}

protected function store(array $methods, bool $isStatic = false): void
protected function compileBlocks(string $section, Collection $blocks): string
{
File::store(
$this->path($this->filename($isStatic)),
$this->makeDocBlock($methods)
return Str::replace(
['{class}', '{methods}'],
[
Str::studly($section),
$blocks->implode("\n"),
],
$this->stub()
);
}

protected function makeDocBlock(array $methods): string
protected function prepare(string $name, array $functions): array
{
return Str::replace('{methods}', implode(PHP_EOL, $methods), $this->template());
return $this->docBlock($name, $this->docBlockParameters($functions));
}

protected function names(): array
protected function docBlock(string $name, string $parameters): array
{
return collect($this->macros())->map(
fn (Macro|string $macro, int|string $name) => is_string($name) ? $name : $macro::name()
)->all();
return [
sprintf(' * @method $this %s(%s)', $name, $parameters),
sprintf(' * @method static $this %s(%s)', $name, $parameters),
];
}

protected function path(?string $filename = null): string
/**
* @param array<ReflectionParameter> $functions
*
* @return Collection
*/
protected function docBlockParameters(array $functions): string
{
return base_path('vendor/_http_macros/' . $filename);
return collect($functions)->map(function (ReflectionParameter $parameter) {
$result = $parameter->hasType() ? $this->compactTypes($parameter->getType()) : 'mixed';

$result .= ' $' . $parameter->getName();

if ($parameter->isDefaultValueAvailable()) {
$result .= ' = ' . var_export($parameter->getDefaultValue(), true);
}

return $result;
})->implode(', ');
}

protected function filename(bool $isStatic): string
protected function compactTypes(ReflectionNamedType|ReflectionUnionType $type): string
{
return $isStatic
? '_ide_helper_macro_static.php'
: '_ide_helper_macro.php';
if ($type instanceof ReflectionNamedType) {
return class_exists($type->getName()) ? '\\' . $type->getName() : $type->getName();
}

return collect($type->getTypes())->map(
fn (ReflectionNamedType $type) => $this->compactTypes($type)
)->implode('|');
}

protected function cleanUp(): void
protected function reflectionCallback(Closure $callback): ReflectionFunction
{
return new ReflectionFunction($callback);
}

protected function resolveName(Macro|string $macro, int|string $name): string
{
return is_numeric($name) ? $macro::name() : $name;
}

protected function sections(): Collection
{
return collect(config('http.macros', []));
}

protected function helperPath(string $name): string
{
Directory::ensureDelete($this->path());
return $this->directory() . "/_ide_helper_macro_$name.php";
}

protected function macros(): array
protected function directory(): string
{
return config('http.macros.response', []);
return base_path('vendor/_http_macros');
}

protected function template(): string
protected function stub(): string
{
return file_get_contents(__DIR__ . '/../../stubs/helper.stub');
}
Expand Down
2 changes: 1 addition & 1 deletion stubs/helper.stub
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace Illuminate\Http\Client {
/**
{methods}
*/
class Response {}
class {class} {}
}
5 changes: 1 addition & 4 deletions tests/Datasets/types.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,4 @@

declare(strict_types=1);

dataset('types', [
['static', '_static'],
['dynamic', ''],
]);
dataset('types', ['request', 'response']);
2 changes: 1 addition & 1 deletion tests/Helpers/content.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

function content(string $filename): string
{
return trim(file_get_contents($filename));
return file_get_contents($filename);
}
12 changes: 0 additions & 12 deletions tests/Snapshots/dynamic

This file was deleted.

11 changes: 11 additions & 0 deletions tests/Snapshots/request
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

/** @noinspection all */

namespace Illuminate\Http\Client {
/**
* @method $this withLogger(string $channel)
* @method static $this withLogger(string $channel)
*/
class Request {}
}
15 changes: 15 additions & 0 deletions tests/Snapshots/response
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

/** @noinspection all */

namespace Illuminate\Http\Client {
/**
* @method $this toData(\Closure|string $class, string|int|null $key = NULL)
* @method static $this toData(\Closure|string $class, string|int|null $key = NULL)
* @method $this toDataCollection(\Closure|string $class, string|int|null $key = NULL)
* @method static $this toDataCollection(\Closure|string $class, string|int|null $key = NULL)
* @method $this toFoo(\Closure|string $class, string|int|null $key = NULL)
* @method static $this toFoo(\Closure|string $class, string|int|null $key = NULL)
*/
class Response {}
}
12 changes: 0 additions & 12 deletions tests/Snapshots/static

This file was deleted.

12 changes: 6 additions & 6 deletions tests/Unit/Commands/GenerateHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
fn () => Directory::ensureDelete(base_path('vendor/_http_macros'))
);

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

expect($directory)->not->toBeDirectory();
Expand All @@ -25,9 +25,9 @@
expect(content($filename))->toBe(content($snapshot));
})->with('types');

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

expect($directory)->not->toBeDirectory();
Expand All @@ -40,9 +40,9 @@
expect(content($filename))->toBe(content($snapshot));
})->with('types');

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

expect($directory)->not->toBeDirectory();
Expand Down