Skip to content

chore: raise PHPStan to level 8 - #133

Merged
dakorpar merged 1 commit into
masterfrom
chore/phpstan-level-up
Aug 14, 2026
Merged

chore: raise PHPStan to level 8#133
dakorpar merged 1 commit into
masterfrom
chore/phpstan-level-up

Conversation

@dakorpar

@dakorpar dakorpar commented Aug 14, 2026

Copy link
Copy Markdown
Member

Raises the PHPStan level in make phpstan from 7 to 8. Level 8 checks method calls and property access on nullable types, which surfaced three real holes rather than just noise.

What level 8 found

BootstrapRenderer::configElem() could throw away the element it was configuring.
When a config entry had a CONTAINER key whose own config produced no element, $el = $container overwrote the element with null and the caller silently lost it. It now only swaps in the container when there is one. That also makes the method total — given an element, it always returns one — which is expressed with a conditional return type:

@return ($el is null ? Html|null : Html)

With that, getElem() is declared : Html instead of : ?Html, and its ~20 call sites across renderBody(), renderPair(), renderFeedback() and renderDescription() stop looking nullable. Two configElem(…, null) calls that always needed a real element were switched to getElem(), and a dead !empty($nonLabel) guard on an always-truthy Html object was dropped.

BootstrapRow::$container was typed Container but could hold null.
setParent() takes ?IContainer and assigned straight to the property, so a detached row (or one attached to a non-Nette\Forms\Container) violated its own declared type. Now:

  • the property is Container|null;
  • getParent() returns ?IContainer, matching IComponent and reality;
  • setParent() throws InvalidArgumentException for a parent that is not a Nette\Forms\Container, instead of storing something the rest of the class cannot use;
  • a new getContainer(): Container gives render(), addComponent() and BootstrapCell::render() the attached container, or an InvalidStateException with a clear message instead of a null-call fatal.

BootstrapRow::$ownedNames was declared string[] but collected ?string.
It now records the name the component actually ends up with after the add.

API notes

BootstrapRow::getParent() widened from IContainer to ?IContainer. Callers that need the container should use the new getContainer().

Verification

  • make phpstan (now -l 8) — no errors
  • make cs — clean
  • make tests — 161 tests, 245 assertions, all passing (3 new tests cover the detached-row and wrong-parent paths)

No fixture changes: rendered HTML is unchanged.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.48%. Comparing base (87fa3b3) to head (1a19a7f).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #133      +/-   ##
============================================
+ Coverage     97.05%   97.48%   +0.43%     
- Complexity      309      313       +4     
============================================
  Files            25       25              
  Lines           986      996      +10     
============================================
+ Hits            957      971      +14     
+ Misses           29       25       -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Level 8 checks calls on nullable types, which surfaced three real holes:

- `configElem()` dropped the element it was configuring whenever a
  `CONTAINER` config produced no element of its own. It now keeps the
  element in that case, which also makes it total: given an element, it
  always returns one. That is expressed as a conditional return type, so
  `getElem()` can be declared to return `Html` rather than `Html|null`
  and its ~20 call sites no longer look nullable.
- `BootstrapRow::$container` was typed `Container` but `setParent()`
  accepts `?IContainer`, so a detached row held null behind a
  non-nullable type. The property is now nullable, `getParent()` reports
  that honestly, `setParent()` rejects a parent that is not a
  `Nette\Forms\Container`, and the new `getContainer()` gives rendering
  code the attached container or a clear exception.
- `BootstrapRow::$ownedNames` was declared `string[]` but collected the
  nullable `$name` argument. It now records the name the component
  actually ends up with.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dakorpar
dakorpar force-pushed the chore/phpstan-level-up branch from c81720b to 1a19a7f Compare August 14, 2026 19:44
@dakorpar
dakorpar changed the base branch from fix/63-formprint-blueprint to master August 14, 2026 19:44
@dakorpar
dakorpar merged commit af238e5 into master Aug 14, 2026
9 checks passed
@dakorpar
dakorpar deleted the chore/phpstan-level-up branch August 14, 2026 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant