Traits

General

PHP’s class model allows single inheritance only with contracts being enforced separately via interfaces. A trait can provide both implementation and contracts. Specifically, a class can inherit from a base class while also using code from one or more traits. At the same time, that class can implement contracts from one or more interfaces as well as from one or more traits. The use of a trait by a class does not involve any inheritance hierarchy, so unrelated classes can use the same trait. In summary, a trait is a set of methods and/or state information that can be reused.

Traits are designed to support classes; a trait cannot be instantiated directly.

The members of a trait each have visibility, which applies once they are used by a given class. The class that uses a trait can change the visibility of any of that trait’s members, by either widening or narrowing that visibility. For example, a private trait member can be made public in the using class, and a public trait member can be made private in that class.

Once implementation comes from both a base class and one or more traits, name conflicts can occur. However, trait usage provides the means for disambiguating such conflicts. Names gotten from a trait can also be given aliases.

A class member with a given name overrides one with the same name in any traits that class uses, which, in turn, overrides any such name from base classes.

Traits can contain both instance and static members, including both methods and properties. In the case of a trait with a static property, each class using that trait has its own instance of that property.

Methods in a trait have full access to all members of any class in which that trait is used.

Trait Declarations

Syntax

trait-declaration:
   trait   name   {   trait-member-declarationsopt   }

trait-member-declarations:
   trait-member-declaration
   trait-member-declarations   trait-member-declaration

trait-member-declaration:
   property-declaration
   method-declaration
   constructor-declaration
   destructor-declaration
   trait-use-clauses

Semantics

A trait-declaration defines a named set of members, which are made available to any class that uses that trait.

Trait names are case-insensitive.

The members of a trait are those specified by its trait-member-declaration clauses, and members imported from any other traits using trait-use-clauses.

A trait may contain the following members:

  • Properties – the variables made available to the class in which the trait is used.
  • Methods – the computations and actions that can be performed by the class in which the trait is used.
  • Constructor – the actions required to initialize an instance of the class in which the trait is used.
  • Destructor – the actions to be performed when an instance of the class in which the trait is used is no longer needed.

If a member has no explicit visibility, public is assumed.

Examples

trait T
{
  private $prop1 = 1000;
  protected static $prop2;
  var $prop3;
  public function compute( ... ) { ... }
  public static function getData( ... ) { ... }
}

Trait Uses

Syntax

trait-use-clauses:
   trait-use-clause
   trait-use-clauses   trait-use-clause

trait-use-clause:
   use   trait-name-list   trait-use-specification

trait-name-list:
   qualified-name
   trait-name-list   ,   qualified-name

trait-use-specification:
   ;
   {   trait-select-and-alias-clausesopt   }

trait-select-and-alias-clauses:
   trait-select-and-alias-clause
   trait-select-and-alias-clauses   trait-select-and-alias-clause

trait-select-and-alias-clause:
   trait-select-insteadof-clause   ;
   trait-alias-as-clause   ;

trait-select-insteadof-clause:
   qualified-name   ::   name   insteadof   trait-name-list

trait-alias-as-clause:
   name   as   visibility-modifieropt   name
   name   as   visibility-modifier   nameopt

Constraints

The name items in trait-name-list must designate trait names, excluding the name of the trait being declared.

The left-hand name in trait-select-insteadof-clause must unambiguously designate a member of a trait made available by trait-use-clauses. The right-hand name in trait-select-insteadof-clause must unambiguously designate a trait made available by trait-use-clauses.

The left-hand name in trait-alias-as-clause must unambiguously designate a member of a trait made available by trait-use-clauses. The right-hand name in trait-alias-as-clause must be a new, unqualified name.

Semantics

trait-use-clauses can be used as part of trait-member-declarations or class-member-declarations to import members of a trait into a different trait or a class. This is done via one or more trait-use-clause items, each of which contains a comma-separated list of trait names. A trait-use-clause list ends in a semicolon or a brace-delimited set of trait-select-insteadof-clause and trait-alias-as-clause statements.

A trait-select-insteadof-clause allows to avoid name clashes. Specifically, the left-hand name designates which name to be used from of a pair of names. That is, T1::compute insteadof T2; indicates that calls to method compute, for example, should be satisfied by a method of that name in trait T1 rather than T2.

A trait-alias-as-clause allows a (possibly qualified) name to be assigned a simple alias name. Specifically, the left-hand name in trait-alias-as-clause designates a name made available by trait-use-clauses - that is to be aliased, and the right-hand name is the alias.

If trait-alias-as-clause contains a visibility-modifier, if a right-hand name is provided, the modifier controls the visibility of the alias, otherwise, it controls the visibility of the left-hand name.

Examples

trait T1 { public function compute( ... ) { ... } }
trait T2 { public function compute( ... ) { ... } }
trait T3 { public function sort( ... ) { ... } }
trait T4
{
  use T3;
  use T1, T2
  {
    T1::compute insteadof T2; // disambiguate between two computes
    T3::sort as private sorter; // make alias with adjusted visibility
  }
}