Skip to content

__PHP_Incomplete_Class error when using cache in Laravel 13 #240

Description

@SameOldNick

When using this package with Laravel 13, the parse() method in hisorange\BrowserDetect\Parser can return __PHP_Incomplete_Class instead of a ResultInterface object when the result is retrieved from cache. This causes a fatal type error.

Steps to Reproduce

The following code demonstrates the issue by parsing a user agent string twice (first call creates cache, second call retrieves from cache):

use hisorange\BrowserDetect\Parser;
use Illuminate\Support\Facades\Cache;

$userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';

$parser = new Parser(
    Cache::store('file'),
    request(),
    config('browser-detect')
);

// First call - parses and stores in cache
$result1 = $parser->parse($userAgent);
echo 'Browser Name: ' . $result1->browserName() . "\n";

// Second call - retrieves from cache and fails
$result2 = $parser->parse($userAgent);
echo 'Browser Name: ' . $result2->browserName() . "\n";

Expected Output

Browser Name: Chrome 120
Browser Name: Chrome 120

Actual Output

hisorange\BrowserDetect\Parser::parse(): Return value must be of type hisorange\BrowserDetect\Contracts\ResultInterface, __PHP_Incomplete_Class returned

at vendor/hisorange/browser-detect/src/Parser.php:172
  168▕
  169▕         $this->runtime[$key] = $result;
  170▕     }
  171▕
➜ 172▕     return $this->runtime[$key];
  173▕ }
  174▕
  175▕ /**
  176▕  * Create a unique cache key for the user agent.

Root Cause

Laravel 13 introduced a security feature that requires a whitelist of classes that can be unserialized from cache. This prevents arbitrary class deserialization vulnerabilities. See the Laravel 13 upgrade guide for details.

When hisorange\BrowserDetect\Result is not in the whitelist, Laravel's cache system cannot reconstruct the object during unserialization, resulting in __PHP_Incomplete_Class.

Solution

Add the Result class to the serializable_classes array in your config/cache.php file:

return [
    // ... other cache configuration

    /*
    |--------------------------------------------------------------------------
    | Serializable Classes
    |--------------------------------------------------------------------------
    |
    | Here you may specify which classes may be unserialized. This prevents
    | PHP from unserializing arbitrary objects. You may use "*" to allow all.
    |
    */

    'serializable_classes' => [
        \hisorange\BrowserDetect\Result::class,
    ],
];

Alternatively, if you trust all cached data, you can use:

'serializable_classes' => true, // Not recommended!

Note: After updating the configuration, clear your cache:

php artisan cache:clear

Documentation Suggestion

It would be helpful to add a note about Laravel 13 compatibility in the package documentation, specifically mentioning this configuration requirement for users who have caching enabled.

Environment

  • Laravel: 13.x
  • PHP: 8.3+
  • hisorange/browser-detect: 5.0.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions