mirror of
https://github.com/sigmasternchen/php-doc-en
synced 2025-03-15 16:38:54 +00:00

git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@333195 c90b9560-bf6c-de11-be94-00142212c4b1
484 lines
11 KiB
XML
484 lines
11 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<sect1 xml:id="language.oop5.traits" xmlns="http://docbook.org/ns/docbook">
|
|
<title>Traits</title>
|
|
<para>
|
|
As of PHP 5.4.0, PHP implements a method of code reuse called Traits.
|
|
</para>
|
|
<para>
|
|
Traits are a mechanism for code reuse in single inheritance languages such as
|
|
PHP. A Trait is intended to reduce some limitations of single inheritance by
|
|
enabling a developer to reuse sets of methods freely in several independent
|
|
classes living in different class hierarchies. The semantics of the combination
|
|
of Traits and classes is defined in a way which reduces complexity, and avoids
|
|
the typical problems associated with multiple inheritance and Mixins.
|
|
</para>
|
|
<para>
|
|
A Trait is similar to a class, but only intended to group functionality in a
|
|
fine-grained and consistent way. It is not possible to instantiate a Trait on
|
|
its own. It is an addition to traditional inheritance and enables horizontal
|
|
composition of behavior; that is, the application of class members without
|
|
requiring inheritance.
|
|
</para>
|
|
|
|
<example xml:id="language.oop5.traits.basicexample">
|
|
<title>Trait example</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait ezcReflectionReturnInfo {
|
|
function getReturnType() { /*1*/ }
|
|
function getReturnDescription() { /*2*/ }
|
|
}
|
|
|
|
class ezcReflectionMethod extends ReflectionMethod {
|
|
use ezcReflectionReturnInfo;
|
|
/* ... */
|
|
}
|
|
|
|
class ezcReflectionFunction extends ReflectionFunction {
|
|
use ezcReflectionReturnInfo;
|
|
/* ... */
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
|
|
<sect2 xml:id="language.oop5.traits.precedence">
|
|
<title>Precedence</title>
|
|
<para>
|
|
An inherited member from a base class is overridden by a member inserted
|
|
by a Trait. The precedence order is that members from the current class
|
|
override Trait methods, which in turn override inherited methods.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.precedence.examples.ex1">
|
|
<title>Precedence Order Example</title>
|
|
<para>
|
|
An inherited method from a base class is overridden by the
|
|
method inserted into MyHelloWorld from the SayWorld Trait. The behavior is
|
|
the same for methods defined in the MyHelloWorld class. The precedence order
|
|
is that methods from the current class override Trait methods, which in
|
|
turn override methods from the base class.
|
|
</para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
class Base {
|
|
public function sayHello() {
|
|
echo 'Hello ';
|
|
}
|
|
}
|
|
|
|
trait SayWorld {
|
|
public function sayHello() {
|
|
parent::sayHello();
|
|
echo 'World!';
|
|
}
|
|
}
|
|
|
|
class MyHelloWorld extends Base {
|
|
use SayWorld;
|
|
}
|
|
|
|
$o = new MyHelloWorld();
|
|
$o->sayHello();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Hello World!
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example xml:id="language.oop5.traits.precedence.examples.ex2">
|
|
<title>Alternate Precedence Order Example</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait HelloWorld {
|
|
public function sayHello() {
|
|
echo 'Hello World!';
|
|
}
|
|
}
|
|
|
|
class TheWorldIsNotEnough {
|
|
use HelloWorld;
|
|
public function sayHello() {
|
|
echo 'Hello Universe!';
|
|
}
|
|
}
|
|
|
|
$o = new TheWorldIsNotEnough();
|
|
$o->sayHello();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Hello Universe!
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.multiple">
|
|
<title>Multiple Traits</title>
|
|
<para>
|
|
Multiple Traits can be inserted into a class by listing them in the use
|
|
statement, separated by commas.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.multiple.ex1">
|
|
<title>Multiple Traits Usage</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait Hello {
|
|
public function sayHello() {
|
|
echo 'Hello ';
|
|
}
|
|
}
|
|
|
|
trait World {
|
|
public function sayWorld() {
|
|
echo 'World';
|
|
}
|
|
}
|
|
|
|
class MyHelloWorld {
|
|
use Hello, World;
|
|
public function sayExclamationMark() {
|
|
echo '!';
|
|
}
|
|
}
|
|
|
|
$o = new MyHelloWorld();
|
|
$o->sayHello();
|
|
$o->sayWorld();
|
|
$o->sayExclamationMark();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Hello World!
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.conflict">
|
|
<title>Conflict Resolution</title>
|
|
<para>
|
|
If two Traits insert a method with the same name, a fatal error is produced,
|
|
if the conflict is not explicitly resolved.
|
|
</para>
|
|
<para>
|
|
To resolve naming conflicts between Traits used in the same class,
|
|
the <literal>insteadof</literal> operator needs to be used to chose exactly
|
|
one of the conflicting methods.
|
|
</para>
|
|
<para>
|
|
Since this only allows one to exclude methods, the <literal>as</literal>
|
|
operator can be used to allow the inclusion of one of the conflicting
|
|
methods under another name.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.conflict.ex1">
|
|
<title>Conflict Resolution</title>
|
|
<para>
|
|
In this example, Talker uses the traits A and B.
|
|
Since A and B have conflicting methods, it defines to use
|
|
the variant of smallTalk from trait B, and the variant of bigTalk from
|
|
trait A.
|
|
</para>
|
|
<para>
|
|
The Aliased_Talker makes use of the <literal>as</literal> operator
|
|
to be able to use B's bigTalk implementation under an additional alias
|
|
<literal>talk</literal>.
|
|
</para>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait A {
|
|
public function smallTalk() {
|
|
echo 'a';
|
|
}
|
|
public function bigTalk() {
|
|
echo 'A';
|
|
}
|
|
}
|
|
|
|
trait B {
|
|
public function smallTalk() {
|
|
echo 'b';
|
|
}
|
|
public function bigTalk() {
|
|
echo 'B';
|
|
}
|
|
}
|
|
|
|
class Talker {
|
|
use A, B {
|
|
B::smallTalk insteadof A;
|
|
A::bigTalk insteadof B;
|
|
}
|
|
}
|
|
|
|
class Aliased_Talker {
|
|
use A, B {
|
|
B::smallTalk insteadof A;
|
|
A::bigTalk insteadof B;
|
|
B::bigTalk as talk;
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.visibility">
|
|
<title>Changing Method Visibility</title>
|
|
<para>
|
|
Using the <literal>as</literal> syntax, one can also adjust the visibility
|
|
of the method in the exhibiting class.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.visibility.ex1">
|
|
<title>Changing Method Visibility</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait HelloWorld {
|
|
public function sayHello() {
|
|
echo 'Hello World!';
|
|
}
|
|
}
|
|
|
|
// Change visibility of sayHello
|
|
class MyClass1 {
|
|
use HelloWorld { sayHello as protected; }
|
|
}
|
|
|
|
// Alias method with changed visibility
|
|
// sayHello visibility not changed
|
|
class MyClass2 {
|
|
use HelloWorld { sayHello as private myPrivateHello; }
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.composition">
|
|
<title>Traits Composed from Traits</title>
|
|
<para>
|
|
Just as classes can make use of traits, so can other traits. By using one
|
|
or more traits in a trait definition, it can be composed partially or
|
|
entirely of the members defined in those other traits.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.composition.ex1">
|
|
<title>Traits Composed from Traits</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait Hello {
|
|
public function sayHello() {
|
|
echo 'Hello ';
|
|
}
|
|
}
|
|
|
|
trait World {
|
|
public function sayWorld() {
|
|
echo 'World!';
|
|
}
|
|
}
|
|
|
|
trait HelloWorld {
|
|
use Hello, World;
|
|
}
|
|
|
|
class MyHelloWorld {
|
|
use HelloWorld;
|
|
}
|
|
|
|
$o = new MyHelloWorld();
|
|
$o->sayHello();
|
|
$o->sayWorld();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs;
|
|
<screen>
|
|
<![CDATA[
|
|
Hello World!
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.abstract">
|
|
<title>Abstract Trait Members</title>
|
|
<para>
|
|
Traits support the use of abstract methods in order to impose requirements
|
|
upon the exhibiting class.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.abstract.ex1">
|
|
<title>Express Requirements by Abstract Methods</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait Hello {
|
|
public function sayHelloWorld() {
|
|
echo 'Hello'.$this->getWorld();
|
|
}
|
|
abstract public function getWorld();
|
|
}
|
|
|
|
class MyHelloWorld {
|
|
private $world;
|
|
use Hello;
|
|
public function getWorld() {
|
|
return $this->world;
|
|
}
|
|
public function setWorld($val) {
|
|
$this->world = $val;
|
|
}
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.static">
|
|
<title>Static Trait Members</title>
|
|
<para>
|
|
Traits can define both static members and static methods.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.static.ex1">
|
|
<title>Static Variables</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait Counter {
|
|
public function inc() {
|
|
static $c = 0;
|
|
$c = $c + 1;
|
|
echo "$c\n";
|
|
}
|
|
}
|
|
|
|
class C1 {
|
|
use Counter;
|
|
}
|
|
|
|
class C2 {
|
|
use Counter;
|
|
}
|
|
|
|
$o = new C1(); $o->inc(); // echo 1
|
|
$p = new C2(); $p->inc(); // echo 1
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example xml:id="language.oop5.traits.static.ex2">
|
|
<title>Static Methods</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait StaticExample {
|
|
public static function doSomething() {
|
|
return 'Doing something';
|
|
}
|
|
}
|
|
|
|
class Example {
|
|
use StaticExample;
|
|
}
|
|
|
|
Example::doSomething();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect2>
|
|
|
|
<sect2 xml:id="language.oop5.traits.properties">
|
|
<title>Properties</title>
|
|
<para>
|
|
Traits can also define properties.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.properties.example">
|
|
<title>Defining Properties</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait PropertiesTrait {
|
|
public $x = 1;
|
|
}
|
|
|
|
class PropertiesExample {
|
|
use PropertiesTrait;
|
|
}
|
|
|
|
$example = new PropertiesExample;
|
|
$example->x;
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<para>
|
|
If a trait defines a property then a class can not define a property with
|
|
the same name, otherwise an error is issued. It is an
|
|
<constant>E_STRICT</constant> if the class definition is compatible (same
|
|
visibility and initial value) or fatal error otherwise.
|
|
</para>
|
|
<example xml:id="language.oop5.traits.properties.conflicts">
|
|
<title>Conflict Resolution</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?php
|
|
trait PropertiesTrait {
|
|
public $same = true;
|
|
public $different = false;
|
|
}
|
|
|
|
class PropertiesExample {
|
|
use PropertiesTrait;
|
|
public $same = true; // Strict Standards
|
|
public $different = true; // Fatal error
|
|
}
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|