Document how to add additional parameters to interface methods

Created on 17 April 2023, over 1 year ago
Updated 15 September 2023, over 1 year ago

Problem/Motivation

Sometimes, interfaces are wrong šŸ“Œ Fix PHPStan L1 errors "Call to method getDefinitions()/getSortedDefinitions() on an unknown class Drupal\Core\Plugin\CategorizingPluginManagerTrait." Needs work . Sometimes, you may want to add parameters to existing methods. Sometimes, you may want to add or change typehints to arguments or return values.

How to do this in Drupal given its BC policy that does not allow this even in majors?

Cases to cover:

  1. adding a method to the interface -> already covered
  2. removing a method from the interface -> already covered
  3. adding parameter(s) to existing method --> covered by Adding arguments to interface methods ā†’
  4. removing parameter(s) from existing method -> already covered
  5. adding typehint(s) to method parameter(s) -> see #19
  6. changing typehint(s) to method parameter(s) -> see #19

Proposed resolution

Changes arguments or typehints of interface methods

Leveraging on Symfony's DebugClassLoader, it's now possible to add arguments to and change signatures of methods in interfaces in a major release.

Since it's normally a BC break to introduce new arguments (because any implementing class that does not comply with the changed signature would fail at loading), this is a two steps process.

This pattern is applicable on all argument changes. For example, changing the argument order of a method.

During the current release cycle, introduce the argument change(s) in an inline comment, for example:

-  public function foo($bar);
+  public function foo($bar /* , BazInterface $baz */);

Or change the typehint of an argument, for example:

-  public function foo(string $bar);
+  public function foo(/* string|Stringable $bar */);

Document the changes in the docblock of the method, inside a phpcs:disable block to prevent PHPCS from firing an error when parsing the signature of the method; in the comments include a @see to a follow-up issue that will take care of actually doing the change in the next major release:

    * phpcs:disable Drupal.Commenting
    * @todo Uncomment new method parameters before drupal:11.0.0.
    * @see https://www.drupal.org/project/drupal/issues/3354672
    *
    * @param BazInterface $baz
    *   Documentation for parameter $baz.
    * phpcs:enable

Tag the follow-up issue with 'Major version only'.

This will allow the testing framework to trigger deprecation errors for the implementing classes that do not have the new signature in place yet, but because PHP allows methods to add additional non-interface arguments without errors, classes can immediately update. In order to prevent tests fail because of that, add an ignore line in .deprecation-ignore.txt, like e.g.

# Drupal 11.
%Foo::foo\(\).* will require a new "BazInterface \$baz" argument in the next major version of its interface%
%Bar::bar\(\).* will require a new "BazInterface \$baz" argument in the next major version of its interface%

Once the branch opens for issues for new major release issues:

  1. Remove the inline comment from the interface, exposing the new full signature:
  2. -  public function foo($bar /* , BazInterface $baz */);
    +  public function foo($bar, BazInterface $baz);
    

    or

    -  public function foo(/* string|Stringable $bar */);
      +  public function foo(string|Stringable $bar);
    
  3. remove the PHPCS ignore and @todo from the docblock,
  4. implement the new signature in the concrete classes
  5. remove the ignore line from the .deprecation-ignore.txt file.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

šŸŒ± Plan
Status

Fixed

Version

11.0 šŸ”„

Component
BaseĀ  ā†’

Last updated about 2 hours ago

Created by

šŸ‡®šŸ‡¹Italy mondrake šŸ‡®šŸ‡¹

Live updates comments and jobs are added and updated live.
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.

  • Issue created by @mondrake
  • šŸ‡¬šŸ‡§United Kingdom catch

    If we can do šŸ“Œ Remove the debug classloader deprecation silencing for Drupal interfaces Closed: duplicate then I think the next step would be to add docs to https://www.drupal.org/about/core/policies/core-change-policies/bc-policy ā†’ on how to use it to add parameters to interface methods.

    For adding/changing type hints we could probably do something like removing a parameter then adding it back again, but should be separate issues probably.

  • šŸ‡®šŸ‡¹Italy mondrake šŸ‡®šŸ‡¹

    Add to IS a list of cases to cover. Anything else?

  • šŸ‡®šŸ‡¹Italy mondrake šŸ‡®šŸ‡¹

    In my view, #2 covers case 1 from the IS, #5 covers case 5.

    Would be good to have a position for all the cases.

  • šŸ‡¬šŸ‡§United Kingdom catch

    adding a method to the interface
    removing a method from the interface

    These are already covered in the existing bc policy, IMO we should not bundle things into one issue but handle one at a time where there are deficiencies.

  • šŸ‡¬šŸ‡§United Kingdom catch

    removing parameter(s) from existing method

    this is also already covered by the existing bc policy.

  • šŸ‡®šŸ‡¹Italy mondrake šŸ‡®šŸ‡¹
  • šŸ‡¬šŸ‡§United Kingdom catch

    šŸ“Œ Prepare FormBuilder for variadic functions Fixed did exactly what we need to document here, so I think the next step is adding that as an example to the bc policy at https://www.drupal.org/about/core/policies/core-change-policies/drupal-d... ā†’

  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Ok, to get this rolling i attacked a proposed addition to the core page on deprecations.

  • Status changed to Needs review over 1 year ago
  • last update over 1 year ago
    Patch Failed to Apply
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    ANd readable text:

    ā€‹
    Adding arguments to interface methods
    Leveraging on Symfony's DebugClassLoader, it's now possible to add arguments to methods in interfaces in next major release.

    Since it's normally a BC break to introduce new arguments (because any implementing class that does not comply with the changed signature would fail at loading), this is a two steps process.

    Step 1 - prepare the new signature
    During the current release cycle, introduce the new argument(s) in an inline comment, for example:

    - public function submitForm($form_arg, FormStateInterface &$form_state);
    + public function submitForm($form_arg, FormStateInterface &$form_state /* , mixed ...$args */);

    Document the new argument(s) in the docblock of the method, inside an PHPCS ignore block to prevent PHPCS to fire an error when parsing the signature of the method; in the comments include a @see to a follow-up issue that will take care of actually doing the change in the next major release:

    * phpcs:disable Drupal.Commenting
    * @todo Uncomment new method parameters before drupal:11.0.0.
    * @see https://www.drupal.org/project/drupal/issues/3354672 šŸ“Œ [11.x] Adjust parameters in interfaces Active
    *
    * @param mixed ...$args
    * Any additional arguments are passed on to the functions called by
    * \Drupal::formBuilder()->getForm(), including the unique form constructor
    * function. For example, the node_edit form requires that a node object is
    * passed in here when it is called. These are available to implementations
    * of hook_form_alter() and hook_form_FORM_ID_alter() as the array
    * $form_state->getBuildInfo()['args'].
    * phpcs:enable

    Tag the follow-up issue with 'Major version only'.

    This will let the testing framework trigger deprecation errors for the implementing classes that do not have, yet, the to-be signature in place. In order to prevent tests fail because of that, add an ignore line in .deprecation-ignore.txt, like e.g.

    # Drupal 11.
    %FormBuilder::getForm\(\).* will require a new "mixed \.\.\. \$args" argument in the next major version of its interface%
    %FormBuilder::submitForm\(\).* will require a new "mixed \.\.\. \$args" argument in the next major version of its interface%

    Step 2 - implement the new signature
    Once the branch opens for issues for new major release issues:

    Remove the inline comment from the interface, exposing the new full signature:
    - public function submitForm($form_arg, FormStateInterface &$form_state /* , mixed ...$args */);
    + public function submitForm($form_arg, FormStateInterface &$form_state, mixed ...$args);

    remove the PHPCS ignore and @todo from the docblock,
    implement the new signature in the concrete classes
    remove the ignore line from the .deprecation-ignore.txt file.
    ā€‹

  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Updated covered bullet points as pointed out by catch

  • Status changed to Needs work over 1 year ago
  • 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 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.

  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Suggestion by @catch is to use something like:

    -  public function foo(OldInterface $bar)
    +  public function foo(/** OldInterface|NewInterface $bar */)
    

    For changing interface typehints. Was trying to write this, but need to check it with some real code. Also not sure if if this would need to be an example as above of perhaps:

    -  public function foo(array $bar)
    +  public function foo(/** NewInterface $bar */)
    

    That perhaps feels a little more realistic in context of Drupal code.

  • Status changed to Needs review over 1 year ago
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Ok, lets see, this seems like an good idea (tm). Added example for changing method signature:

    Or change the typehint of an argument, for example:

    -  public function foo(string $bar);
    +  public function foo(/* string|Stringable $bar */);
    
    

    Document the changes in the docblock of the method, inside an PHPCS ignore block to prevent PHPCS to fire an error when parsing the signature of the method; in the comments include a @see to a follow-up issue that will take care of actually doing the change in the next major release:

        * phpcs:disable Drupal.Commenting
        * @todo Uncomment new method parameters before drupal:11.0.0.
        * @see https://www.drupal.org/project/drupal/issues/3354672
        *
        * @param mixed ...$args
        *   Any additional arguments are passed on to the functions called by
        *   \Drupal::formBuilder()->getForm(), including the unique form constructor
        *   function. For example, the node_edit form requires that a node object is
        *   passed in here when it is called. These are available to implementations
        *   of hook_form_alter() and hook_form_FORM_ID_alter() as the array
        *   $form_state->getBuildInfo()['args'].
        * phpcs:enable
    

    Tag the follow-up issue with 'Major version only'.

    This will let the testing framework trigger deprecation errors for the implementing classes that do not have, yet, the to-be signature in place. In order to prevent tests fail because of that, add an ignore line in .deprecation-ignore.txt, like e.g.

    # Drupal 11.
    %FormBuilder::getForm\(\).* will require a new "mixed \.\.\. \$args" argument in the next major version of its interface%
    %FormBuilder::submitForm\(\).* will require a new "mixed \.\.\. \$args" argument in the next major version of its interface%
    

    Once the branch opens for issues for new major release issues:

    1. Remove the inline comment from the interface, exposing the new full signature:
    2. -  public function submitForm($form_arg, FormStateInterface &$form_state /* , mixed ...$args */);
      +  public function submitForm($form_arg, FormStateInterface &$form_state, mixed ...$args);
      

      or

      -  public function foo(/* string|Stringable $bar */);
        +  public function foo(string|Stringable $bar);
      
      
    3. remove the PHPCS ignore and @todo from the docblock,
    4. implement the new signature in the concrete classes
    5. remove the ignore line from the .deprecation-ignore.txt file.
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Updated IS with proposed solution

  • šŸ‡¬šŸ‡§United Kingdom catch
  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Update propopsed solution to only used fake classes/interfaces/methods/arguments

  • Status changed to RTBC over 1 year ago
  • šŸ‡¬šŸ‡§United Kingdom catch

    Talked with @bbrala about this in slack, I think the proposed wording is really clear, and covers as many cases as we could think of, so ready to go for me.

    RTBC and tagging for documentation updates.

  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Documentation has been updated ā†’ .

  • šŸ‡¬šŸ‡§United Kingdom catch

    Docs changes look good but I wonder if we should move it directly under https://www.drupal.org/about/core/policies/core-change-policies/drupal-d... ā†’ since it's dealing with parameters?

  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Hmm, both could be right, the placement right now is classes -> interfaces.

    I don't mind either way, both make sense in my mind.

  • šŸ‡¬šŸ‡§United Kingdom joachim

    > %FormBuilder::getForm\(\).* will require a new "mixed \.\.\. \$args" argument in the next major version of its interface%

    Is the idea that implementations/subclasses can add the parameters as optional right away, then then they're compatible with both D10 (without the param) and D11 (with the param)?

    I'm assuming this message isn't under our control -- but it would be nice if it said that or if we documented that somewhere.

  • šŸ‡³šŸ‡±Netherlands bbrala Netherlands

    Think you might wanna look at the is, that's close to what was actually posted. Does the doc page still show that form example? Thought I generalised it.

  • šŸ‡¬šŸ‡§United Kingdom joachim

    AFAICT, the IS doesn't explain how implementors should react to the deprecation.

    And the IS says how to ignore the deprecation warning in a test, but it doesn't say where the warning is generated.

  • šŸ‡¬šŸ‡§United Kingdom catch

    The warning is generated by Symfony's debug classloader (and this is why we have no control over the contents).

    Implementors should add the extra parameter or type hint to their current codebase - PHP won't complain and it'll be forwards compatible for when the interface changes in the next major release.

  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • last update over 1 year ago
    Patch Failed to Apply
  • Status changed to Fixed over 1 year ago
  • šŸ‡ŗšŸ‡øUnited States xjm
  • šŸ‡³šŸ‡æNew Zealand quietone

    I think that #27 and #28, about the placement of the new section on the page, has been resolved. xjm moved it to it's own heading, added the links to the tag and other tidying up. Adding credit for xjm for that work.

    The new addition is at Interface method signature changes ā†’ .

    Thanks everyone!

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.71.5 2024