Skip to content

[detector-report] UselessPropertyHookDetector #521

Description

@jessegall

Detector: UselessPropertyHookDetector

Report (why the flagged code is CORRECT and the detector is wrong):
The body reads static::DRIVER, which is late static binding: SecretWizard, AgentWizard, SchemaWizard and IntegrationWizard each redeclare DRIVER, so the hook answers a different class per subclass. The detector looks for $this and parent:: and treats static:: as a constant, so it reads a polymorphic body as a fixed one. Laziness is the second reason it cannot be a stored property: a Wizard is built from its own defaults while the template is compiled, where the container has no request-scoped driver bound and Queries::refuse() is in force, so resolving in a constructor runs the resolution at compile time. And a Wizard's constructor must stay argument-free for the same reason, which rules out passing the driver in.

Cleanest design the reporter can conceive:
A wizard names its driver class as a constant and reads the instance lazily through a get hook, exactly as written. The constant is what makes each subclass a wizard of its own; the hook is what keeps the resolution out of compile time.

⚖️ 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/Ui/Scenes/Wizards/Wizard.php:53

Code (src/Ui/Scenes/Wizards/Wizard.php:53):

  50       * @var TDriver
  51       */
  52      protected WizardDriver $driver {
→ 53          get => App::make(static::DRIVER);
  54      }
  55  
  56      protected function setup(): void
  57      {
  58          $this->driver->observe(fn () => $this->signal(new ScreenChanged));
  59      }
  60  
  61      public function cacheState(): string
  62      {
  63          return $this->driver->currentScreen();
  64      }
  65  
  66      # ----------[ Scripts ]----------
  67  
  68      #[ClientSide]
  69      public function shut(Element $element, Document $page): void
  70      {
  71          $page->onKeys(Keys::of(Key::Escape), fn (Element $node) => $node->state->open = false);
  72      }
  73  
  74      protected function compose(): Renderable | array | null
  75      {
  76          return Modal::of(
  77              Frame::dialog(Region::of(WizardBody::of(App::make($this->driver->currentScreen()))))

Where: src/Ui/Scenes/Wizards/Secret/SecretWizard.php:13

Code (src/Ui/Scenes/Wizards/Secret/SecretWizard.php:13):

  10   * @extends Wizard<SecretDriver>
  11   */
  12  #[Scoped]
→ 13  final class SecretWizard extends Wizard
  14  {
  15      protected const string DRIVER = SecretDriver::class;
  16  }

Filed via commandments report from a consumer project.

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