scramble icon indicating copy to clipboard operation
scramble copied to clipboard

[Bug]: response()->json() with dynamic status returns a string for Response

Open lcyoong opened this issue 8 months ago • 3 comments

What happened?

Tried two scenarios for Response

Scenario 1 return response()->json($data, $status); returns the structure $data (expected behaviour)

Image

Scenario 2 return response()->json($data, $status); simply returns a string

Image

Is there a workaround to this?

How to reproduce the bug

Passing a dynamic status to response()->json() i.e. return response()->json($data, $status);

Package Version

0.12.17

PHP Version

8.4.4

Laravel Version

12.0.1

Which operating systems does with happen with?

No response

Notes

No response

lcyoong avatar Apr 15 '25 13:04 lcyoong

@lcyoong Can you please show the pieces of your real code so I can reproduce? Anything that can be reproduced but in the real context

romalytvynenko avatar Apr 15 '25 16:04 romalytvynenko

Hello @romalytvynenko Here you go. It's a simplified version of my code but it produces the same result

class GroupController extends BaseController
{
    private $status = 200;
    /**
     * List of groups
     */
    public function index(GetGroups $getGroups)
    {
        return response()->json([
            'foo' => 'bar'
        ], $this->status);
    }
}

This will produce the result

Image

When I pass the explicit value of status

class GroupController extends BaseController
{
    private $status = 200;
    /**
     * List of groups
     */
    public function index(GetGroups $getGroups)
    {
        return response()->json([
            'foo' => 'bar'
        ], 200);
    }
}

It will show the response structure correctly.

Image

lcyoong avatar Apr 16 '25 07:04 lcyoong

@lcyoong

class GroupController extends BaseController
{
    private $status = 200;
    /**
     * List of groups
     */
    public function index(GetGroups $getGroups)
    {
        return response()->json([
            'foo' => 'bar'
        ], $this->status);
    }
}

This specific example is not working due to the type of status being inferred as int (and not as 200).

You see, theoretically the value of $status property of this class may be changed from the outside. And there is no way in the index method to know about this. So Scramble cannot be sure that the type of $this->status is 200.

Next, when the type of status you pass to response()->json() is not a literal integer value, the response is not getting documented and you get the observed behavior. The simplest fix for your case would be to use const:

class GroupController extends BaseController
{
    const STATUS = 200;
    /**
     * List of groups
     */
    public function index(GetGroups $getGroups)
    {
        return response()->json([
            'foo' => 'bar'
        ], static::STATUS);
    }
}

Sure this may not "fix" the issue at all if you rely on dynamic $this->status, but that is the entire point: properties types are tricky and cannot be relied on as we cannot be sure about its type.

romalytvynenko avatar Apr 16 '25 09:04 romalytvynenko

@lcyoong I think I found the work-around. In my case I wanted to use single function to create a user if not exists and send OTP for login.

When new user is created I wanted to return 201 else 200 and I needed this to reflect in my doc.

this is my function

public function generate(GenerateOtpRequest $request) : JsonResponse {
        $phone_number = $request->phone_number;
        $user = User::wherePhoneNumber($request->phone_number)->first();
        $new_user = false;

        if(!$user) {
            $new_user = true;
            $user = $this->userService->createFromPhone($phone_number);
        }

        $success = $this->otpService->generateAndSend($user);

        if($new_user) {
            /**
             * @status 201
             * @body array{success: bool}
            */
            return response()->json(
                [
                    'success' => $success
                ],
                Response::HTTP_CREATED
            );
        }

        /**
         * @status 200
         * @body array{success: bool}
        */
        return response()->json(
            [
                'success' => $success
            ],
            Response::HTTP_OK
        );

    }

Output:

Image Image

shubhamdhaboya avatar Nov 26 '25 13:11 shubhamdhaboya