Skip to content

Model toJSON fails when there has been a previous JSON encode or decode error #46368

@manticorp

Description

@manticorp
  • Laravel Version: 10.2.0
  • PHP Version: 8.1.11
  • Database Driver & Version: N/A

Description:

When json encoding a model using toJson, if there was a previous json error, the model fails because of how it detects json errors.

Here is the toJson code in Illuminate\Database\Eloquent\Model.php:

    /**
     * Convert the model instance to JSON.
     *
     * @param  int  $options
     * @return string
     *
     * @throws \Illuminate\Database\Eloquent\JsonEncodingException
     */
    public function toJson($options = 0)
    {
        $json = json_encode($this->jsonSerialize(), $options);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw JsonEncodingException::forModel($this, json_last_error_msg());
        }

        return $json;
    }

The problem occurs if, somewhere else in the software, an error was thrown in a json_encode or json_decode call, even if called with the @ operator.

I would suggest changing to something like this:

    public function toJson($options = 0)
    {
        try {
            $json = json_encode($this->jsonSerialize(), $options & JSON_THROW_ON_ERROR);
        } catch (\JsonException $e) {
            throw JsonEncodingException::forModel($this, $e->getMessage());
        }

        return $json;
    }

or this:

    public function toJson($options = 0)
    {
        if (json_last_error() !== JSON_ERROR_NONE) {
            json_encode("");
        }

        $json = json_encode($this->jsonSerialize(), $options);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw JsonEncodingException::forModel($this, json_last_error_msg());
        }

        return $json;
    }

Steps To Reproduce:

Do the following:

composer create-project laravel/laravel laravel-json-bug
cd laravel-json-bug
php artisan make:model Test --all

Create a test route in web.php:

Route::get('/test', [TestController::class, 'index']);

Edit TestController.php:

<?php

namespace App\Http\Controllers;

use App\Models\Test;

class TestController extends Controller
{
    public function index()
    {
        $testModel = new Test();
        $testModel->foo = 'bar';

        @json_decode('[this is invalid json]');

        return view('test', ['test' => $testModel]);
    }
}

Now create a view test.blade.php

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>
    </head>
    <body>
        <script>console.log(@js($test));</script>
    </body>
</html>

You will be greeted with the following error:

Error encoding model [App\Models\Test] with ID [] to JSON: Syntax error

image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions