Skip to content

fix: resolve pre-existing phpstan and phpcs CI failures - #28

Closed
Decipher wants to merge 5 commits into
4.1.xfrom
fix/phpstan-phpcs-cleanup
Closed

fix: resolve pre-existing phpstan and phpcs CI failures#28
Decipher wants to merge 5 commits into
4.1.xfrom
fix/phpstan-phpcs-cleanup

Conversation

@Decipher

@Decipher Decipher commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Problem

The 4.1.x branch has pre-existing phpstan and phpcs failures that block CI on all PRs.

Changes

  • Php.php: Correct phpcs:ignore sniff name (DrupalPractice.CodeAnalysis variant)
  • CustomFormatters deriver: Cast base_plugin_definition to array before offset access
  • Test files: Replace redundant assertNotFalse/assert with proper \assert() type narrowing on drupalCreateUser() return
  • CustomFormattersTestBase: Fix property type to UserInterface, remove dead !== FALSE guard
  • phpstan.neon: Targeted ignores for deprecated node_access_rebuild() and _contextual_links_to_id() (no replacement available in Drupal 11)
  • phpstan.neon: Ignore entityStorageDirectInjection in FormatterListBuilder (required by ConfigEntityListBuilder parent constructor)

Why separate PR?

These issues are unrelated to any feature work. Splitting them out unblocks CI for all future PRs.

Summary by CodeRabbit

  • Bug Fixes
    • Improved custom formatter derivative registration to handle differing base definitions more reliably.
  • Chores
    • Extended static-analysis ignore rules for known-safe patterns and added additional code-quality suppression for formatter view logic.
  • Tests
    • Strengthened functional and kernel test setup by standardizing admin/account typing and login assertions, improving consistency across test suites.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Decipher, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 39ceb3fc-a118-4e82-9b1e-d3617615d336

📥 Commits

Reviewing files that changed from the base of the PR and between a950d46 and 707acc0.

📒 Files selected for processing (5)
  • tests/src/Functional/CustomFormattersTestBase.php
  • tests/src/Functional/FormatterSettingFormTest.php
  • tests/src/Functional/FormatterSettingUiTest.php
  • tests/src/Kernel/FormatterPreviewFormTest.php
  • tests/src/Kernel/FormatterSettingAccessControlTest.php
📝 Walkthrough

Walkthrough

The pull request extends static-analysis suppressions, normalizes custom formatter derivative definitions to arrays, sets Drupal 11.3 for lint assembly, and updates functional and kernel test account handling.

Changes

Analysis and test compatibility

Layer / File(s) Summary
Static-analysis suppressions
phpstan.neon, src/Plugin/CustomFormatters/FormatterType/Php.php, .github/workflows/test.yml
Additional PHPStan and PHPCS suppressions cover existing Drupal and evaluated-code patterns, while lint assembly specifies Drupal 11.3.
Derivative base definition normalization
src/Plugin/Derivative/CustomFormatters.php
Enabled formatter derivatives now receive an array-cast base plugin definition.
Test setup and assertion typing
tests/src/Functional/*, tests/src/Kernel/*
Functional and kernel tests update account annotations, user setup, login behavior, and fully qualified assertions.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing existing PHPStan and PHPCS CI failures.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/phpstan-phpcs-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Decipher
Decipher force-pushed the fix/phpstan-phpcs-cleanup branch 2 times, most recently from 21bb160 to 9cff333 Compare July 28, 2026 07:23
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.55%. Comparing base (2655e80) to head (dcea02e).
⚠️ Report is 6 commits behind head on 4.1.x.

Additional details and impacted files
@@            Coverage Diff             @@
##            4.1.x      #28      +/-   ##
==========================================
+ Coverage   78.54%   78.55%   +0.01%     
==========================================
  Files          25       25              
  Lines        1729     1730       +1     
  Branches       64       64              
==========================================
+ Hits         1358     1359       +1     
  Misses        368      368              
  Partials        3        3              

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Decipher
Decipher force-pushed the fix/phpstan-phpcs-cleanup branch 2 times, most recently from a3c664a to fefad1d Compare July 28, 2026 20:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/src/Functional/FormatterSettingUiTest.php`:
- Line 39: Remove the redundant AccountInterface instanceof assertions in
tests/src/Functional/FormatterSettingUiTest.php lines 39-39 and
tests/src/Kernel/FormatterSettingAccessControlTest.php lines 63-68. Update the
affected test setup methods to use the users returned by drupalCreateUser() and
UserCreationTrait::createUser() directly, preserving all subsequent behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ae08a69-66f0-4b41-8bce-d2612d32280b

📥 Commits

Reviewing files that changed from the base of the PR and between 56cea21 and a3c664a.

📒 Files selected for processing (9)
  • .github/workflows/test.yml
  • phpstan.neon
  • src/Plugin/CustomFormatters/FormatterType/Php.php
  • src/Plugin/Derivative/CustomFormatters.php
  • tests/src/Functional/CustomFormattersTestBase.php
  • tests/src/Functional/FormatterSettingFormTest.php
  • tests/src/Functional/FormatterSettingUiTest.php
  • tests/src/Kernel/FormatterPreviewFormTest.php
  • tests/src/Kernel/FormatterSettingAccessControlTest.php
🚧 Files skipped from review as they are similar to previous changes (3)
  • phpstan.neon
  • src/Plugin/Derivative/CustomFormatters.php
  • src/Plugin/CustomFormatters/FormatterType/Php.php

'administer node display',
]);
$this->assertNotFalse($admin);
\assert($admin instanceof AccountInterface);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
git ls-files | rg '(^|/)tests/src/(Functional|Kernel)/FormatterSetting(Ui|AccessControl)Test\.php$' || true

echo "== file outlines/sections =="
for f in $(git ls-files | rg '(^|/)tests/src/(Functional|Kernel)/FormatterSetting(Ui|AccessControl)Test\.php$'); do
  echo "--- $f ($(wc -l < "$f") lines) ---"
  sed -n '1,120p' "$f" | cat -n
done

echo "== locate AccountInterface and class declarations =="
rg -n "use .*\bAccountInterface|class .*extends|function createUser|drupalLogin|assert\\(" tests/src tests -S || true

echo "== PHP/PHPUnit available? =="
which php || true
php -v 2>/dev/null || true

Repository: Decipher/custom_formatters

Length of output: 21642


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate Composer dependencies / Drupal packages =="
fd -a 'composer\.(json|lock)$' . | sed 's#^\./##'
for f in $(fd 'composer\.(json|lock)$' .); do
  echo "--- $f ---"
  if [[ "$f" == *composer.json ]]; then
    python3 - <<'PY' "$f"
import json, sys
p=sys.argv[1]
data=json.load(open(p))
print("name", data.get("name"))
print("require", data.get("require", {}))
print("require-dev", data.get("require-dev", {}))
PY
  else
    rg -n '"drupal/core"|"drush/drush"|"phpoffice/phpstan-drupal"|"phpunit/phpunit"|drupal/user' "$f" | head -80 || true
  fi
done

echo "== package manager presence =="
command -v composer || true
composer -V 2>/dev/null || true

echo "== inspect Drupal core drupal_createUser signature if vendored =="
rg -n "function drupalCreateUser|createUser\(" vendor -S 2>/dev/null | head -80 || true

Repository: Decipher/custom_formatters

Length of output: 568


🌐 Web query:

Drupal 9 BrowserTestBase drupalCreateUser return TypeHandler::createUser UserInterface AccountInterface docblock

💡 Result:

In Drupal 9, BrowserTestBase provides the drupalCreateUser method as an alias for the createUser method defined in the UserCreationTrait [1][2][3]. The return type specified in the docblock for UserCreationTrait::createUser is \Drupal\user\UserInterface|false (or \Drupal\user\UserInterface in more recent versions) [4][5]. While the method returns a fully loaded user entity (which implements UserInterface), the docblock often notes that it returns FALSE if account creation fails [4][6]. Key details regarding the interfaces: 1. UserInterface: This is the interface that defines a user entity in Drupal [7][8]. It extends AccountInterface, ContentEntityInterface, and EntityChangedInterface [7]. 2. AccountInterface: This is a foundational interface representing a user's session and account properties (such as user ID, roles, and permissions) [9]. Because UserInterface extends AccountInterface, a user object returned by drupalCreateUser satisfies both type hints [7]. In practice, the object returned by drupalCreateUser is an instance of \Drupal\user\Entity\User, which implements UserInterface [4][5]. The docblock in UserCreationTrait specifically documents the return type as \Drupal\user\UserInterface to maintain standard interface-based type hinting for entity objects, rather than relying on the concrete entity class [4][5].

Citations:


🌐 Web query:

Drupal UserInterface extends AccountInterface

💡 Result:

Yes, the Drupal UserInterface extends AccountInterface [1][2][3]. In the Drupal API, the UserInterface (found at \Drupal\user\UserInterface) defines a user entity and extends several interfaces, including ContentEntityInterface, EntityChangedInterface, and AccountInterface [1][2][4]. The AccountInterface (found at \Drupal\Core\Session\AccountInterface) is designed to represent the current user and defines methods related to account identity, roles, and access checking [5][6]. Because UserInterface extends AccountInterface, any user entity object inherently satisfies the requirements of an account [7]. When writing code that specifically requires access checking (such as verifying permissions), Drupal best practices recommend type-hinting for AccountInterface rather than UserInterface to ensure broader compatibility, as other objects (like the global session) also implement AccountInterface without being full user entities [7][5].

Citations:


🌐 Web query:

Drupal Core phpstan-drupal assert instanceof AccountInterface UserInterface always true UserCreationTrait createUser

💡 Result:

In Drupal test environments, when using UserCreationTrait::createUser, PHPStan may report an instanceof.alwaysTrue error if the returned object is already known by the static analyzer to implement AccountInterface or UserInterface [1][2]. This occurs because PHPStan has identified the return type of createUser—which returns an object implementing these interfaces—and determines that the subsequent instanceof check is redundant [1]. The UserCreationTrait::createUser method is defined in Drupal core to return a user entity (or false in older versions), and its type information is explicitly provided via PHPDoc [3][4]. When PHPStan tracks this type correctly, it concludes that the variable satisfies the interface requirements by definition [1]. To resolve this issue, you have several options: 1. Remove the redundant check: Since PHPStan has determined the check is always true, the safest and cleanest approach is to remove the unnecessary instanceof condition entirely [1]. If the code requires assurance about the type, PHPStan already possesses that knowledge [1]. 2. Use PHPStan ignore comments: If you must retain the check for runtime safety (e.g., in edge cases where the trait might be extended or overridden unexpectedly), you can suppress the warning using a comment above the code line [1]: // @phpstan-ignore instanceof.alwaysTrue if ($user instanceof AccountInterface) {... } 3. Configure phpstan.neon: You can disable this rule globally or per-file in your phpstan.neon configuration if it frequently triggers false positives in your test suite [1][2]: parameters: ignoreErrors: - identifier: instanceof.alwaysTrue Note that Drupal core has actively worked to clean up these types of PHPStan errors in test traits to improve developer experience and baseline stability [5][6][7]. If you are working on a Drupal module, ensure you are using the modern signature of createUser (array $permissions = [], $name = NULL, $admin = FALSE, array $values = []) to avoid deprecation issues and improve type accuracy [7].

Citations:


Remove the redundant AccountInterface assertions.

drupalCreateUser() in CustomFormattersTestBase returns a Drupal user object that already implements AccountInterface, and UserCreationTrait::createUser() in FormatterSettingAccessControlTest returns \Drupal\user\UserInterface, which also extends AccountInterface. Drop these instanceof checks to avoid redundant assertions/PHPStan instanceof.alwaysTrue noise.

🧰 Tools
🪛 GitHub Check: lint

[failure] 39-39:
Instanceof between Drupal\user\UserInterface and Drupal\Core\Session\AccountInterface will always evaluate to true.


[failure] 39-39:
Call to function assert() with true will always evaluate to true.

📍 Affects 2 files
  • tests/src/Functional/FormatterSettingUiTest.php#L39-L39 (this comment)
  • tests/src/Kernel/FormatterSettingAccessControlTest.php#L63-L68
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/src/Functional/FormatterSettingUiTest.php` at line 39, Remove the
redundant AccountInterface instanceof assertions in
tests/src/Functional/FormatterSettingUiTest.php lines 39-39 and
tests/src/Kernel/FormatterSettingAccessControlTest.php lines 63-68. Update the
affected test setup methods to use the users returned by drupalCreateUser() and
UserCreationTrait::createUser() directly, preserving all subsequent behavior.

Source: Linters/SAST tools

@Decipher
Decipher force-pushed the fix/phpstan-phpcs-cleanup branch from fefad1d to 81e9403 Compare July 28, 2026 22:05
- Php.php: correct phpcs sniff name (DrupalPractice.CodeAnalysis)
- CustomFormatters deriver: cast base_plugin_definition to array
- Test files: use \assert() for type narrowing on drupalCreateUser()
  return (returns User|false on Drupal 11.3, the $CORE_SUPPORTED version)
- CustomFormattersTestBase: fix property type to AccountInterface
- GitHub CI: pin lint job to DRUPAL_VERSION=11.3 to match drupalcode.org
  $CORE_SUPPORTED, ensuring consistent type inference across environments
- phpstan.neon: targeted ignores for deprecated node_access_rebuild()
  and _contextual_links_to_id() (no replacement available in Drupal 11)
- phpstan.neon: ignore entityStorageDirectInjection in ListBuilder
  (required by ConfigEntityListBuilder parent constructor)
@Decipher
Decipher force-pushed the fix/phpstan-phpcs-cleanup branch from 81e9403 to e6edab9 Compare July 28, 2026 22:15
- FormatterListBuilder entityStorageDirectInjection ignore now uses a
  leading-wildcard path so it matches under both GitHub Actions' build/
  layout and Drupal.org's module-root layout; the rooted path only
  matched the former, so the DrupalCI-equivalent pipeline still failed.
- Remove instanceof/assert(AccountInterface) checks in test setUp()
  methods now that phpstan proves the returned type is already
  narrowed, and drop the resulting unused imports.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/src/Kernel/FormatterSettingAccessControlTest.php`:
- Around line 62-65: Update the test setup around createUser() so both returned
User|false values are explicitly validated before assignment to adminUser and
regularUser. Fail the test immediately when either creation returns false, then
assign the narrowed user values and preserve the existing access-test setup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f0138b03-a101-42dd-9fc3-81032b95a8b8

📥 Commits

Reviewing files that changed from the base of the PR and between 81e9403 and a950d46.

📒 Files selected for processing (9)
  • .github/workflows/test.yml
  • phpstan.neon
  • src/Plugin/CustomFormatters/FormatterType/Php.php
  • src/Plugin/Derivative/CustomFormatters.php
  • tests/src/Functional/CustomFormattersTestBase.php
  • tests/src/Functional/FormatterSettingFormTest.php
  • tests/src/Functional/FormatterSettingUiTest.php
  • tests/src/Kernel/FormatterPreviewFormTest.php
  • tests/src/Kernel/FormatterSettingAccessControlTest.php
💤 Files with no reviewable changes (3)
  • tests/src/Functional/FormatterSettingUiTest.php
  • tests/src/Functional/FormatterSettingFormTest.php
  • tests/src/Kernel/FormatterPreviewFormTest.php
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/Plugin/CustomFormatters/FormatterType/Php.php
  • .github/workflows/test.yml
  • phpstan.neon

Comment on lines +62 to +65
$admin = $this->createUser(['administer custom formatters']);
$this->adminUser = $admin;
$regular = $this->createUser([]);
$this->regularUser = $regular;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore type narrowing after createUser().

createUser() returns User|false, but these assignments claim AccountInterface. This currently fails PHPStan and can pass false into access() if setup fails. Add an explicit failure check before assigning each user.

Proposed fix
     $admin = $this->createUser(['administer custom formatters']);
+    if (!$admin instanceof \Drupal\Core\Session\AccountInterface) {
+      throw new \RuntimeException('Failed to create the admin test user.');
+    }
     $this->adminUser = $admin;
     $regular = $this->createUser([]);
+    if (!$regular instanceof \Drupal\Core\Session\AccountInterface) {
+      throw new \RuntimeException('Failed to create the regular test user.');
+    }
     $this->regularUser = $regular;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$admin = $this->createUser(['administer custom formatters']);
$this->adminUser = $admin;
$regular = $this->createUser([]);
$this->regularUser = $regular;
$admin = $this->createUser(['administer custom formatters']);
if (!$admin instanceof \Drupal\Core\Session\AccountInterface) {
throw new \RuntimeException('Failed to create the admin test user.');
}
$this->adminUser = $admin;
$regular = $this->createUser([]);
if (!$regular instanceof \Drupal\Core\Session\AccountInterface) {
throw new \RuntimeException('Failed to create the regular test user.');
}
$this->regularUser = $regular;
🧰 Tools
🪛 GitHub Check: lint

[failure] 65-65:
Property Drupal\Tests\custom_formatters\Kernel\FormatterSettingAccessControlTest::$regularUser (Drupal\Core\Session\AccountInterface) does not accept Drupal\user\Entity\User|false.


[failure] 63-63:
Property Drupal\Tests\custom_formatters\Kernel\FormatterSettingAccessControlTest::$adminUser (Drupal\Core\Session\AccountInterface) does not accept Drupal\user\Entity\User|false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/src/Kernel/FormatterSettingAccessControlTest.php` around lines 62 - 65,
Update the test setup around createUser() so both returned User|false values are
explicitly validated before assignment to adminUser and regularUser. Fail the
test immediately when either creation returns false, then assign the narrowed
user values and preserve the existing access-test setup.

Source: Linters/SAST tools

Decipher added 3 commits July 29, 2026 10:13
The previous commit removed these as redundant per a CodeRabbit
suggestion, verified locally with an unpinned DRUPAL_VERSION. Both
GitHub Actions (pinned to 11.3) and gitlab.local's DrupalCI-equivalent
pipeline disagree: on 11.3, drupalCreateUser()/createUser() return
User|false, so the assert()/instanceof was legitimately narrowing
away the false case, not redundant. Removing it broke
assign.propertyType and argument.type checks on both real pipelines.

The FormatterListBuilder phpstan.neon path fix from the previous
commit is unaffected and stays.
Drupal.org's actual pipeline (git.drupalcode.org MR !13, job 11227419)
confirms these are currently redundant: the resolved drupal/core
UserCreationTrait::createUser() stub no longer includes |false, so
phpstan reports instanceof.alwaysTrue / function.alreadyNarrowedType.

GitHub Actions and the gitlab.local mirror disagreed only because they
resolved a different drupal/core patch at a different point in time
(this project doesn't commit a composer.lock, so each CI run resolves
fresh). Drupal.org is the actual target, so it wins.
DRUPAL_VERSION=11.3.0 was pinned to match drupalcode.org's
$CORE_SUPPORTED at the time, but core has since moved on to 11.4 as
current stable — drupal.org's own MR pipeline now resolves
drupal/core 11.4.4, not 11.3.x. The pin doesn't track that; it just
goes stale again every time core releases a new minor.

Drop the override and let it fall back to devtools/assemble's default
(~11, i.e. latest compatible 11.x), matching drupal.org's own
behavior of always resolving current stable rather than a fixed
patch level.
@Decipher

Copy link
Copy Markdown
Owner Author

Landed via drupal.org MR !13 (squash-merged into 4.1.x as f6fae76), then synced back here. Closing rather than merging since the content is already in 4.1.x.

@Decipher Decipher closed this Jul 29, 2026
@Decipher
Decipher deleted the fix/phpstan-phpcs-cleanup branch July 29, 2026 11:21
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