Files
doc-fr/language/oop5/traits.xml
2022-01-31 15:56:23 +00:00

514 lines
12 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: afec339bc5b7918baddd9ba3cd02b90f3b423230 Maintainer: yannick Status: ready -->
<!-- Reviewed: no Maintainer: itanea -->
<sect1 xml:id="language.oop5.traits" xmlns="http://docbook.org/ns/docbook">
<title>Traits</title>
<para>
PHP implémente une manière pour réutiliser du code appelée Traits.
</para>
<para>
Les traits sont un mécanisme de réutilisation de code dans un langage à héritage simple
tel que PHP. Un trait tente de réduire certaines limites de l'héritage simple, en autorisant
le développeur à réutiliser un certain nombre de méthodes dans des classes indépendantes.
La sémantique entre les classes et les traits réduit la complexité et évite les problèmes
typiques de l'héritage multiple et des Mixins.
</para>
<para>
Un trait est semblable à une classe, mais il ne sert qu'à grouper des fonctionnalités d'une
manière intéressante. Il n'est pas possible d'instancier un Trait en lui-même. C'est un
ajout à l'héritage traditionnel, qui autorise la composition horizontale de comportements,
c'est à dire l'utilisation de méthodes de classe sans besoin d'héritage.
</para>
<example xml:id="language.oop5.traits.basicexample">
<title>Exemple d'utilisation de Trait</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>Précédence</title>
<para>
Une méthode héritée depuis une classe mère est écrasée par une méthode issue d'un Trait.
L'ordre de précédence fait en sorte que les méthodes de la classe courante
écrasent les méthodes issues d'un Trait, elles-mêmes surchargeant les méthodes héritées.
</para>
<example xml:id="language.oop5.traits.precedence.examples.ex1">
<title>Exemple avec l'ordre de précédence</title>
<para>
Une méthode héritée depuis la classe de base est écrasée par celle provenant
du Trait. Ce n'est pas le cas des méthodes réelles, écrites dans la classe de base.
</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>Autre exemple d'ordre de précédence</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>Multiples Traits</title>
<para>
Une classe peut utiliser de multiples Traits en les déclarant avec le
mot-clé <literal>use</literal>, séparés par des virgules.
</para>
<example xml:id="language.oop5.traits.multiple.ex1">
<title>Utilisation de plusieurs Traits</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>Résolution des conflits</title>
<para>
Si deux Traits insèrent une méthode avec le même nom, une erreur fatale est levée si le conflit n'est
pas explicitement résolu.
</para>
<para>
Pour résoudre un conflit de nommage entre des Traits utilisés dans la même classe, il faut utiliser l'opérateur
<literal>insteadof</literal> pour choisir une des méthodes en conflit.
</para>
<para>
Puisque ce principe ne permet que d'exclure des méthodes,
l'opérateur <literal>as</literal> peut être utilisé pour permettre
l'inclusion d'une des méthodes conflictuelles sous un autre nom. Notez que
l'opérateur <literal>as</literal> ne renomme pas la méthode et n'affecte pas
d'autres méthodes non plus.
</para>
<example xml:id="language.oop5.traits.examples.ex1">
<title>Résolution des conflits</title>
<para>
Dans cet exemple, la classe Talker utilise les traits A et B.
Comme A et B ont des méthodes conflictuelles, on indique que l'on souhaite utiliser
la variante de smallTalk depuis le trait B, et la variante de bigTalk depuis le
trait A.
</para>
<para>
La classe Aliased_Talker utilise l'opérateur <literal>as</literal>
pour être capable d'utiliser l'implémentation bigTalk de B sous un alias
supplémentaire <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>Changer la visibilité des méthodes</title>
<para>
En utilisant la syntaxe <literal>as</literal>, vous pouvez aussi ajuster
la visibilité de la méthode dans la classe qui l'utilise.
</para>
<example xml:id="language.oop5.traits.visibility.ex1">
<title>Changer la visibilité des méthodes</title>
<programlisting role="php">
<![CDATA[
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
}
// Modification de la visibilité de la méthode sayHello
class MyClass1 {
use HelloWorld { sayHello as protected; }
}
// Utilisation d'un alias lors de la modification de la visibilité
// La visibilité de la méthode sayHello n'est pas modifiée
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
?>
]]>
</programlisting>
</example>
</sect2>
<sect2 xml:id="language.oop5.traits.composition">
<title>Traits Composés depuis d'autres Traits</title>
<para>
Tout comme les classes peuvent utiliser des traits, d'autres traits le
peuvent aussi. Un trait peut donc utiliser d'autres traits et hériter
de tout ou d'une partie de ceux-ci.
</para>
<example xml:id="language.oop5.traits.composition.ex1">
<title>Traits Composés depuis d'autres 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>Méthodes abstraites dans les Traits</title>
<para>
Les traits supportent l'utilisation de méthodes abstraites afin d'imposer
des contraintes aux classes sous-jacentes. Les méthodes publiques, protégées,
et privées sont supportées.
Antérieur à PHP 8.0.0, seule les méthodes publiques et protégées abstraire
étaient supportées.
</para>
<caution>
<simpara>
Une classe concrète satisfait ces conditions en définissant une méthode concrète
avec le même nom ; sa signature peut être différente.
</simpara>
</caution>
<example xml:id="language.oop5.traits.abstract.ex1">
<title>Obligations requises par les méthodes abstraites</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>Attributs statiques dans les Traits</title>
<para>
Les traits peuvent définir des variables statiques, méthodes statiques
et propriétés statiques.
</para>
<note>
<para>
À partir de PHP 8.1.0, appeler une méthode statique ou accéder à une
propriété statique directement sur un trait est obsolète.
Les méthodes et propriétés statiques devraient seulement être accédé sur une classe utilisant le trait.
</para>
</note>
<example xml:id="language.oop5.traits.static.ex1">
<title>Variables statiques</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>Méthodes statiques</title>
<programlisting role="php">
<![CDATA[
<?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
}
class Example {
use StaticExample;
}
Example::doSomething();
?>
]]>
</programlisting>
</example>
<example xml:id="language.oop5.traits.static.ex3">
<title>Propriétés statiques</title>
<programlisting role="php">
<![CDATA[
<?php
trait StaticExample {
public static $static = 'foo';
}
class Example {
use StaticExample;
}
echo Example::$static;
?>
]]>
</programlisting>
</example>
</sect2>
<sect2 xml:id="language.oop5.traits.properties">
<title>Propriétés</title>
<para>
Les traits peuvent aussi définir des propriétés.
</para>
<example xml:id="language.oop5.traits.properties.example">
<title>Définir des propriétés</title>
<programlisting role="php">
<![CDATA[
<?php
trait PropertiesTrait {
public $x = 1;
}
class PropertiesExample {
use PropertiesTrait;
}
$example = new PropertiesExample;
$example->x;
?>
]]>
</programlisting>
</example>
<para>
Si un trait définit une propriété, alors la classe ne peut pas définir une
propriété de même nom sauf si elle est compatible (même visibilité et valeur
initiale), sinon une erreur fatale est émise.
</para>
<example xml:id="language.oop5.traits.properties.conflicts">
<title>Résolution des conflits</title>
<programlisting role="php">
<![CDATA[
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true;
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
-->