1
0
mirror of https://github.com/php/doc-fr.git synced 2026-03-23 22:52:18 +01:00
Files
archived-doc-fr/language/oop5/basic.xml

855 lines
22 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: fc068b4857342eb409efeafe6723058f39ab1045 Maintainer: yannick Status: ready -->
<!-- Reviewed: no -->
<sect1 xml:id="language.oop5.basic" xmlns="http://docbook.org/ns/docbook">
<title>Syntaxe de base</title>
<sect2 xml:id="language.oop5.basic.class">
<title>class</title>
<para>
Une définition de classe basique commence par le mot-clé
<literal>class</literal>, suivi du nom de la classe.
Suit une paire d'accolades contenant la définition des propriétés et des
méthodes appartenant à la classe.
</para>
<para>
Le nom de la classe peut être quelconque à condition que ce ne soit pas un
<link linkend="reserved">mot réservé</link> en PHP.
À partir de PHP 8.4.0, l'utilisation d'un seul underscore <literal>_</literal> comme nom de classe est obsolète.
Un nom de classe valide commence par une lettre ou un underscore,
suivi de n'importe quel nombre de lettres, chiffres ou underscores.
En tant qu'expression régulière, cela serait exprimé ainsi :
<code>^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$</code>.
</para>
<para>
Une classe peut contenir ses propres <link linkend="language.oop5.constants">constantes</link>,
<link linkend="language.oop5.properties">variables</link>
(appelées "propriétés" ou "attributs"), et fonctions (appelées "méthodes").
</para>
<example>
<title>Définition typique d'une classe</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass
{
// déclaration d'une propriété
public $var = 'une valeur par défaut';
// déclaration des méthodes
public function displayVar() {
echo $this->var;
}
}
?>
]]>
</programlisting>
</example>
<para>
La pseudo-variable <varname>$this</varname> est disponible
lorsqu'une méthode est appelée depuis un contexte objet.
<varname>$this</varname> est la valeur de l'objet appelant.
</para>
<warning>
<para>
Appeler une méthode non-statique statiquement lance une
<classname>Error</classname>.
Avant PHP 8.0.0, ceci générait une notice d'obsolescence,
et <varname>$this</varname> était indéfini.
</para>
<example xml:id="language.oop5.basic.class.this">
<title>Quelques exemples de la pseudo-variable <varname>$this</varname></title>
<programlisting role="php">
<![CDATA[
<?php
class A
{
function foo()
{
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this is not defined.\n";
}
}
}
class B
{
function bar()
{
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
]]>
</programlisting>
&example.outputs.7;
<screen>
<![CDATA[
$this is defined (A)
Deprecated: Non-static method A::foo() should not be called statically in %s on line 27
$this is not defined.
Deprecated: Non-static method A::foo() should not be called statically in %s on line 20
$this is not defined.
Deprecated: Non-static method B::bar() should not be called statically in %s on line 32
Deprecated: Non-static method A::foo() should not be called statically in %s on line 20
$this is not defined.
]]>
</screen>
&example.outputs.8;
<screen>
<![CDATA[
$this is defined (A)
Fatal error: Uncaught Error: Non-static method A::foo() cannot be called statically in %s :27
Stack trace:
#0 {main}
thrown in %s on line 27
]]>
</screen>
</example>
</warning>
<sect3 xml:id="language.oop5.basic.class.readonly">
<title>Classes en lecture seule (readonly)</title>
<para>
À partir de PHP 8.2.0, une classe peut être marquée
avec le modificateur <modifier>readonly</modifier>.
Marquer une classe comme <modifier>readonly</modifier> ajoutera
le <link linkend="language.oop5.properties.readonly-properties">modificateur <modifier>readonly</modifier></link>
à chaque propriété déclarée, et empêchera la création
de <link linkend="language.oop5.properties.dynamic-properties">propriétés dynamiques</link>.
En outre, il est impossible d'ajouter leur prise en charge en utilisant
l'attribut <classname>AllowDynamicProperties</classname>.
Toute tentative de le faire déclenchera une erreur de compilation.
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
#[\AllowDynamicProperties]
readonly class Foo {
}
// Fatal error: Cannot apply #[AllowDynamicProperties] to readonly class Foo
?>
]]>
</programlisting>
</informalexample>
<para>
Comme ni les propriétés non typées ni les propriétés statiques
ne peuvent être marquées avec le modificateur <literal>readonly</literal>,
les classes readonly ne peuvent pas non plus les déclarer :
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
readonly class Foo
{
public $bar;
}
// Fatal error: Readonly property Foo::$bar must have type
?>
]]>
</programlisting>
<programlisting role="php">
<![CDATA[
<?php
readonly class Foo
{
public static int $bar;
}
// Fatal error: Readonly class Foo cannot declare static properties
?>
]]>
</programlisting>
</informalexample>
<para>
Une classe <modifier>readonly</modifier> peut être
<link linkend="language.oop5.basic.extends">étendue</link> si,
et seulement si, la classe enfant est également
une classe <modifier>readonly</modifier>.
</para>
</sect3>
</sect2>
<sect2 xml:id="language.oop5.basic.new">
<title>Le mot-clé <literal>new</literal></title>
<para>
Pour créer une instance d'une classe, le mot-clé <literal>new</literal> doit être
utilisé. Un objet sera alors systématiquement créé, à moins qu'il ait un
<link linkend="language.oop5.decon">constructeur</link>
défini qui lance une <link linkend="language.exceptions">exception</link>
en cas d'erreur. Les classes devraient être définies avant l'instanciation (dans
certains cas, c'est impératif).
</para>
<para>
Si une variable <type>string</type> contenant un nom de classe est utilisée avec
<literal>new</literal>, une nouvelle instance de cette classe sera créée.
Si la classe est dans un espace de noms, son nom pleinement qualifié doit être utilisé.
</para>
<note>
<para>
S'il n'y a pas d'arguments à passer au constructeur de la classe,
les parenthèses après le nom de la classe peuvent être omises.
</para>
</note>
<example>
<title>Création d'une instance</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass {
}
$instance = new SimpleClass();
var_dump($instance);
// Ceci peut également être réalisé avec une variable :
$className = 'SimpleClass';
$instance = new $className(); // new SimpleClass()
var_dump($instance);
?>
]]>
</programlisting>
</example>
<para>
À partir de PHP 8.0.0, l'utilisation de <literal>new</literal> avec des
expressions arbitraires est supportée.
Ceci permet des instantiations plus complexes si l'expression produit une
<type>string</type>. L'expression doit être entourée de parenthèses.
</para>
<example>
<title>Créer une instance en utilisant une expression arbitraire</title>
<para>
Dans l'exemple donné, nous montrons plusieurs exemples d'expressions
arbitraires valides qui produisent un nom de classe.
Ceci montre un appel de fonction, une concaténation de chaînes
et la constante <constant>::class</constant>.
</para>
<programlisting role="php">
<![CDATA[
<?php
class ClassA extends \stdClass {}
class ClassB extends \stdClass {}
class ClassC extends ClassB {}
class ClassD extends ClassA {}
function getSomeClass(): string
{
return 'ClassA';
}
var_dump(new (getSomeClass()));
var_dump(new ('Class' . 'B'));
var_dump(new ('Class' . 'C'));
var_dump(new (ClassD::class));
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
object(ClassA)#1 (0) {
}
object(ClassB)#1 (0) {
}
object(ClassC)#1 (0) {
}
object(ClassD)#1 (0) {
}
]]>
</screen>
</example>
<para>
Dans le contexte de la classe, il est possible de créer un nouvel objet
avec <literal>new self</literal> et <literal>new parent</literal>.
</para>
<para>
Lors de l'assignation d'une instance déjà créée d'une classe à une variable,
la nouvelle variable accédera à la même instance que l'objet qui a été assigné.
Ce comportement est le même lors du passage d'une instance à une fonction.
Une copie d'un objet déjà créé peut être effectuée par
<link linkend="language.oop5.cloning">clonage</link>.
</para>
<example>
<title>Assignation d'un objet</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass {
public string $var;
}
$instance = new SimpleClass();
$assigned = $instance;
$reference =& $instance;
$instance->var = '$assigned aura cette valeur';
$instance = null; // $instance et $reference deviennent null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
NULL
NULL
object(SimpleClass)#1 (1) {
["var"]=>
string(30) "$assigned aura cette valeur"
}
]]>
</screen>
</example>
<para>
Il est possible de créer une instance d'un objet de quelques manières différentes :
</para>
<example>
<title>Créer de nouveaux objets</title>
<programlisting role="php">
<![CDATA[
<?php
class Test
{
public static function getNew()
{
return new static();
}
}
class Child extends Test {}
$obj1 = new Test(); // Par le nom de la classe
$obj2 = new $obj1; // À travers la variable contenant un objet
var_dump($obj1 !== $obj2);
$obj3 = Test::getNew(); // Par la méthode de classe
var_dump($obj3 instanceof Test);
$obj4 = Child::getNew(); // À travers une méthode de classe enfant
var_dump($obj4 instanceof Child);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
bool(true)
bool(true)
bool(true)
]]>
</screen>
</example>
<para>
Il est possible d'accéder à un membre d'un objet nouvellement
créé en une seule expression :
</para>
<example>
<title>Accéder un membre d'un objet nouvellement créé</title>
<programlisting role="php">
<![CDATA[
<?php
echo (new DateTime())->format('Y'), PHP_EOL;
// les parenthèses autour sont optionnelles à partir de PHP 8.4.0
echo new DateTime()->format('Y'), PHP_EOL;
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
2025
2025
]]>
</screen>
</example>
<note>
<simpara>
Avant PHP 7.1, les arguments ne sont pas évalués s'il n'y a pas de
fonction constructeur définie.
</simpara>
</note>
</sect2>
<sect2 xml:id="language.oop5.basic.properties-methods">
<title>Propriétés et méthodes</title>
<para>
Les propriétés et méthodes de classe vivent dans des "espaces de noms" séparés,
donc il est possible d'avoir une propriété et une méthode avec le même nom.
Faire référence à la fois à une propriété ainsi qu'une méthode ont la même
notation, et le fait qu'une propriété sera accédée ou qu'une méthode sera appelée,
dépend seulement du contexte, c'est-à-dire si l'utilisation est un accès variable
ou un appel de fonction.
</para>
<example>
<title>Accès de propriété contre appel de méthode</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public $bar = 'property';
public function bar() {
return 'method';
}
}
$obj = new Foo();
echo $obj->bar, PHP_EOL, $obj->bar(), PHP_EOL;
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
property
method
]]>
</screen>
</example>
<para>
Ceci signifie qu'appeler une <link linkend="functions.anonymous">fonction
anonyme</link> qui a été assignée à une propriété n'est pas possible directement.
Au lieu de cela, la propriété doit d'abord être affectée à une variable.
Il est possible d'appeler ce genre de propriété directement
en la mettant entre parenthèses.
</para>
<example>
<title>Appeler une fonction anonyme enregistrée dans une propriété</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public $bar;
public function __construct() {
$this->bar = function() {
return 42;
};
}
}
$obj = new Foo();
echo ($obj->bar)(), PHP_EOL;
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
42
]]>
</screen>
</example>
</sect2>
<sect2 xml:id="language.oop5.basic.extends">
<title>Le mot-clé <literal>extends</literal></title>
<para>
Une classe peut hériter des constantes, méthodes et des propriétés d'une autre classe en
utilisant le mot-clé <literal>extends</literal> dans la déclaration.
Il n'est pas possible d'étendre plusieurs classes : une classe peut
uniquement hériter d'une seule classe de base.
</para>
<para>
Les constantes, méthodes et propriétés héritées peuvent être redéfinies en les redéclarant avec le
même nom que dans la classe parente. Cependant, si la classe parente a défini
une méthode ou constante comme <link linkend="language.oop5.final">final</link>,
alors celles-ci ne peuvent pas être redéfinies. Il est possible d'accéder aux
méthodes ou propriétés statiques redéfinies en y faisant référence avec l'opérateur
<link linkend="language.oop5.paamayim-nekudotayim">parent::</link>.
</para>
<note>
<simpara>
À partir de PHP 8.1.0, les constantes peuvent être déclarées comme finales.
</simpara>
</note>
<example>
<title>Héritage simple d'une classe</title>
<programlisting role="php">
<![CDATA[
<?php
class SimpleClass
{
function displayVar()
{
echo "Classe parente\n";
}
}
class ExtendClass extends SimpleClass
{
// Redéfinition de la méthode parente
function displayVar()
{
echo "Classe étendue\n";
parent::displayVar();
}
}
$extended = new ExtendClass();
$extended->displayVar();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Classe étendue
Classe parente
]]>
</screen>
</example>
<sect3 xml:id="language.oop.lsp">
<title>Règles de compatibilité de signature</title>
<para>
Lors de la surcharge d'une méthode, sa signature doit être compatible avec
la méthode parente. Sinon, une erreur fatale est émise, ou, antérieur à
PHP 8.0.0, une erreur de niveau <constant>E_WARNING</constant> est générée.
Une signature est compatible si elle respecte les règles de
<link linkend="language.oop5.variance">variance</link>, rend un paramètre
obligatoire optionnel, et seulement si tous les nouveaux paramètres sont
optionnels et assouplissent les règles de visibilité.
Ceci est connu sous le Principe de Substitution de Liskov
(Liskov Substitution Principle), ou simplement LSP.
Le <link linkend="language.oop5.decon.constructor">constructeur</link>,
et les méthodes <literal>private</literal> sont exclus de ces règles de
compatibilité des signatures, et par conséquent n'émettront pas d'erreur
fatale en cas de signature incompatible.
</para>
<example>
<title>Méthodes enfant compatibles</title>
<programlisting role="php">
<![CDATA[
<?php
class Base
{
public function foo(int $a) {
echo "Valid\n";
}
}
class Extend1 extends Base
{
function foo(int $a = 5)
{
parent::foo($a);
}
}
class Extend2 extends Base
{
function foo(int $a, $b = 5)
{
parent::foo($a);
}
}
$extended1 = new Extend1();
$extended1->foo();
$extended2 = new Extend2();
$extended2->foo(1);
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Valid
Valid
]]>
</screen>
</example>
<para>
Les exemples suivants démontrent qu'une méthode enfant qui supprime un paramètre,
ou rend un paramètre optionnel obligatoire, n'est pas compatible avec la méthode parente.
</para>
<example>
<title>Erreur fatale quand une méthode enfant supprime un paramètre</title>
<programlisting role="php">
<![CDATA[
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo()
{
parent::foo(1);
}
}
]]>
</programlisting>
&example.outputs.8.similar;
<screen>
<![CDATA[
Fatal error: Declaration of Extend::foo() must be compatible with Base::foo(int $a = 5) in /in/evtlq on line 13
]]>
</screen>
</example>
<example>
<title>Erreur fatale quand une méthode enfant rend un paramètre optionnel obligatoire</title>
<programlisting role="php">
<![CDATA[
<?php
class Base
{
public function foo(int $a = 5) {
echo "Valid\n";
}
}
class Extend extends Base
{
function foo(int $a)
{
parent::foo($a);
}
}
]]>
</programlisting>
&example.outputs.8.similar;
<screen>
<![CDATA[
Fatal error: Declaration of Extend::foo(int $a) must be compatible with Base::foo(int $a = 5) in /in/qJXVC on line 13
]]>
</screen>
</example>
<warning>
<para>
Renommer un paramètre d'une méthode dans une classe enfant n'est pas
une incompatibilité de signature. Cependant, ceci est découragé car ceci
va résulter en une <classname>Error</classname> lors du runtime si les
<link linkend="functions.named-arguments">arguments nommés</link>
sont utilisés.
</para>
<example>
<title>Erreur lors de l'utilisation des arguments nommés et que les paramètres ont été renommés dans une classe enfant</title>
<programlisting role="php">
<![CDATA[
<?php
class A {
public function test($foo, $bar) {}
}
class B extends A {
public function test($a, $b) {}
}
$obj = new B;
// Pass parameters according to A::test() contract
$obj->test(foo: "foo", bar: "bar"); // ERROR!
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
Fatal error: Uncaught Error: Unknown named parameter $foo in /in/XaaeN:14
Stack trace:
#0 {main}
thrown in /in/XaaeN on line 14
]]>
</screen>
</example>
</warning>
</sect3>
</sect2>
<sect2 xml:id="language.oop5.basic.class.class">
<title>::class</title>
<para>
Le mot clé <literal>class</literal> est également utilisé
pour la résolution des noms de classes.
Il est possible d'obtenir le nom complètement qualifié d'une classe
<literal>ClassName</literal> en utilisant <literal>ClassName::class</literal>.
C'est particulièrement utile avec les classes utilisant des
<link linkend="language.namespaces">espaces de noms</link>.
</para>
<para>
<example xml:id="language.oop5.basic.class.class.name">
<title>Résolution de nom de classe</title>
<programlisting role="php">
<![CDATA[
<?php
namespace NS {
class ClassName {
}
echo ClassName::class;
}
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
NS\ClassName
]]>
</screen>
</example>
</para>
<note>
<para>
La résolution du nom de classe en utilisant <literal>::class</literal> est une
transformation lors de la compilation. C'est-à-dire à l'instant où la chaîne du
nom de classe est créée aucun autochargement n'a encore eu lieu. Par conséquent,
les noms de classes sont étendus même si la classe n'existe pas. Aucune erreur
n'est émise dans ce cas-là.
</para>
<example xml:id="language.oop5.basic.class.class.fail">
<title>Résolution de nom de classe manquante</title>
<programlisting role="php">
<![CDATA[
<?php
print Does\Not\Exist::class;
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Does\Not\Exist
]]>
</screen>
</example>
</note>
<para>
À partir de PHP 8.0.0, <literal>::class</literal> peut être utilisé
sur les objets. Cette résolution se produit lors de l'exécution, et non lors de
la compilation. Ses effets sont les mêmes que d'appeler
<function>get_class</function> sur l'objet.
</para>
<example xml:id="language.oop5.basic.class.class.object">
<title>Résolution du nom d'un objet</title>
<programlisting role="php">
<![CDATA[
<?php
namespace NS {
class ClassName {
}
$c = new ClassName();
print $c::class;
}
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
NS\ClassName
]]>
</screen>
</example>
</sect2>
<sect2 xml:id="language.oop5.basic.nullsafe">
<title>Méthodes et propriétés nullsafe</title>
<para>
À partir de PHP 8.0.0, les méthodes et propriétés peuvent aussi être accédées
avec l'opérateur "nullsafe" : <literal>?-></literal>. L'opérateur nullsafe
fonctionne à l'identique que l'accès de propriétés ou méthodes comme ci-dessus,
à l'exception que si l'objet qui est déréférencé est &null; alors &null; sera
retourné au lieu de lancer une exception. Si le déréférencement fait partie d'une
chaîne, le reste de la chaîne est ignoré.
</para>
<para>
L'effet est similaire à entourer chaque accès par une vérification avec
<function>is_null</function> d'abord, mais plus compact.
</para>
<para>
<example>
<title>Opérateur nullsafe</title>
<programlisting role="php" annotations="non-interactive">
<![CDATA[
<?php
// À partir de PHP 8.0.0, cette ligne:
$result = $repository?->getUser(5)?->name;
// Est équivalente au bloc de code suivant :
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
?>
]]>
</programlisting>
</example>
</para>
<note>
<para>
L'opérateur nullsafe est mieux utilisé quand null est considéré une valeur valide
et potentiellement attendu pour une propriété ou valeur de retour d'une méthode.
Pour indiquer une erreur, lancer une exception est préférable.
</para>
</note>
</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
-->