Replace usages of static::class . '::methodName' to first-class callable syntax static::method(...)

Created on 21 January 2022, over 3 years ago
Updated 13 June 2023, almost 2 years ago

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

๐Ÿ“Œ Task
Status

Needs work

Version

11.0 ๐Ÿ”ฅ

Component
Otherย  โ†’

Last updated about 18 hours ago

Created by

๐Ÿ‡ง๐Ÿ‡ชBelgium mallezie Loenhout

Live updates comments and jobs are added and updated live.
  • PHP 8.2

    The issue particularly affects sites running on PHP version 8.2.0 or later.

  • PHP 8.1

    The issue particularly affects sites running on PHP version 8.1.0 or later.

  • Needs issue summary update

    Issue summaries save everyone time if they are kept up-to-date. See Update issue summary task instructions.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • The 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.

  • Status changed to Needs review about 2 years ago
  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    And 2 more places found

  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    And a bit more for git grep "'strtolower'" and git grep "'array_merge'"

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States ahmadhalah

    Tested manually it works on Drupal core 9.5.6

  • Status changed to Needs work about 2 years ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    @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?

  • Status changed to Needs review about 2 years ago
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia ameymudras

    Replacing a few more instances of static::class

  • Status changed to Needs work about 2 years ago
  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave

    I donโ€™t know if replacing more is the answer. Should be documented in IS what is being replaced though

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States ahmadhalah

    @smustgrave patch #68 works with Drupal 9.6.5. Did you test it on that version?

  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia anchal_gupta

    Fixed CCF

  • ๐Ÿ‡บ๐Ÿ‡ธUnited States smustgrave
  • ๐Ÿ‡ซ๐Ÿ‡ทFrance andypost

    new candidates from ๐Ÿ“Œ Deprecations for PHP 8.1 get_class() and get_called_class() without argument Fixed

    @smustgrave it looks like it needs split as growing, basically all places that are not supposed to be serializable can benefit from that

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia dpi Perth, Australia

    Looks 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.

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia dpi Perth, Australia

    Marked ๐Ÿ› Role::postLoad() warning on every load Closed: duplicate as duplicate.

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia kim.pepper ๐Ÿ„โ€โ™‚๏ธ๐Ÿ‡ฆ๐Ÿ‡บSydney, Australia
  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany donquixote

    We should be careful with this.

    Replace usages of [static::class, '::methodName'] to static::methodName(...) which is more readable and performant since PHP 8.1

    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:

  • For any callable value, If we want to call it only one time, it is faster to call it directly than to first convert it to a closure object.
  • If we have a closure, the cost of calling is almost the same no matter if it is a function or a class (static) method. (if they have the same parameter signature).
  • Calling a closure is generally faster than calling a callable-string or callable-array, if we don't include the time for converting it to a closure.
  • Time can be lost for the extra step of converting to closure. So it is only worthwhile if we call it repeatedly, or .
  • E.g. the way we currently do it in ModuleHandler actually makes things slower, not faster, because we do not reuse the $hookInvoker.

      public function invokeAllWith(string $hook, callable $callback): void {
        foreach (array_keys($this->getImplementationInfo($hook)) as $module) {
          $hookInvoker = \Closure::fromCallable($module . '_' . $hook);
          $callback($hookInvoker, $module);
        }
      }
    

    It would be faster to pass the callable-string directly:

      public function invokeAllWith(string $hook, callable $callback): void {
        foreach (array_keys($this->getImplementationInfo($hook)) as $module) {
          $callback($module . '_' . $hook, $module);
        }
      }
    

    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.

  • ๐Ÿ‡ฉ๐Ÿ‡ชGermany donquixote
    -    $map = array_map('strtolower', $connection->schema()->getFieldTypeMap());
    +    $map = array_map(strtolower(...), $connection->schema()->getFieldTypeMap());
    

    We 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.

    -    $element['#element_validate'][] = [static::class, 'validateElement'];
    +    $element['#element_validate'][] = static::validateElement(...);
    

    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.

    -        '#element_validate' => [[static::class, 'elementValidateFilter']],
    +        '#element_validate' => static::elementValidateFilter(...),
    

    This is missing brackets.

  • Production build 0.71.5 2024