Problem/Motivation
Coming from #3190585-14: [META] Run PHPStan on Drupal core but as 10.0 core should be compatible with PHP 8.2 this now required
After #3299327: Replace deprecated static::method() calls for PHP 8.2 and https://wiki.php.net/rfc/deprecate_partially_supported_callables it would be great to normalize usage of callables to first-class as D10 requires PHP 8.1
If compatibility with PHP < 8.1 is not desired, use of the first-class callable syntax self::method(...) is also possible.
Replace usages of [static::class, '::methodName'] to static::methodName(...) which is more readable and performant since PHP 8.1
Steps to reproduce
- https://wiki.php.net/rfc/deprecate_partially_supported_callables
- https://wiki.php.net/rfc/partially-supported-callables-expand-deprecatio...
- https://php.watch/versions/8.2/partially-supported-callable-deprecation
- https://php.watch/versions/8.1/first-class-callable-syntax
Proposed resolution
- adjust places where this is used - uasort, array_map(), array_filter,call_user_func_array(),
- fix usage and use rector
- review/commit
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
| Comment | File | Size | Author |
|---|---|---|---|
| #67 | 3259716-67.patch | 21.99 KB | andypost |
| #67 | interdiff.txt | 8.33 KB | andypost |
Issue fork drupal-3259716
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
Comment #2
beatrizrodriguesI will work at this.
Comment #4
beatrizrodriguesHope it fits.
Comment #5
mondrakeWe need also to adjust PHPStan baseline, this would have failed if #3259355: Always do a full phpstan analysis on DrupalCI were in
Comment #6
mallezieComment #7
mallezieI added the regenerated baseline.
Reverted some unrelated changes (although at first sight they don't seem wrong, they were not flagged / in scope here).
I've also added changes for big pipe test files which had the same issue in the baseline.
Comment #8
lucienchalom commentedI do believe something went wrong with the MR, there are 92 files changed, 1064 changes in code, including some related to static::class but also... coding standards in css files? scaffold deprecation? and documentation of the migration plugin. And many more.
i couldn't review, moving back to needs work, i'm sorry.
Comment #9
mallezieI've rebased the MR.
This only shows needed changes here.
Comment #10
longwaveLots of test fails.
Comment #11
mallezieTest fails are unrelated here. Seems LayoutBuilder JS tests are flaky today.
See also #3267124: Temporarily skip failing tests
Comment #12
daffie commentedThe MR needs to be rebased.
Comment #13
mallezieComment #14
daffie commentedWhen I look to phpstan-baseline.neon, I find 2 other instances:
for:
I think we need to do explode('::', $callback) first.
for:
All the other changes look good.
Comment #15
andregp commented@daffie, I'll check this.
Comment #16
andregp commentedUpdated the phpstan-baseline.neon file.
Comment #17
andregp commentedI'll try to fix this merge
Comment #18
andregp commentedMerged master
Comment #19
longwavePHPStan is failing.
Comment #20
andregp commented@longwave, sorry, I'll check this
Comment #21
andregp commentedIf I try to change the line in RenderTest.php to
phpstan returs the following error:
So it might be better to keep as it is.
The errors on phpstan at #17 were due to a bad merge, I reverted the merge and did it again.
Comment #22
andregp commentedUpdating status.
Comment #23
lucienchalom commentedwith the MR, the phpstan-baseline.neon is okay, I couldn't find any more errors for "'non\\-empty\\-string' at key '0' is invalid", and all the calls for methods that created them were fixed besides the one cited in #21.
i found another 9 uses of this concatenation, i will list them here, but I consider them out of the scope of the issue because the focus was phpstan. considering this, moving to RTBC.
core/modules/comment/src/Entity/Comment.php (292)
core/modules/content_translation/src/ContentTranslationHandler.php (179)
core/modules/image/tests/modules/image_module_test/src/Plugin/Field/FieldWidget/DummyAjaxWidget.php (34)
core/modules/media/src/Entity/Media.php (492)
core/modules/media/src/Plugin/Filter/MediaEmbed.php (250)
core/modules/user/src/EntityOwnerTrait.php (41)
core/tests/Drupal/KernelTests/Core/Field/Entity/BaseFieldOverrideTest.php (77)
core/tests/Drupal/Tests/Core/Entity/BaseFieldDefinitionTest.php (356)
core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php (273)
Comment #24
longwaveIs it really right to change all these cases, given the code apparently worked before and we are just doing this to please PHPStan?
This feels like it could be raised as a bug in mglaman/phpstan-drupal instead, as that has special code to deal with #lazy_builder and friends: https://github.com/mglaman/phpstan-drupal/blob/main/src/Rules/Drupal/Ren... - if this is valid Drupal code I think it should be fixed over there or in PHPStan instead?
Comment #25
lucienchalom commentedsorry, the list I send is a suggestion of other possible concatenations.
they were not changed!
I am sorry that I made this confusing.
this list is not included in the MR.
Comment #26
longwaveAsked @mglaman in Slack about changing PHPStan but agree that it's probably more trouble than it's worth trying to get it to understand
static::class . '::someMethod'properly, so I retract my comment in #24, but I think one of the changes is incorrect - see MR comment.Comment #27
longwaveHaving said that, I then looked into PHPStan internals and think I can fix it, so I opened https://github.com/mglaman/phpstan-drupal/pull/436 which should allow it to parse
static::class . '::someMethod'correctly.Comment #28
andregp commentedI tried to address https://git.drupalcode.org/project/drupal/-/merge_requests/1709#note_102077
Comment #29
andypostComment #30
andypostUpdated IS steps with links to PHP 8.2 changes so it may need more work (I did not check current MR)
- https://wiki.php.net/rfc/deprecate_partially_supported_callables
- https://php.watch/versions/8.2/partially-supported-callable-deprecation
Comment #31
andypostre-parented
Comment #33
spokjeThere's still one left:
\Drupal\filter\FilterProcessResult::createPlaceholder()Suppression in the PHPStan baseline:
A lot of people (including me) when to a lot of different approaches to get it fixed (see the various commits on the MR), but we seem to all haven't found the correct way to do it.
Comment #34
mglamanhttps://github.com/mglaman/phpstan-drupal/pull/436 is about to be merged, which makes phpstan-drupal more intelligent for resolving
static::class . '::callback'.As for that last example, seems like something phpstan-drupal needs a test case for. We're not resolving the value of
$callbackComment #35
mglamanhttps://github.com/mglaman/phpstan-drupal/releases/tag/1.1.22 was released. This removes the warning about not being able to parse
static::classconcatenation with method names.Comment #36
spokjeTagging for IS update:
(emphasis mine)
vs
vs
So either this _was_ a PHPStan(-drupal) thing, that isn't a PHPStan(-drupal) thing any more, making this issue obsolete, or we need this anyway to become PHP 8.2 compatible.
Or something completely different?
Comment #37
longwave@Spokje This was a phpstan-drupal thing, where it did not understand
static::class . '::methodName'was equivalent to[static::class, 'methodName']for callables. It now does understand that those are equivalent and so postponing this until we upgrade to mglaman/phpstan-drupal 1.1.22, which should solve the false positives in the baseline that relate to this issue.Comment #38
spokjeThanks @longwave,
So if I understand this correctly: As soon as we require mglaman/phpstan-drupal 1.1.22 this false positives will disappear, making this issue obsolete and there's no need for doing this anyway to become (more) PHP 8.2 compatible?
Comment #39
spokjeFWIW: I locally did a rebuild of the PHPStan baseline with mglaman/phpstan-drupal 1.1.22 and all the failures we're fixing in the MR here are indeed gone.
So this issue will be obsolete when Core requires that version or higher.
Comment #40
mglamanThe next release of phpstan-drupal will include a stub for FilterProcessResult to define
$callableascallable-string, fixing the one reported error here. See https://github.com/mglaman/phpstan-drupal/pull/453Comment #41
andyposthttps://wiki.php.net/rfc/partially-supported-callables-expand-deprecatio... accepted
Comment #42
andypostFiled bug for PHP 8.2 #3299327-3: Replace deprecated static::method() calls for PHP 8.2
https://wiki.php.net/rfc/deprecate_partially_supported_callables
btw the recommended variant of Callable is
Comment #43
andypostThere's even Rector rule https://github.com/rectorphp/rector-src/pull/2544 and follow-up so
rectorphp >=0.13.8is requiredComment #44
spokjeAs far as I can tell, this issue is now outdated, since all the (false) positives in PHPStan are gone since #3299606: Update mglaman/phpstan-drupal to 1.1.25 to unblock testing on PHP 8.2 landed.
Comment #45
andypost@Spokje please check #42 - I'm sure we must normalize callables as 10+ requires PHP 8.1
I did not update IS because not clear how to apply rector rule to convert'em all
Anyway it should be postponed on #3299327: Replace deprecated static::method() calls for PHP 8.2
Comment #46
spokjeI looked at #37 when I closed this one. I'll leave it as it is now, because I'm lost at this point.
Comment #47
longwavestatic::class . "::method"is the format used here, and this is fine; this is suggested in the RFC as a replacement forstatic::method. I don't think we need to do anything here, even for future PHP versions - only #3299327: Replace deprecated static::method() calls for PHP 8.2.Comment #48
andypost@longwave the suggested method is to use first-class callable since 8.1 as it's more optimized (require no conversion from array to callable so improve performance)
Comment #49
andypostre-title to make it clear and updated IS
Comment #50
andypostThere's rector for that https://github.com/rectorphp/rector-src/commit/fe88fbb6306cec3728b9158fd...
So I filed #3300804: Add rector for PHP 8.2 callables
Comment #51
andypostParent issue commited and there's related follow-up #3309750: Fix callable syntax for PHP 8.2 in Views.php
Comment #52
andypostlast blocker for PHP 8.2 commited, so this can be commited as improvement only to 10.1
Comment #53
andypostHere's a start patch
Comment #54
andypostAnd here conversion of "static::" and "self::" I found (surely needs rector)
Meantime discovered bug, so callables help #3318581: FIx \Drupal\Core\Config\ConfigImporter::doSyncStep() to accept callable
Comment #55
andypostpatch #54 for 10.1.x (some offset)
Comment #57
andypostFix failed test
Comment #58
andypostUse callable in module handler instead of
call_user_func_arrayComment #59
nod_Should this form be changed also?
into something like
Not sure I get all the implications. In any case the patch from #58 looks good
Comment #60
mondrakeThis doesn't look like it's related to PHPStan anymore.
Comment #61
andypost@nod_ it's great question but closures are not serializable so it should break render cache, I see a lot of usage in render element classes so they should remain serializable for form-api purposes.
Comment #62
nod_Thanks a lot for the explanations :)
\Drupal\user\Entity\Role::postLoadcould be update fromuasort($entities, [static::class, 'sort']);uasort($entities, static::sort(...));?\Drupal\migrate_drupal_ui\Batch\MigrateUpgradeImportBatch::runmight have some, it's for batch related things so it might not be possible because of a serialization thing\Drupal\Core\Mail\MailFormatHelper::htmlToTextthe call to$string = preg_replace_callback($pattern, [static::class, 'htmlToMailUrls'], $string);could be updated?\Drupal\views\Plugin\views\filter\Date::hasValidGroupedValuethe call to$actual = count(array_filter($group['value'], [static::class, 'arrayFilterZero']));could be updated?All the other
static::classi could see were used in render arrays or in tests so per #61 they should be left alone.Saw a couple of
array_map([static::class, __FUNCTION__], $merged)in json API module, or'callback' => [static::class, 'doesNotExist']in tests but I'm not sure those are relevant for this issue.Comment #63
kim.pepperHere's patch #58 for Drupal 9.5.x
Comment #64
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It either no longer applies to Drupal core, or fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
Apart from a re-roll or rebase, this issue may need more work to address feedback in the issue or MR comments. To progress an issue, incorporate this feedback as part of the process of updating the issue. This helps other contributors to know what is outstanding.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #65
andypostRe-roll after #3299327: Replace deprecated static::method() calls for PHP 8.2
Comment #66
andypostAnd 2 more places found
Comment #67
andypostAnd a bit more for
git grep "'strtolower'"andgit grep "'array_merge'"Comment #68
ahmadhalahTested manually it works on Drupal core 9.5.6
Comment #69
smustgrave commented@ahmadhalah patch #67 applied cleanly to D10 and patch #68 seems to be missing bulk of changes.
Applied patch #67
Searched for [static::class,
There are 82 matches still.
Could it be added to the issue summary specifically what is being replaced and what isn't?
Comment #70
ameymudras commentedReplacing a few more instances of static::class
Comment #71
smustgrave commentedI don’t know if replacing more is the answer. Should be documented in IS what is being replaced though
Comment #72
ahmadhalah@smustgrave patch #68 works with Drupal 9.6.5. Did you test it on that version?
Comment #73
anchal_gupta commentedFixed CCF
Comment #74
smustgrave commentedHiding files up till #67
@andypost could you update what is being replaced in the IS please.
Comment #75
andypostnew candidates from #3351004: Deprecations for PHP 8.1 get_class() and get_called_class() without argument
@smustgrave it looks like it needs split as growing, basically all places that are not supposed to be serializable can benefit from that
Comment #76
dpiLooks like we're addressing array-callable, not just string callable. And
\Closure::fromCallable, others?Issue summary needs update, title could lose the mention of string.
Comment #77
dpiMarked #3354146: Role::postLoad() warning on every load as duplicate.
Comment #78
kim.pepperComment #80
donquixote commentedWe should be careful with this.
Readable, yes.
Also, closures are _verified_. Once we have a closure object, we know it can be called. The 'Call to undefined function' error happens when the closure is created, not when it is called - which makes for nicer error detection.
But:
More performant - yes, but it depends.
Also it is not serializable - but this is only relevant for caching between requests.
I created this script to compare different ways of calling different types of callbacks:
https://gist.github.com/donquixote/85efcca90056111e967dd14cb1f9de9c
It can be a good idea to copy the result into a spreadsheet app and sort by different columns.
The results could be different depending on php configuration etc. Also I found some unexpected side effects from changing the total number of repetitions and the order of comparison candidates.
Conclusions:
E.g. the way we currently do it in ModuleHandler actually makes things slower, not faster, because we do not reuse the $hookInvoker.
It would be faster to pass the callable-string directly:
The performance argument does not need to stop us, there can be sufficient other reasons to use first-class callables.
However, if we want to use performance as an argument _for_ first-class callables, we need to look more closely.
Comment #81
donquixote commentedWe can do this, but from what I found it will be slower, not faster. Imo in a case like this, a literal string is totally fine, and looks even simpler than the first-class callable. It is even less ambiguous because it is automatically in the root namespace.
Also, the IDE (or a static analysis tool) sees that the parameter is callable, so it can know that the literal string refers to a function.
Should the form and render array stuff not be serializable?
Maybe in some cases it doesn't need to be.
I think a raw original form is cached, but a processed form is not. So maybe it's fine.
This being said, the first-class callables do look nicer than the callable arrays. So for these I totally see the point, whenever it works.
This is missing brackets.