Detector: mutable-static-state
Report (why the flagged code is CORRECT and the detector is wrong):
ReferenceTracker::$composing is a dynamic scope, not a global: Compose pushes the tracker for exactly the duration of one compose() call and pops it in finally. Compose bodies are static by the project's architecture and build children with new and ::of(), so the writes that pair with the parent's recorded reads (535 StateProxy writes, Primitive passThrough) land on fresh components that hold no collaborator and never pass through the parent's Reactive recorder. Pairing matches reads and writes in the order they happen, which a walk after compose cannot recover.
Cleanest design the reporter can conceive:
A try/finally-scoped stack pushed and popped by the one pipe that runs compose, read only by the write path of components built inside it, which is the flagged code.
⚖️ Maintainer litmus: a valid detector-report needs the flagged code to ALREADY BE the
cleanest design. If the design above differs from the flagged code at all, THAT design is
the owed fix — close this report; the fix is still owed.
Where: src/View/Core/ReferenceTracker.php:60-104
Code (src/View/Core/ReferenceTracker.php:60-104):
57 private readonly Component | null $component = null,
58 ) {}
59
→ 60 /**
→ 61 * @template TReturn
→ 62 *
→ 63 * @param Closure(): TReturn $compose
→ 64 * @return TReturn
→ 65 */
→ 66 public static function composing(self $passed, Closure $compose): mixed
→ 67 {
→ 68 self::$composing[] = $passed;
→ 69
→ 70 try
→ 71 {
→ 72 return $compose();
→ 73 }
→ 74 finally
→ 75 {
→ 76 array_pop(self::$composing);
→ 77
→ 78 $passed->drain();
→ 79 }
→ 80 }
→ 81
→ 82 /**
→ 83 * @param array<array-key, mixed> $values
→ 84 */
→ 85 public static function set(Component $component, array $values): void
→ 86 {
→ 87 $composing = end(self::$composing);
→ 88
→ 89 if ($composing === false)
→ 90 {
→ 91 return;
→ 92 }
→ 93
→ 94 $composing->pair($component, $values);
→ 95 }
→ 96
→ 97 public static function authored(Component $component, string $property, mixed $value): void
→ 98 {
→ 99 if (! Referable::is($value))
→ 100 {
→ 101 return;
→ 102 }
→ 103
→ 104 $write = new AuthoredWrite($component, $property, $value);
105 $composing = end(self::$composing);
106
107 $component->wrote($write);
Where: src/View/StateProxy.php:25-32
Code (src/View/StateProxy.php:25-32):
22 */
23 public function __construct(private readonly Component $component) {}
24
→ 25 public function __set(string $name, mixed $value): void
→ 26 {
→ 27 $value = self::cased($this->component::stateClass(), $name, $value);
→ 28
→ 29 $this->component->setState(static fn (State $state) => $state->{$name} = $value);
→ 30
→ 31 ReferenceTracker::authored($this->component, $name, $value);
→ 32 }
33
34 /**
35 * @param class-string $state
Where: src/View/Compiling/Pipes/Compose.php:39-45
Code (src/View/Compiling/Pipes/Compose.php:39-45):
36 self::exposeReferenced($component, $opened->state);
37 }
38
→ 39 if ($opened->composes && $component instanceof Composition)
→ 40 {
→ 41 $component->replaceChildren(ReferenceTracker::composing(
→ 42 $opened->passed,
→ 43 fn () => $this->composed($component, $opened->node, $opened->state, $opened->passed),
→ 44 ));
→ 45 }
46
47 return new ComposedChildren(
48 component: $component,
Filed via commandments report from a consumer project.
Detector:
mutable-static-stateReport (why the flagged code is CORRECT and the detector is wrong):
ReferenceTracker::$composing is a dynamic scope, not a global: Compose pushes the tracker for exactly the duration of one compose() call and pops it in finally. Compose bodies are static by the project's architecture and build children with new and ::of(), so the writes that pair with the parent's recorded reads (535 StateProxy writes, Primitive passThrough) land on fresh components that hold no collaborator and never pass through the parent's Reactive recorder. Pairing matches reads and writes in the order they happen, which a walk after compose cannot recover.
Cleanest design the reporter can conceive:
A try/finally-scoped stack pushed and popped by the one pipe that runs compose, read only by the write path of components built inside it, which is the flagged code.
Where:
src/View/Core/ReferenceTracker.php:60-104Code (
src/View/Core/ReferenceTracker.php:60-104):Where:
src/View/StateProxy.php:25-32Code (
src/View/StateProxy.php:25-32):Where:
src/View/Compiling/Pipes/Compose.php:39-45Code (
src/View/Compiling/Pipes/Compose.php:39-45):Filed via
commandments reportfrom a consumer project.