diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index be5a2b3f1dbe..adcf93843d5b 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -4,6 +4,7 @@ use ArrayAccess; use Closure; +use Exception; use Illuminate\Contracts\Broadcasting\HasBroadcastChannel; use Illuminate\Contracts\Queue\QueueableCollection; use Illuminate\Contracts\Queue\QueueableEntity; @@ -143,6 +144,13 @@ abstract class Model implements Arrayable, ArrayAccess, CanBeEscapedWhenCastToSt */ protected static $dispatcher; + /** + * The models that are currently being booted. + * + * @var array + */ + protected static $booting = []; + /** * The array of booted models. * @@ -286,12 +294,20 @@ public function __construct(array $attributes = []) protected function bootIfNotBooted() { if (! isset(static::$booted[static::class])) { - static::$booted[static::class] = true; + if (isset(static::$booting[static::class])) { + throw new LogicException('The ['.__METHOD__.'] method may not be called on model ['.static::class.'] while it is being booted.'); + } + + static::$booting[static::class] = true; $this->fireModelEvent('booting', false); static::booting(); static::boot(); + + static::$booted[static::class] = true; + unset(static::$booting[static::class]); + static::booted(); static::$bootedCallbacks[static::class] ??= []; diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index d7f6f1538b72..54c2673b3b6f 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -3330,6 +3330,21 @@ public function testUseFactoryAttribute() $this->assertEquals(EloquentModelWithUseFactoryAttribute::class, $factory->modelName()); $this->assertEquals('test name', $instance->name); // Small smoke test to ensure the factory is working } + + public function testNestedModelBootingIsDisallowed() + { + $this->expectExceptionMessageMatches('/The \[(.+)] method may not be called on model \[(.+)\] while it is being booted\./'); + + $model = new class extends Model + { + protected static function boot() + { + parent::boot(); + + $tableName = (new static())->getTable(); + } + }; + } } class EloquentTestObserverStub