Problem/Motivation

How about bumping PHPStan rule level to 3 before D12 is shipped, e.g. during the beta phase?

The baseline will likely be at its lowest after all the deprecated D11 code will have been removed, before new deprecations will start getting in. Also, the PHPStan2 and 3 preparation issues have been lingering forever, not much focus; bumping the level and adding to the baseline, even if it will bring a large amount of errors, would potentially drive the focus on the fixes.

Proposed resolution

Bump PHPStan rule level to 3.

Add jangregor/phpstan-prophecy as a dev dependency. This would allow resolving Prophecy mocks into their mocked types, and avoid approx 2k additional false positives in the baseline-

jangregor/phpstan-prophecy Dependency Evaluation

Maintainership of the package

Security policies of the package

  • Security policies of the package: unspecified, but this is a dev dependency
  • Expected release and support cycles

    Best effort?

    Code quality

    Documented, comprehensive tests

    Other dependencies it would add, if any (the full tree, not just direct dependencies), and evaluations for those dependencies as well

    None.

    Remaining tasks

    If the dependency is approved, tag the issue, "approved dependency evaluation".

    User interface changes

    API changes

    Data model changes

    Release notes snippet

    Issue fork drupal-3425412

    Command icon Show commands

    Start within a Git clone of the project using the version control instructions.

    Or, if you do not have SSH keys set up on git.drupalcode.org:

    Comments

    mondrake created an issue. See original summary.

    mondrake’s picture

    We'd probably need to add Jan0707/phpstan-prophecy as a dependency to interpret the prophesized methods that are yielding a lot of L2 errors.

    longwave’s picture

    mondrake’s picture

    #4 yes, it's the contrary... :)

    Alternative approach.

    mondrake changed the visibility of the branch 3425412-bump-phpstan-rule to hidden.

    mondrake changed the visibility of the branch 3425412-bump-phpstan-rule to active.

    mondrake’s picture

    Status: Active » Needs review

    Adds 7785 new level-2 errors to the baseline.

    Requires --dev jangregor/phpstan-prophecy dev-master. Release 1.0.0 has a failure that was fixed by https://github.com/Jan0707/phpstan-prophecy/pull/316. We would need to wait for a point release here.

    NR for the concept.

    idebr’s picture

    Component: batch system » base system
    mondrake’s picture

    Tagging.

    mstrelan’s picture

    I generated a baseline for each level from 1-6 so see where is the biggest jump.

    level 1: 630 errors
    level 2: 8415 errors
    level 3: 10657 errors
    level 4: 14012 errors
    level 5: 16015 errors
    level 6: 64641 errors

    I think if we're happy with a baseline of ~8000 errors at level 2 then we should aim a little higher, perhaps level 3, or, if there is appetite, then 4 or 5.

    mondrake’s picture

    TBH I do not think we can be happy with a baseline of 8k. But the pre-work to reduce it is nowhere.

    I am rather hoping that bumping the level 1 notch up will drive more fixing, and in any case prevent further non-L2-compliant code to be introduced.

    We have introduced L0 2 years ago with 1k errors more or less, and bumped to L1 in Nov 22 - still having 630 errors now is not a great score... bumping to 8k is a very large step, and more than enough for some time to come.

    Somewhere I read L2 should be the decent minimum for any codebase, let's walk there before we run further.

    mondrake’s picture

    Let me also say there's another reason not to skip levels: each level up will require contributors some time to adapt their coding style - use of assertions or inline doctypes to clarify type of variables, later on description of iterative types etc... it takes time to digest.

    I remember contributing to Doctrine DBAL (that is on L9), without prior experience of PHPStan reports... at first I simply could not understand what I was doing wrong... then I joined the pieces and started seeing the benefits, but you really need to have a codebase that is already at that level to be able to contribute meaningfully. In the current shape of Drupal codebase, jumping ahead would mean that new contribution will have to do all sort of workarounds to satisfy PHPStan, whereas it should be PHPStan helping us improve the code.

    longwave’s picture

    Was hoping for some low hanging fruit we can knock off to quickly reduce the number but it's not looking that promising:

    $ grep message core/.phpstan-baseline.php|sort|uniq -c|sort -rn|head
        203 	'message' => '#^Access to an undefined property Drupal\\\\Core\\\\Session\\\\AccountInterface\\:\\:\\$passRaw\\.$#',
        201 	'message' => '#^Access to an undefined property Drupal\\\\Core\\\\Session\\\\AccountInterface\\:\\:\\$name\\.$#',
         85 	'message' => '#^Call to an undefined method Drupal\\\\Tests\\\\WebAssert\\:\\:waitForElementVisible\\(\\)\\.$#',
         80 	'message' => '#^Call to an undefined method Drupal\\\\Tests\\\\WebAssert\\:\\:waitForElement\\(\\)\\.$#',
         79 	'message' => '#^Call to an undefined method Drupal\\\\Tests\\\\WebAssert\\:\\:assertWaitOnAjaxRequest\\(\\)\\.$#',
         68 	'message' => '#^Call to an undefined method Drupal\\\\Core\\\\TypedData\\\\TraversableTypedDataInterface\\:\\:get\\(\\)\\.$#',
         48 	'message' => '#^Call to an undefined method Drupal\\\\Core\\\\Entity\\\\EntityInterface\\:\\:get\\(\\)\\.$#',
         41 	'message' => '#^Call to an undefined method Drupal\\\\workflows\\\\WorkflowTypeInterface\\:\\:addEntityTypeAndBundle\\(\\)\\.$#',
         32 	'message' => '#^Call to an undefined method Drupal\\\\Core\\\\Entity\\\\EntityInterface\\:\\:getTranslation\\(\\)\\.$#',
         30 	'message' => '#^Cannot access property \\$uri on object\\|false\\.$#',
    

    Even solving all those will only take 867 off the 8,000+ errors. The WebAssert ones look like they might just need a return type declaring correctly but e.g. the raw password one is test specific and might need us to rethink how we handle passwords during functional tests.

    smustgrave’s picture

    Status: Needs review » Needs work

    MR appears to be unmergable, surprised the bot never picked it up.

    I'm not super into the phpstan stuff like others here in this ticket but in regards to bumping levels. When a new rule or anything is added usually involves having to go back and update several MRs. Can be argued that's just the name of the game with Drupal but can understand community frustration when they have an MR all ready and it keeps getting sent back because of changing rules. So think doing small increments is good.

    mondrake’s picture

    Status: Needs work » Needs review

    Please let’s focus on opinions if this should go or not, rather than starting NR-NW tennis. The MR will become unmergeable every day, but it’s not rerolling it that the question changes.

    Also please keep comments to the scope of this issue: bump to level 2. There’s another issue that preaches level 9, lets have the appropriate level discussion there, in one place.

    Thanks

    mstrelan’s picture

    Just pointing out that phpstan-prophecy 1.0.1 has been released and the MR has been updated to that version, so the concerns in #8 about the dev release are no longer applicable. Let's get this in.

    mondrake’s picture

    Issue tags: +no-needs-review-bot
    mondrake’s picture

    @mstrelan thanks a lot for clarifying in #17 that phpstan-prophecy is no longer blocking

    spokje’s picture

    Looks like when we bump to PHPStan L2 we get an initial addition of ~7800 suppressions.
    Seeing the amount of active issues to tackle the L1 suppressions, I think it's safe to assume these won't go away quickly.

    So the benefit would be preventing new L2 issues creeping in new/refactored code.

    It's probably my lack of Google skills, but what are the extra checks in L2 compared to L1, what will be prevented from creeping in by us doing PHPStan L++?

    mstrelan’s picture

    Level 2 gives you:

    unknown methods checked on all expressions (not just $this), validating PHPDocs

    See https://phpstan.org/user-guide/rule-levels

    Or more specifically see https://github.com/phpstan/phpstan-src/blob/1.11.x/conf/config.level2.neon

    spokje’s picture

    Status: Needs review » Reviewed & tested by the community

    Thanks @mstrelan, should have found that myself, but somehow didn't. (Makes mental note to check caffeine-percentage of new bought coffee-brand).

    Seeing those checks and also the fact that if we want to do it with the least amount of disruptions to other MRs, it's probably now or when D12 arrives, I'm pulling the trigger and put the ball in core commiters court: RTBC for me.

    catch’s picture

    I'm +1 here for the obvious reason that it will stop us introducing new errors, so even if it takes years to reduce the current baseline, at least it will be visibly getting smaller instead of silently getting larger.

    On going to L3 or L5 etc., staggering the MR breakage for new rules seems like a good reason to do one at a time. We could still try to go up a level every 6 months or 3 months if we want to (I'm not saying we should definitely do that, but it's worth thinking about). It does look like level 5 would be a couple of small jumps.

    mstrelan’s picture

    I don't want to change the scope, will almost instantly open a follow up if this goes in, but level 3 gives us:

    return types, types assigned to properties

    On all new code, which most code reviews are picking up anyway, but would be much nicer if it was for free.

    spokje’s picture

    One argument against staggering would be that raising the PHPStan level would be disruptive to (almost) all open MRs for each bump, so bumping more then one level would make it less disruptive.

    That might be the case when the baseline was in the NEON format, and IIRC was one (of many) reasons the Bump-to-L9 issue is stalling.
    But as part of that issue, we switched the baseline over to PHP with the reasoning that Git would be much smarter about merging it.

    Is bumping a PHPStan level (with each one bringing in loads of changes on the baseline file) still as disruptive to other MRs as we (or at least I) think, or is this a non-issue by now?

    If we're in non-issue land on that, I'm all about staggering, if not, we might want to prevent all/a lot of open MRs grinding to a rebase-halt each time we stagger-bump and do it in one go?

    catch’s picture

    One argument against staggering would be that raising the PHPStan level would be disruptive to (almost) all open MRs for each bump, so bumping more then one level would make it less disruptive.

    This is true to an extent, but it will only be disruptive in the following cases:
    1. The MR is changing the baseline already, and has a conflict.
    2. The MR is introducing new PHPStan failures which are caught by the new level.

    Hopefully this won't be almost all open MRs but just a small subset.

    There have been 698 commits to the 11.x branch in the past 90 days, so even if we went up a level every three months, anything committed in the interim between bumps by definition won't get affected. That still leaves the thousands of open MRs, but not all of these are rebased that often too.

    mondrake’s picture

    Just noticed that phpstan-prophecy specifies this in their composer.json

                "conflict": {
                    "phpspec/prophecy": "<1.7.0 || >=2.0.0",
                    "phpunit/phpunit": "<6.0.0 || >=10.0.0"
                },

    So this ATM would block the PHPUnit bump to 10.

    We probably want to wait for a release upstream that is compatible?

    mondrake’s picture

    Status: Reviewed & tested by the community » Postponed

    Actually https://github.com/Jan0707/phpstan-prophecy/pull/334 allowing PHPUnit 10 & 11 was already merged (yesterday!), so it's a matter of waiting for a new point release.

    mstrelan’s picture

    Does this need a dependency evaluation also?

    mondrake’s picture

    Issue summary: View changes

    Added a dependency evaluation

    mondrake’s picture

    phpstan-prophecy just released 1.0.2 that is PHPUnit 10 compatible, bumping

    catch’s picture

    Status: Postponed » Needs review

    Back to NR?

    mondrake’s picture

    I refrained from self-RTBC, but it should be a quick check to do it.

    It will certainly require frequent rebasing.

    mstrelan’s picture

    Does this still need framework manager review? Otherwise RTBC from me.

    spokje’s picture

    Status: Needs review » Reviewed & tested by the community

    Let's bump it to RTBC and see if the core committers think we still need FM approval and if so, if they can ping them :)

    mstrelan’s picture

    Status: Reviewed & tested by the community » Needs work
    Issue tags: +Needs change record

    We probably need a change record :D

    spokje’s picture

    Status: Needs work » Needs review
    Issue tags: -Needs change record

    Added two CRs.

    mstrelan’s picture

    Status: Needs review » Reviewed & tested by the community

    Thanks for creating the CRs. This looks good. Baseline may need to be regenerated when committing.

    longwave’s picture

    @alexpott was concerned about the size of the baseline, a handful of quick fixes in MR!7547 cut it down by about 25%.

    spokje’s picture

    a handful of quick fixes in MR!7547 cut it down by about 25%.

    Great, but not sure this is part of this issue? Or even what an acceptable amount of added errors to a baseline would be?

    Personally, (n=1, yadayada) this seems like follow-up issue material to me and might be derailing the original intent?
    But what do I know? _shrug_

    longwave’s picture

    Oh yeah, that's why I made a separate MR, only as a demo. But maybe we move that to a separate issue and try and pick off these and any other widespread ones caused by traits or base classes, before doing this, if there are concerns about baseline size.

    catch’s picture

    Title: Bump PHPStan rule level to 2 » [PP-1] Bump PHPStan rule level to 2
    Status: Reviewed & tested by the community » Postponed

    Let's do what #42 suggests, it's not just the actual baseline size, but that'll also help to avoid some MR conflicts potentially later on.

    mondrake’s picture

    Added inline comments, even though it's for the separate issue now I think.

    andypost’s picture

    mondrake’s picture

    Rebased.

    New stats:

    ----------------------------------------
    PHPStan baseline statistics
    ----------------------------------------
     16312 * Total baselined errors
    ----------------------------------------
    Breakdown by error identifier:
      8885 missingType.return
      3411 method.notFound
      2163 property.notFound
       315 method.nonObject
       248 property.nonObject
       241 return.missing
       168 variable.undefined
       152 class.notFound
       108 arguments.count
        89 missingType.generics
        56 isset.variable
        55 parameter.phpDocType
        47 varTag.differentVariable
        45 varTag.nativeType
        40 empty.variable
        39 return.phpDocType
        39 staticClassAccess.privateMethod
        31 constructor.unusedParameter
        27 varTag.variableNotFound
        18 property.phpDocType
        15 pluginManagerSetsCacheBackend.missingCacheBackend
        11 binaryOp.invalid
         9 class.extendsDeprecatedClass
         8 class.nameCase
         8 assignOp.invalid
         8 interface.nameCase
         8 method.void
         6 parameter.defaultValue
         6 phpDoc.parseError
         5 staticMethod.nonObject
         5 varTag.misplaced
         4 parameter.notFound
         4 cast.string
         4 encapsedStringPart.nonString
         3 varTag.noVariable
         3 staticClassAccess.privateProperty
         3 method.deprecated
         2 throws.notThrowable
         2 impureMethod.pure
         2 property.protected
         2 parameter.deprecatedClass
         2 staticClassAccess.privateConstant
         2 includeOnce.fileNotFound
         1 smallerOrEqual.invalid
         1 generics.existingClass
         1 staticServiceDeprecatedService.deprecated
         1 greater.invalid
         1 nullCoalesce.variable
         1 varTag.trait
         1 method.protected
         1 function.deprecated
         1 equal.invalid
         1 unset.offset
         1 callable.void
         1 staticMethod.void
         1 phpunit.coversMethod

    HEAD 11.x stats:

    ----------------------------------------
    PHPStan baseline statistics
    ----------------------------------------
      9442 * Total baselined errors
    ----------------------------------------
    Breakdown by error identifier:
      8885 missingType.return
       168 variable.undefined
       121 return.missing
        89 missingType.generics
        56 isset.variable
        40 empty.variable
        31 constructor.unusedParameter
        15 pluginManagerSetsCacheBackend.missingCacheBackend
         9 class.extendsDeprecatedClass
         8 method.notFound
         4 property.notFound
         3 arguments.count
         3 method.deprecated
         2 parameter.deprecatedClass
         2 includeOnce.fileNotFound
         1 staticServiceDeprecatedService.deprecated
         1 nullCoalesce.variable
         1 function.deprecated
         1 unset.offset
         1 staticMethod.void
         1 phpunit.coversMethod

    Changed

    • phpstan-baseline: 16312 (9442)
    • phpstan-baseline.method.notFound: 3411 (8)
    • phpstan-baseline.property.notFound: 2163 (4)
    • phpstan-baseline.return.missing: 241 (121)
    • phpstan-baseline.arguments.count: 108 (3)
    mondrake’s picture

    Version: 11.x-dev » main
    ghost of drupal past’s picture

    andypost’s picture

    Faced that NoDiscard is skipped because checkThisOnly: true in #3560672-12: [policy, no patch] Decide if and where to adopt the #[NoDiscard] attribute

    longwave’s picture

    Title: [PP-1] Bump PHPStan rule level to 2 » Bump PHPStan rule level to 2
    Status: Postponed » Needs review

    When we added return types to the baseline we had 12,810 entries:

    $ git show 4548e0708681707ce1eb99226dfedb5819301ae8:core/.phpstan-baseline.php|grep '^]'|wc -l
    12810
    

    We are currently at 6,632 entries in main. We've completed #42 at level 1 by fixing most test traits and trivial cases; we're left with largely concrete return types now that we can't necessarily add because of downstream users. #3486376: Extend Symfony DebugClassLoader to report missing cross-module @return types will notify those users in most cases, but there isn't a huge amount we can do until then.

    The latest job shows the baseline would be bumped to 13,881 entries if we go to level 2 now. Perhaps it's time for Drupal 12?

    longwave’s picture

    Level 3 takes us up to 15,343 entries, which isn't significantly more. Maybe we can make that jump?

    longwave’s picture

    phpstan/phpstan-symfony is at 15,311 which is only a reduction of 32 entries. It looks like if we can feed it an XML container definition it might be more useful, but I don't think it's worth adding in this issue.

    mondrake’s picture

    Title: Bump PHPStan rule level to 2 » Bump PHPStan rule level to 3

    I'm all for this, +1

    mstrelan’s picture

    Issue tags: -PHPStan-2 +PHPStan-3

    CR needs updating to mention level 3

    smustgrave’s picture

    Appears to need a rebase but there are some trait tickets and other random ticket that add/remove chunks for the baseline. Should those land and then bump?

    mondrake’s picture

    #57 this will likely require a rebase anytime another commit happens, so it’s rather pointless getting into the guinea pig wheel 😀

    We probably need to agree it’s worth doing, when positive set it to be committed on a specific date (in the beta window?), then set it to a status it does not get forgotten (RTBC?).

    BTW when this is committed, a large part of the MRs in the queue will need rebase too

    catch’s picture

    During the beta window sounds like a good idea for commit timing. As well as lots of rebases this is also going to mean most backports to 11.x won't cherry pick, although that's increasingly the case already.

    About 15k entries seems like it should hopefully be manageable to deal with.

    PHPStan has got a lot faster at generating a baseline so it's not too bad doing that as an extra step before commit occasionally.

    longwave’s picture

    Title: Bump PHPStan rule level to 3 » [May 18, 2026] Bump PHPStan rule level to 3
    Status: Needs review » Postponed
    Issue tags: +beta target
    andypost’s picture

    mondrake’s picture

    Title: [May 18, 2026] Bump PHPStan rule level to 3 » [Sep 14, 2026] Bump PHPStan rule level to 3

    mondrake changed the visibility of the branch 3425412-phpstan-2-quick-fixes to hidden.

    longwave’s picture

    Status: Postponed » Needs work

    Time to revive this again if we are to land it in two weeks.

    longwave’s picture

    mondrake’s picture

    solideogloria’s picture

    Status: Postponed » Needs work

    That issue has been marked Fixed. Changing status.

    solideogloria’s picture

    Issue summary: View changes

    Updating IS to PHPStan-3

    mondrake’s picture

    Status: Needs work » Needs review
    Issue tags: +Avoid commit conflicts

    Brought the MR up-to-date. Practically any commit would imply a reroll here will be needed, so tagged 'avoid commit conflicts'.

    mondrake’s picture

    Issue tags: -no-needs-review-bot
    smustgrave’s picture

    Status: Needs review » Reviewed & tested by the community
    Issue tags: -Needs issue summary update

    Checked jangregor/phpstan-prophecy and it seems to be well supported (know this was already checked just didn't know what to verify). They've had 1 release this year (2026) and 3 last year (2025) so think that's a good pace.

    Results are in the pudding right. Upping to level 3 passed the pipeline.

    I'm not framework manager but going to mark.

    Summary seems fine to me.

    Hope this is okay to mark..

    longwave’s picture

    This has a chance of conflicting with everything else that is RTBC and hoping to land before beta, so I think this should wait for commit until just after beta1 is released, I just wanted to resurrect it early enough that we don't miss it or forget.

    mondrake’s picture

    Can a FM address the 'Needs framework manager review' tag please in the meantime? We do not have to wait to confirm the dependency is OK.

    longwave’s picture

    Issue summary: View changes

    Bumping the versions in the IS.

    alexpott’s picture

    Discussed with @longwave - ideally #3067979: Exclude test files from release packages would have landed so the baseline would be excluded from most Drupal site. @longwave has a great idea of adding just the baseline to .gitattributes. Let's open a follow-up to do that.

    I'm very +1 to making this change and setting a new line in the sand for Drupal 12.

    quietone’s picture

    Title: [Sep 14, 2026] Bump PHPStan rule level to 3 » Bump PHPStan rule level to 3
    Issue summary: View changes
    Issue tags: +Needs release manager review

    Removing the date since it is September.

    Updated according to the dependency policy by converting to the template in the policy and tagging for release manager review.

    If this is approved, the issue is to be tagged, "approved dependency evaluation"

    larowlan’s picture

    Discussed with longwave and alexpott
    Both Alex and I are in support

    The idea to use gitattributes is fine for a follow up

    mondrake’s picture

    Updated CR.

    godotislate’s picture

    +1 from me.

    needs-review-queue-bot’s picture

    Status: Reviewed & tested by the community » Needs work
    StatusFileSize
    new91 bytes

    The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

    nicxvan’s picture

    There was some discussion in slack https://drupal.slack.com/archives/C079NQPQUEN/p1788462517402589?thread_t...

    I had some concerns about generating the baseline locally and resources required.

    The thinking is since the job will generate the baseline that can be downloaded the local generation is not a concern.

    One more question does the failure link to the new baseline file?

    Also I had a question about the baseline size, this will about double the size, but that brings it to about the size the initial baseline was so that should be fine too.

    borisson_’s picture

    Also I had a question about the baseline size, this will about double the size, but that brings it to about the size the initial baseline was so that should be fine too.

    I agree that I don't think this will be a problem at the moment, because we've worked with a baseline of this size before, because of that I think it should also not be a very big issue regarding resources. It seems like there are a couple of traits that we can fix which will bring this back down to a more manageable state soon.