dedoc/scramble

PHP native Enum type not supported

davidmohamedfr opened this issue · 9 comments

Hi !
I'm having a "Path cannot be empty" error on docs generation because i'm using native Enum class from PHP

From what I've investigate, trouble come from MethodReflector:getReflection using new ReflectionMethod when reflection for Enum need to be used with new ReflectionEnum

Hope this will help someone fix this issue.

@davidmohamedfr please share some code examples so I can reproduce

This is an example.

I'm in PHP 8.3.2, using Laravel Framework 11.2.0 and Scramble 0.10.13

enum MyEnum: int
{
    case TITI = 1;
    case TOTO = 2;

    public function label(): string
    {
        return match ($this) {
            Origin::TITI => 'titi',
            Origin::TOTO => 'toto',
        };
    }
}
enum CarsEnum: int
{
    case TOYOTA= 1;
    case FERRARI = 2;
}
   /**
     * @param array<string, mixed> $data
     */
    public static function create(array $data)
    {
        return new DTO(
            titi: MyEnum::from($data['titi']),
            cars: CarsEnum::from($data['cars']),
        );
    }

@davidmohamedfr I still don't understand how your controller looks like for the api route that fails

@davidmohamedfr Please share the code that I can use to reproduce. This includes everything that is needed to have the same issue

Hello. Yes, the issue is present.

Code for reproducing:

Enum:

<?php

enum SupplierGroupEnum: int
{
    case TEST = 1;
}

Controller:

<?php

final class SupplierGroupController extends Controller
{
    public function index(): JsonResource
    {
        $supplierGroups = SupplierGroupEnum::cases();

        return EnumResource::collection($supplierGroups);
    }
}

Additionally, this issue appears after version 0.10.11.

@Tegos I still cannot reproduce the error. I can confirm the type of Enum::cases() is not inferred, but there is no error (exception)

Hm, ok, let's try to check again :)

PHP: 8.1

Composer: "dedoc/scramble": "v0.10.13"

Controller:

<?php

namespace App\Http\Controllers\Api\Static;

use App\Http\Controllers\Controller;
use App\Http\Resources\System\EnumResource;
use App\Support\Enums\Supplier\SupplierDeliveryType;
use Illuminate\Http\Resources\Json\JsonResource;

final class SupplierDeliveryTypeController extends Controller
{
    public function index(): JsonResource
    {
        $supplierDeliveryTypes = SupplierDeliveryType::cases();

        return EnumResource::collection($supplierDeliveryTypes);
    }
}

Enum:

<?php

namespace App\Support\Enums\Supplier;

use App\Support\Enums\Interfaces\LabelableEnum;

enum SupplierDeliveryType: int implements LabelableEnum
{
    case LOCAL = 0;
    case PLANE = 1;
    case SHIP = 2;
    case TRUCK = 3;

    public function label(): string
    {
        return SupplierDeliveryType::getLabel($this);
    }

    public static function getLabel(self $value): string
    {
        return match ($value) {
            SupplierDeliveryType::LOCAL => 'Немає',
            SupplierDeliveryType::PLANE => 'Авіа',
            SupplierDeliveryType::SHIP => 'Море',
            SupplierDeliveryType::TRUCK => 'Авто',
        };
    }
}

Resource:

<?php

namespace App\Http\Resources\System;

use App\Support\Enums\Interfaces\LabelableEnum;
use BackedEnum;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

final class EnumResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        /** @var BackedEnum|LabelableEnum $enumUnit */
        $enumUnit = $this->resource;

        return [
            'id' => $enumUnit->value,
            'slug' => $enumUnit->name,
            'name' => $enumUnit->label(),
        ];
    }
}

In the api I got this error:
image

stacktrace
[2024-07-07 20:50:25] local.ERROR: Error when analyzing route 'GET api/admin/static/supplier-delivery-types' (App\Http\Controllers\Api\Static\SupplierDeliveryTypeController@index): Path cannot be empty – vendor\dedoc\scramble\src\Infer\Reflector\MethodReflector.php on line 40
[2024-07-07 20:50:25] local.ERROR: Path cannot be empty {"exception":"[object] (ValueError(code: 0): Path cannot be empty at \\vendor\\dedoc\\scramble\\src\\Infer\\Reflector\\MethodReflector.php:40)
[stacktrace]
#0 \\vendor\\dedoc\\scramble\\src\\Infer\\Reflector\\MethodReflector.php(40): file_get_contents()
#1 \\vendor\\dedoc\\scramble\\src\\Infer\\Reflector\\MethodReflector.php(60): Dedoc\\Scramble\\Infer\\Reflector\\MethodReflector->getMethodCode()
#2 \\vendor\\dedoc\\scramble\\src\\Infer\\Analyzer\\MethodAnalyzer.php(32): Dedoc\\Scramble\\Infer\\Reflector\\MethodReflector->getAstNode()
#3 \\vendor\\dedoc\\scramble\\src\\Infer\\Definition\\ClassDefinition.php(63): Dedoc\\Scramble\\Infer\\Analyzer\\MethodAnalyzer->analyze()
#4 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\ReferenceTypeResolver.php(264): Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition->getMethodDefinition()
#5 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\ReferenceTypeResolver.php(131): Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver->resolveStaticMethodCallReferenceType()
#6 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\ReferenceTypeResolver.php(149): Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver->Dedoc\\Scramble\\Infer\\Services\\{closure}()
#7 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\ReferenceTypeResolver.php(72): Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver->doResolve()
#8 \\vendor\\dedoc\\scramble\\src\\Support\\Type\\TypeWalker.php(33): Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver->Dedoc\\Scramble\\Infer\\Services\\{closure}()
#9 \\vendor\\dedoc\\scramble\\src\\Support\\Type\\TypeWalker.php(50): Dedoc\\Scramble\\Support\\Type\\TypeWalker->replace()
#10 \\vendor\\dedoc\\scramble\\src\\Support\\Type\\TypeWalker.php(50): Dedoc\\Scramble\\Support\\Type\\TypeWalker->replace()
#11 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\ReferenceTypeResolver.php(72): Dedoc\\Scramble\\Support\\Type\\TypeWalker->replace()
#12 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\RecursionGuard.php(35): Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver->Dedoc\\Scramble\\Infer\\Services\\{closure}()
#13 \\vendor\\dedoc\\scramble\\src\\Infer\\Services\\ReferenceTypeResolver.php(74): Dedoc\\Scramble\\Infer\\Services\\RecursionGuard::run()
#14 \\vendor\\dedoc\\scramble\\src\\Infer\\Definition\\ClassDefinition.php(76): Dedoc\\Scramble\\Infer\\Services\\ReferenceTypeResolver->resolve()
#15 \\vendor\\dedoc\\scramble\\src\\Support\\RouteInfo.php(233): Dedoc\\Scramble\\Infer\\Definition\\ClassDefinition->getMethodDefinition()
#16 \\vendor\\dedoc\\scramble\\src\\Support\\OperationExtensions\\RequestBodyExtension.php(33): Dedoc\\Scramble\\Support\\RouteInfo->getMethodType()
#17 \\vendor\\dedoc\\scramble\\src\\Support\\OperationBuilder.php(28): Dedoc\\Scramble\\Support\\OperationExtensions\\RequestBodyExtension->handle()
#18 \\vendor\\dedoc\\scramble\\src\\Generator.php(142): Dedoc\\Scramble\\Support\\OperationBuilder->build()
#19 \\vendor\\dedoc\\scramble\\src\\Generator.php(44): Dedoc\\Scramble\\Generator->routeToOperation()
#20 [internal function]: Dedoc\\Scramble\\Generator->Dedoc\\Scramble\\{closure}()
#21 \\vendor\\laravel\\framework\\src\\Illuminate\\Collections\\Arr.php(600): array_map()
#22 \\vendor\\laravel\\framework\\src\\Illuminate\\Collections\\Collection.php(778): Illuminate\\Support\\Arr::map()
#23 \\vendor\\dedoc\\scramble\\src\\Generator.php(56): Illuminate\\Support\\Collection->map()
#24 \\vendor\\dedoc\\scramble\\src\\Scramble.php(123): Dedoc\\Scramble\\Generator->__invoke()
#25 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\CallableDispatcher.php(40): Dedoc\\Scramble\\Scramble::Dedoc\\Scramble\\{closure}()
#26 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(237): Illuminate\\Routing\\CallableDispatcher->dispatch()
#27 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Route.php(208): Illuminate\\Routing\\Route->runCallable()
#28 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(806): Illuminate\\Routing\\Route->run()
#29 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(144): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}()
#30 \\app\\Http\\Middleware\\HttpBasicAuth.php(28): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#31 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): App\\Http\\Middleware\\HttpBasicAuth->handle()
#32 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken.php(78): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#33 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\VerifyCsrfToken->handle()
#34 \\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(121): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#35 \\vendor\\laravel\\framework\\src\\Illuminate\\Session\\Middleware\\StartSession.php(64): Illuminate\\Session\\Middleware\\StartSession->handleStatefulRequest()
#36 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Session\\Middleware\\StartSession->handle()
#37 \\vendor\\laravel\\framework\\src\\Illuminate\\Cookie\\Middleware\\EncryptCookies.php(67): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#38 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Cookie\\Middleware\\EncryptCookies->handle()
#39 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#40 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(807): Illuminate\\Pipeline\\Pipeline->then()
#41 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(784): Illuminate\\Routing\\Router->runRouteWithinStack()
#42 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(748): Illuminate\\Routing\\Router->runRoute()
#43 \\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php(737): Illuminate\\Routing\\Router->dispatchToRoute()
#44 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(200): Illuminate\\Routing\\Router->dispatch()
#45 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(144): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}()
#46 \\app\\Http\\Middleware\\ForceJsonResponse.php(13): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#47 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): App\\Http\\Middleware\\ForceJsonResponse->handle()
#48 \\app\\Http\\Middleware\\SentryContext.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#49 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): App\\Http\\Middleware\\SentryContext->handle()
#50 \\vendor\\hryha\\request-logger\\src\\Http\\Middleware\\RequestLogger.php(25): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#51 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Hryha\\RequestLogger\\Http\\Middleware\\RequestLogger->handle()
#52 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#53 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull.php(31): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#54 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull->handle()
#55 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php(21): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#56 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php(40): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle()
#57 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\TrimStrings->handle()
#58 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php(27): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#59 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle()
#60 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php(99): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#61 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance->handle()
#62 \\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\HandleCors.php(49): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#63 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Http\\Middleware\\HandleCors->handle()
#64 \\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\TrustProxies.php(39): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#65 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Illuminate\\Http\\Middleware\\TrustProxies->handle()
#66 \\vendor\\itsgoingd\\clockwork\\Clockwork\\Support\\Laravel\\ClockworkMiddleware.php(24): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#67 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(183): Clockwork\\Support\\Laravel\\ClockworkMiddleware->handle()
#68 \\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php(119): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}()
#69 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(175): Illuminate\\Pipeline\\Pipeline->then()
#70 \\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php(144): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter()
#71 \\public\\index.php(52): Illuminate\\Foundation\\Http\\Kernel->handle()
#72 {main}
"}

@Tegos reproduced on 0.10.13!

Fixed in 0.11.*!

Closing the issue as not reproducible in 0.11.*. Feel free to re-open if it is reproduced in 0.11.*

CC @davidmohamedfr