Skip to content

[detector-report] MutableStaticStateDetector #580

Description

@jessegall

Detector: MutableStaticStateDetector

Report (why the flagged code is CORRECT and the detector is wrong):
The theme map cannot be held on an instance. A stylesheet set is a 'const array' read off a class-string, so Stylesheet::of() is reached from a component's STATIC stylesheet() accessor, which has no receiver and no object for a collaborator to be injected into. use() sets the map exactly once at boot and throws ThemeAlreadyChosen on a second, different one, so execution order is not load-bearing and a second write is a loud named failure.

Cleanest design the reporter can conceive:
A write-once static holding the boot-time theme map, throwing a named exception on a second differing write -- which is what the code is. Moving the map onto an instance requires Stylesheet::of() to be reached from a receiver, and it is reached from a static stylesheet() accessor that exists so a set can stay a 'const array'. Resolving from the container at read time puts the framework inside View\Stylesheets, which imports nothing from Illuminate, and puts a service locator on every style read.

⚖️ 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/Stylesheets/Stylesheet.php:24

Code (src/View/Stylesheets/Stylesheet.php:24):

  21              throw ThemeAlreadyChosen::for(array_keys($roles));
  22          }
  2324          self::$active = $roles;
  25      }
  26  
  27      /**
  28       * @template T of PrimitiveRole|CompositionRole
  29       *
  30       * @param  class-string<T>  $role
  31       * @return class-string<T>
  32       */
  33      public static function of(string $role): string
  34      {
  35          return self::$active[$role] ?? $role;
  36      }
  37  }

Where: src/View/Primitives/Text.php:22

Code (src/View/Primitives/Text.php:22):

  19       */
  20      public static function stylesheet(): string
  21      {
→ 22          return Stylesheet::of(TextStylesheet::class);
  23      }
  24  
  25      private function __construct(string | StateRef | null $text = null)
  26      {
  27          $this->state->text = $text;
  28      }
  29  
  30      public static function reading(StateRef $ref): self
  31      {
  32          return new self($ref)->passThrough(text: $ref);
  33      }
  34  
  35      public static function of(string | int | null $text = null): self
  36      {
  37          return new self($text === null ? null : (string) $text)->passThrough(text: $text);
  38      }
  39  
  40      public function headingLevel(int $level): static
  41      {
  42          $this->state->headingLevel = $level;
  43  
  44          return $this;
  45      }
  46  }

Where: src/Laravel/ViewServiceProvider.php:97

Code (src/Laravel/ViewServiceProvider.php:97):

   94  
   95      public function register(): void
   96      {
→  97          Stylesheet::use($this->app['config']->get('workflows.theme', []));
   98  
   99          foreach (self::DRIVERS as $contract => $driver)
  100          {
  101              $this->app->scoped($driver);
  102              $this->app->bind($contract, $driver);
  103          }
  104  
  105          $this->app->singleton(CompiledScenes::class, static fn ($app) => new CompiledScenes($app->basePath()));
  106          $this->app->scoped(Shapes::class, static fn ($app) => new Shapes($app->basePath()));
  107          $this->app->singleton(RenderedScenes::class, static fn ($app) => new RenderedScenes($app->basePath()));
  108  
  109          $this->app->scoped(ClientState::class, static fn ($app) => $app->make(Navigation::class)->hasDestination()
  110              ? ClientState::none()
  111              : ClientState::from($app->make(Request::class)));
  112  
  113          $this->app->scoped(Pipes::class);
  114          $this->app->scoped(PipeTimings::class);
  115  
  116          $this->app->scoped(SceneResponse::class, static function ($app) {
  117              $request = $app->make(Request::class);
  118  
  119              return $request->wantsJson() || $request->boolean('json')
  120                  ? $app->make(AsJson::class)
  121                  : $app->make(AsPage::class);

Filed via commandments report from a consumer project.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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