Laravel Version
12.56.0
PHP Version
8.5.2
Database Driver & Version
No response
Description
Notice
While we have only tested this with Laravel 12, this is most likely also an issue with Laravel 13. The framework code has not been changed in-between afaik.
Issue
With PHP 8.5 there have been fixes concerning the order on how trait methods are loaded.
See php/php-src#15753 and php/php-src#16198 for reference.
This results in methods appearing through traits outputting in a different order than before when using the reflection api. Before, parent trait methods appeared first with children following. Now it is the other way around.
This causes issues with the bootTraits method in the Illuminate/Database/Eloquent/Model class as it cycles through (new ReflectionClass($class))->getMethods().
As an example, we use spatie/laravel-model-states. This package has a trait HasStates, which executes $this->getCasts() internally on initializeHasStates(). The casts come from the Illuminate/Database/Eloquent/Concerns/HasAttributes and its initializeHasAttributes().
class MyModel extends Model {
use HasStates;
}
In PHP 8.4, the (new ReflectionClass($class))->getMethods() first returned the method from HasAttributes, then HasStates. -> they were executed in the correct order
In PHP 8.5, it returns children first: first HasStates, then HasAttributes -> This will break because HasStates relies on HasAttributes to be executed first.
Possible Fix
The order in the output of class_uses_recursive has not changed.
This is PHP 8.5:
It should be possible to instead loop over this instead of the reflection methods to keep the same order as before.
// \Illuminate\Database\Eloquent\Model::bootTraits
$ref = (new ReflectionClass($class));
foreach (class_uses_recursive($class) as $trait) {
$conventionalBootMethod = 'boot'.class_basename($trait);
$conventionalInitMethod = 'initialize'.class_basename($trait);
if ($ref->hasMethod($conventionalBootMethod) &&
($method = $ref->getMethod($conventionalBootMethod)) &&
! in_array($method->getName(), $booted) &&
$method->isStatic() &&
$method->getAttributes(Boot::class) !== []) {
$method->invoke(null);
$booted[] = $method->getName();
}
if ($ref->hasMethod($conventionalInitMethod) &&
($method = $ref->getMethod($conventionalInitMethod)) ||
$method->getAttributes(Initialize::class) !== []) {
static::$traitInitializers[$class][] = $method->getName();
}
}
Steps To Reproduce
It is basic PHP functionality.
trait T1
{
public function t1_1() {}
public function t1_2() {}
}
trait T2
{
public function t2() {}
}
class A
{
use T1;
}
class B extends A
{
use T2;
}
var_dump((new ReflectionClass(new B))->getMethods());
You can run this code in PHP 8.4:
And in PHP 8.5:

Laravel Version
12.56.0
PHP Version
8.5.2
Database Driver & Version
No response
Description
Notice
While we have only tested this with Laravel 12, this is most likely also an issue with Laravel 13. The framework code has not been changed in-between afaik.
Issue
With PHP 8.5 there have been fixes concerning the order on how trait methods are loaded.
See php/php-src#15753 and php/php-src#16198 for reference.
This results in methods appearing through traits outputting in a different order than before when using the reflection api. Before, parent trait methods appeared first with children following. Now it is the other way around.
This causes issues with the
bootTraitsmethod in theIlluminate/Database/Eloquent/Modelclass as it cycles through(new ReflectionClass($class))->getMethods().As an example, we use
spatie/laravel-model-states. This package has a traitHasStates, which executes$this->getCasts()internally oninitializeHasStates(). The casts come from theIlluminate/Database/Eloquent/Concerns/HasAttributesand itsinitializeHasAttributes().In PHP 8.4, the
(new ReflectionClass($class))->getMethods()first returned the method fromHasAttributes, thenHasStates. -> they were executed in the correct orderIn PHP 8.5, it returns children first: first
HasStates, thenHasAttributes-> This will break because HasStates relies on HasAttributes to be executed first.Possible Fix
The order in the output of
class_uses_recursivehas not changed.This is PHP 8.5:
It should be possible to instead loop over this instead of the reflection methods to keep the same order as before.
Steps To Reproduce
It is basic PHP functionality.
You can run this code in PHP 8.4:
And in PHP 8.5: