11

abstract_trait_method_validation

 3 years ago
source link: https://wiki.php.net/rfc/abstract_trait_method_validation
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

PHP RFC: Validation for abstract trait methods

Introduction

Traits can contain abstract methods, which are used to specify requirements the trait has on the using class. However, the signatures of these methods are currently only erratically enforced.

This RFC proposes to verify abstract trait methods against the implementation provided in the using class, according to our usual inheritance rules. Additionally the RFC allows the declaration of abstract private methods in traits.

trait MyTrait {
    abstract private function neededByTheTrait(): string;
 
    public function doSomething() {
        return strlen($this->neededByTheTrait());
    }
}
 
class TraitUser {
    use MyTrait;
 
    // This is allowed:
    private function neededByTheTrait(): string { }
 
    // This is forbidden (incorrect return type)
    private function neededByTheTrait(): stdClass { }
 
    // This is forbidden (non-static changed to static)
    private static function neededByTheTrait(): string { }
}

Proposal

Status Quo

Abstract trait method signatures are currently not enforced in likely the most common case, where the method implementation is provided directly by using class:

trait T {
    abstract public function test(int $x);
}
 
class C {
    use T;
 
    // Allowed, but shouldn't be due to invalid type.
    public function test(string $x) {}
}

The method signature is enforced if the implementing method comes from a parent class:

trait T {
    abstract public function test(int $x);
}
 
class P {
    public function test(string $x) {}
}
 
class C extends P {
    use T;
 
    // Not allowed, because P::test() incompatible with T::test().
}

The method signature is also enforced if the implementing method comes from a child class:

trait T {
    abstract public function test(int $x);
}
 
abstract class P {
    use T;
}
 
class C extends P {
    // Not allowed, due to type mismatch.
    public function test(string $x) {}
}

Finally, if multiple traits define the same abstract method, then the signature is enforced bidirectionally between the traits (but not the implementing class):

trait T1 {
    abstract public function test($x);
}
 
trait T2 {
    abstract public function test($x): int;
}
 
class C {
    // Invalid, because T1::test() incompatible with T2::test().
    use T1, T2;
 
    public function test($x): int {}
}

This enforcement is incorrect: The method implementation provided by the class is compatible with both traits. As such, this should be legal code.

Proposal

This RFC proposes to always validate the signature of abstract trait methods against the implementing method, independently of its origin. Additionally the incorrect bidirectional cross-trait compatibilty check from the last example is removed.

A fatal error is generated if the implementing method is not compatible with the abstract trait method, where compatibility entails:

  • The signature must be compatible, which includes arity compatibility, contravariant parameter type compatibility and covariant return type compatibility.
  • The static-ness of the methods must match.

Additionally, this RFC allows the declaration of abstract private methods in traits only. Normally abstract private methods are a contradiction in terms, because the method providing the implementation would not be visible from the class issuing the requirement. However, abstract private methods are well-defined inside traits, because trait methods have access to private methods of the using class.

Private abstract methods must be implemented by the using class. Their implementation cannot be postponed by marking the class abstract, as this would once again render the implementation inaccessible.

Contrary to the usual inheritance rules, the visibility level of the abstract trait method is not enforced. This means that an abstract protected method in the trait can be implemented by a private method in the class, even though this reduces visibility. This exception is added for backwards-compatibility reasons: Because abstract private methods were forbidden prior to this proposal, a private requirement for the trait could not be specified with private visibility. This exception allows code to use abstract protected trait methods with private implementations and thus be compatible with both PHP 7 and PHP 8. Code that only needs to be compatible with PHP 8 should prefer the use of abstract private methods for clarity.

Backward Incompatible Changes

Code that currently declares abstract trait methods and implements them with an incorrect signature in a using class will break. Such code can be fixed by either fixing the method signature in the using class, or by removing the abstract method from the trait.

Voting opened 2020-03-06 and closes 2020-03-20.

Validate abstract trait methods? Real name Yes No ajf (ajf) success.png  alcaeus (alcaeus) success.png  alec (alec) success.png  as (as) success.png  ashnazg (ashnazg) success.png  beberlei (beberlei) success.png  brzuchal (brzuchal) success.png  bwoebi (bwoebi) success.png  carusogabriel (carusogabriel) success.png  cmb (cmb) success.png  colinodell (colinodell) success.png  daverandom (daverandom) success.png  derick (derick) success.png  diegopires (diegopires) success.png  ekin (ekin) success.png  galvao (galvao) success.png  girgias (girgias) success.png  googleguy (googleguy) success.png  jhdxr (jhdxr) success.png  kalle (kalle) success.png  kguest (kguest) success.png  kocsismate (kocsismate) success.png  lcobucci (lcobucci) success.png  levim (levim) success.png  malukenho (malukenho) success.png  marandall (marandall) success.png  mariano (mariano) success.png  mcmic (mcmic) success.png  narf (narf) success.png  nicolasgrekas (nicolasgrekas) success.png  nikic (nikic) success.png  ocramius (ocramius) success.png  pmjones (pmjones) success.png  pmmaga (pmmaga) success.png  pollita (pollita) success.png  ralphschindler (ralphschindler) success.png  ramsey (ramsey) success.png  rasmus (rasmus) success.png  reywob (reywob) success.png  ruudboon (ruudboon) success.png  santiagolizardo (santiagolizardo) success.png  sebastian (sebastian) success.png  sergey (sergey) success.png  sirsnyder (sirsnyder) success.png  stas (stas) success.png  svpernova09 (svpernova09) success.png  tandre (tandre) success.png  trowski (trowski) success.png  villfa (villfa) success.png  wyrihaximus (wyrihaximus) success.png  yunosh (yunosh) success.png  zimt (zimt) success.png  Final result: 52 0 This poll has been closed.

rfc/abstract_trait_method_validation.txt · Last modified: 2020/04/23 10:49 by nikic

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK