Skip to content

Commit d51bc1e

Browse files
committed
[11.x] Delay calling a "default" callback until the last possible second
1 parent dc3dc5d commit d51bc1e

File tree

1 file changed

+42
-15
lines changed

1 file changed

+42
-15
lines changed

src/Illuminate/Database/Eloquent/Concerns/PreventsCircularRecursion.php

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ trait PreventsCircularRecursion
1111
/**
1212
* The cache of objects processed to prevent infinite recursion.
1313
*
14-
* @var \WeakMap<static, array<string, mixed>>
14+
* @var WeakMap<static, array<string, mixed>>
1515
*/
1616
protected static $recursionCache;
1717

@@ -28,31 +28,40 @@ protected function withoutRecursion($callback, $default = null)
2828

2929
$onceable = Onceable::tryFromTrace($trace, $callback);
3030

31-
$object = $onceable->object ?? $this;
32-
33-
$stack = static::getRecursiveCallStack($object);
31+
$stack = static::getRecursiveCallStack($this);
3432

3533
if (array_key_exists($onceable->hash, $stack)) {
36-
return $stack[$onceable->hash];
34+
return is_callable($stack[$onceable->hash])
35+
? static::setRecursiveCallValue($this, $onceable->hash, call_user_func($stack[$onceable->hash]))
36+
: $stack[$onceable->hash];
3737
}
3838

3939
try {
40-
$stack[$onceable->hash] = is_callable($default) ? call_user_func($default) : $default;
41-
42-
static::getRecursionCache()->offsetSet($object, $stack);
40+
static::setRecursiveCallValue($this, $onceable->hash, $default);
4341

4442
return call_user_func($onceable->callable);
4543
} finally {
46-
if ($stack = Arr::except($this->getRecursiveCallStack($object), $onceable->hash)) {
47-
static::getRecursionCache()->offsetSet($object, $stack);
48-
} elseif (static::getRecursionCache()->offsetExists($object)) {
49-
static::getRecursionCache()->offsetUnset($object);
50-
}
44+
static::clearRecursiveCallValue($this, $onceable->hash);
5145
}
5246
}
5347

5448
/**
55-
* Get the current stack of methods being called recursively.
49+
* Remove an entry from the recursion cache for an object.
50+
*
51+
* @param object $object
52+
* @param string $hash
53+
*/
54+
protected static function clearRecursiveCallValue($object, string $hash)
55+
{
56+
if ($stack = Arr::except(static::getRecursiveCallStack($object), $hash)) {
57+
static::getRecursionCache()->offsetSet($object, $stack);
58+
} elseif (static::getRecursionCache()->offsetExists($object)) {
59+
static::getRecursionCache()->offsetUnset($object);
60+
}
61+
}
62+
63+
/**
64+
* Get the stack of methods being called recursively for the current object.
5665
*
5766
* @param object $object
5867
* @return array
@@ -67,10 +76,28 @@ protected static function getRecursiveCallStack($object): array
6776
/**
6877
* Get the current recursion cache being used by the model.
6978
*
70-
* @return \WeakMap
79+
* @return WeakMap
7180
*/
7281
protected static function getRecursionCache()
7382
{
7483
return static::$recursionCache ??= new WeakMap();
7584
}
85+
86+
/**
87+
* Set a value in the recursion cache for the given object and method.
88+
*
89+
* @param object $object
90+
* @param string $hash
91+
* @param mixed $value
92+
* @return mixed
93+
*/
94+
protected static function setRecursiveCallValue($object, string $hash, $value)
95+
{
96+
static::getRecursionCache()->offsetSet(
97+
$object,
98+
tap(static::getRecursiveCallStack($object), fn (&$stack) => $stack[$hash] = $value),
99+
);
100+
101+
return static::getRecursiveCallStack(($object))[$hash];
102+
}
76103
}

0 commit comments

Comments
 (0)