Nette Documentation Preview

syntax
SmartObject
***********

.[perex]
SmartObject corrigeait le comportement des objets de plusieurs façons, mais le PHP d'aujourd'hui inclut déjà la plupart de ces améliorations de façon native. Cependant, il ajoute encore le support des *propriétés*.


Installation :

```shell
composer require nette/utils
```


Propriétés, getters et setters .[#toc-properties-getters-and-setters]
=====================================================================

Dans les langages modernes orientés objet (par exemple C#, Python, Ruby, JavaScript), le terme *propriété* fait référence aux [membres spéciaux des classes |https://en.wikipedia.org/wiki/Property_(programming)] qui ressemblent à des variables mais sont en fait représentés par des méthodes. Lorsque la valeur de cette "variable" est assignée ou lue, la méthode correspondante (appelée getter ou setter) est appelée. C'est très pratique, cela nous donne un contrôle total sur l'accès aux variables. Nous pouvons valider l'entrée ou générer des résultats uniquement lorsque la propriété est lue.

Les propriétés PHP ne sont pas prises en charge, mais le trait `Nette\SmartObject` peut les imiter. Comment l'utiliser ?

- Ajoutez une annotation à la classe sous la forme `@property <type> $xyz`
- Créez un getter nommé `getXyz()` ou `isXyz()`, un setter nommé `setXyz()`
- Le getter et le setter doivent être *public* ou *protégé* et sont optionnels, donc il peut y avoir une propriété *lire seulement* ou *écrire seulement*.

Nous utiliserons la propriété de la classe Circle pour nous assurer que seuls des nombres non négatifs sont placés dans la variable `$radius`. Remplacez `public $radius` par la propriété :

```php
/**
 * @property float $radius
 * @property-read bool $visible
 */
class Circle
{
	use Nette\SmartObject;

	private float $radius = 0.0; // not public

	// getter for property $radius
	protected function getRadius(): float
	{
		return $this->radius;
	}

	// setter for property $radius
	protected function setRadius(float $radius): void
	{
		// sanitizing value before saving it
		$this->radius = max(0.0, $radius);
	}

	// getter for property $visible
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10;  // actually calls setRadius(10)
echo $circle->radius;  // calls getRadius()
echo $circle->visible; // calls isVisible()
```

Les propriétés sont principalement du "sucre syntaxique" ((sucre syntaxique)), destiné à rendre la vie du programmeur plus douce en simplifiant le code. Si vous n'en voulez pas, vous n'êtes pas obligé de les utiliser.


Un aperçu de l'histoire .[#toc-a-glimpse-into-history]
======================================================

SmartObject permettait d'affiner le comportement des objets de nombreuses manières, mais le PHP d'aujourd'hui incorpore déjà la plupart de ces améliorations de manière native. Le texte suivant est un regard nostalgique sur l'histoire, nous rappelant comment les choses ont évolué.

Dès le début, le modèle objet de PHP a souffert d'une myriade de lacunes et de déficiences. Cela a conduit à la création de la classe `Nette\Object` (en 2007), qui visait à corriger ces problèmes et à améliorer le confort d'utilisation de PHP. Il suffisait que d'autres classes héritent de cette classe pour bénéficier des avantages qu'elle offrait. Lorsque PHP 5.4 a introduit le support des traits, la classe `Nette\Object` a été remplacée par le trait `Nette\SmartObject`. Cela a éliminé le besoin d'hériter d'un ancêtre commun. De plus, le trait pouvait être utilisé dans des classes qui héritaient déjà d'une autre classe. La fin définitive de `Nette\Object` est intervenue avec la publication de PHP 7.2, qui a interdit aux classes d'être nommées `Object`.

Au fur et à mesure du développement de PHP, le modèle d'objet et les capacités du langage se sont améliorés. Plusieurs fonctions de la classe `SmartObject` sont devenues redondantes. Depuis la sortie de PHP 8.2, il ne reste plus qu'une seule fonctionnalité non directement supportée par PHP : la possibilité d'utiliser ce que l'on appelle des [propriétés |#Properties, getters, and setters].

Quelles sont les fonctionnalités offertes par `Nette\Object` et, par extension, par `Nette\SmartObject`? En voici un aperçu. (Dans les exemples, la classe `Nette\Object` est utilisée, mais la plupart des fonctionnalités s'appliquent également au trait `Nette\SmartObject` ).


Erreurs de cohérence .[#toc-inconsistent-errors]
------------------------------------------------
PHP avait un comportement incohérent lors de l'accès aux membres non déclarés. L'état au moment de `Nette\Object` était le suivant :

```php
echo $obj->undeclared; // E_NOTICE, later E_WARNING
$obj->undeclared = 1;  // passes silently without reporting
$obj->unknownMethod(); // Fatal error (not catchable by try/catch)
```

Une erreur fatale terminait l'application sans possibilité de réagir. L'écriture silencieuse dans des membres inexistants sans avertissement pouvait entraîner des erreurs graves difficiles à détecter. `Nette\Object` Tous ces cas ont été détectés et une exception `MemberAccessException` a été levée.

```php
echo $obj->undeclared;   // throw Nette\MemberAccessException
$obj->undeclared = 1;    // throw Nette\MemberAccessException
$obj->unknownMethod();   // throw Nette\MemberAccessException
```
Depuis PHP 7.0, PHP ne provoque plus d'erreurs fatales non capturables, et l'accès à des membres non déclarés est un bug depuis PHP 8.2.


Voulez-vous dire ? .[#toc-did-you-mean]
---------------------------------------
Si une erreur `Nette\MemberAccessException` était déclenchée, peut-être à cause d'une faute de frappe lors de l'accès à une variable d'objet ou de l'appel d'une méthode, `Nette\Object` tentait de donner un indice dans le message d'erreur sur la façon de corriger l'erreur, sous la forme de l'addendum emblématique "Did you mean ?".

```php
class Foo extends Nette\Object
{
	public static function from($var)
	{
	}
}

$foo = Foo::form($var);
// throw Nette\MemberAccessException
// "Call to undefined static method Foo::form(), did you mean from()?"
```

Bien que le PHP d'aujourd'hui n'ait pas de fonction "did you mean ?", cette phrase peut être ajoutée aux erreurs par [Tracy |tracy:]. Il peut même [corriger automatiquement ces erreurs |tracy:open-files-in-ide#toc-demos].


Méthodes d'extension .[#toc-extension-methods]
----------------------------------------------
Inspiré par les méthodes d'extension de C#. Elles donnaient la possibilité d'ajouter de nouvelles méthodes à des classes existantes. Par exemple, vous pouvez ajouter la méthode `addDateTime()` à un formulaire pour ajouter votre propre DateTimePicker.

```php
Form::extensionMethod(
	'addDateTime',
	fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);

$form = new Form;
$form->addDateTime('date');
```

Les méthodes d'extension se sont avérées peu pratiques car leurs noms n'étaient pas autocomplétés par les éditeurs, qui signalaient au contraire que la méthode n'existait pas. Par conséquent, leur prise en charge a été abandonnée.


Obtention du nom de la classe .[#toc-getting-the-class-name]
------------------------------------------------------------

```php
$class = $obj->getClass(); // using Nette\Object
$class = $obj::class;      // since PHP 8.0
```


Accès à la réflexion et aux annotations .[#toc-access-to-reflection-and-annotations]
------------------------------------------------------------------------------------

`Nette\Object` a proposé un accès à la réflexion et aux annotations en utilisant les méthodes `getReflection()` et `getAnnotation()`:

```php
/**
 * @author John Doe
 */
class Foo extends Nette\Object
{
}

$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // returns 'John Doe'
```

Depuis PHP 8.0, il est possible d'accéder aux méta-informations sous forme d'attributs :

```php
#[Author('John Doe')]
class Foo
{
}

$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];
```


Les récupérateurs de méthodes .[#toc-method-getters]
----------------------------------------------------

`Nette\Object` offraient un moyen élégant de traiter les méthodes comme s'il s'agissait de variables :

```php
class Foo extends Nette\Object
{
	public function adder($a, $b)
	{
		return $a + $b;
	}
}

$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5
```

Depuis PHP 8.1, vous pouvez utiliser la  syntaxe:https://www.php.net/manual/en/functions.first_class_callable_syntax dite "first-class callable":https://www.php.net/manual/en/functions.first_class_callable_syntax:

```php
$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5
```


Événements .[#toc-events]
-------------------------

`Nette\Object` a offert un sucre syntaxique pour déclencher l'[événement |nette:glossary#events]:

```php
class Circle extends Nette\Object
{
	public array $onChange = [];

	public function setRadius(float $radius): void
	{
		$this->onChange($this, $radius);
		$this->radius = $radius;
	}
}
```

Le code `$this->onChange($this, $radius)` est équivalent à ce qui suit :

```php
foreach ($this->onChange as $callback) {
	$callback($this, $radius);
}
```

Pour des raisons de clarté, nous vous recommandons d'éviter la méthode magique `$this->onChange()`. Un bon substitut est [Nette\Utils\Arrays::invoke |arrays#invoke]:

```php
Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);
```

SmartObject

SmartObject corrigeait le comportement des objets de plusieurs façons, mais le PHP d'aujourd'hui inclut déjà la plupart de ces améliorations de façon native. Cependant, il ajoute encore le support des propriétés.

Installation :

composer require nette/utils

Propriétés, getters et setters

Dans les langages modernes orientés objet (par exemple C#, Python, Ruby, JavaScript), le terme propriété fait référence aux membres spéciaux des classes qui ressemblent à des variables mais sont en fait représentés par des méthodes. Lorsque la valeur de cette „variable“ est assignée ou lue, la méthode correspondante (appelée getter ou setter) est appelée. C'est très pratique, cela nous donne un contrôle total sur l'accès aux variables. Nous pouvons valider l'entrée ou générer des résultats uniquement lorsque la propriété est lue.

Les propriétés PHP ne sont pas prises en charge, mais le trait Nette\SmartObject peut les imiter. Comment l'utiliser ?

  • Ajoutez une annotation à la classe sous la forme @property <type> $xyz
  • Créez un getter nommé getXyz() ou isXyz(), un setter nommé setXyz()
  • Le getter et le setter doivent être public ou protégé et sont optionnels, donc il peut y avoir une propriété lire seulement ou écrire seulement.

Nous utiliserons la propriété de la classe Circle pour nous assurer que seuls des nombres non négatifs sont placés dans la variable $radius. Remplacez public $radius par la propriété :

/**
 * @property float $radius
 * @property-read bool $visible
 */
class Circle
{
	use Nette\SmartObject;

	private float $radius = 0.0; // not public

	// getter for property $radius
	protected function getRadius(): float
	{
		return $this->radius;
	}

	// setter for property $radius
	protected function setRadius(float $radius): void
	{
		// sanitizing value before saving it
		$this->radius = max(0.0, $radius);
	}

	// getter for property $visible
	protected function isVisible(): bool
	{
		return $this->radius > 0;
	}
}

$circle = new Circle;
$circle->radius = 10;  // actually calls setRadius(10)
echo $circle->radius;  // calls getRadius()
echo $circle->visible; // calls isVisible()

Les propriétés sont principalement du „sucre syntaxique“ ((sucre syntaxique)), destiné à rendre la vie du programmeur plus douce en simplifiant le code. Si vous n'en voulez pas, vous n'êtes pas obligé de les utiliser.

Un aperçu de l'histoire

SmartObject permettait d'affiner le comportement des objets de nombreuses manières, mais le PHP d'aujourd'hui incorpore déjà la plupart de ces améliorations de manière native. Le texte suivant est un regard nostalgique sur l'histoire, nous rappelant comment les choses ont évolué.

Dès le début, le modèle objet de PHP a souffert d'une myriade de lacunes et de déficiences. Cela a conduit à la création de la classe Nette\Object (en 2007), qui visait à corriger ces problèmes et à améliorer le confort d'utilisation de PHP. Il suffisait que d'autres classes héritent de cette classe pour bénéficier des avantages qu'elle offrait. Lorsque PHP 5.4 a introduit le support des traits, la classe Nette\Object a été remplacée par le trait Nette\SmartObject. Cela a éliminé le besoin d'hériter d'un ancêtre commun. De plus, le trait pouvait être utilisé dans des classes qui héritaient déjà d'une autre classe. La fin définitive de Nette\Object est intervenue avec la publication de PHP 7.2, qui a interdit aux classes d'être nommées Object.

Au fur et à mesure du développement de PHP, le modèle d'objet et les capacités du langage se sont améliorés. Plusieurs fonctions de la classe SmartObject sont devenues redondantes. Depuis la sortie de PHP 8.2, il ne reste plus qu'une seule fonctionnalité non directement supportée par PHP : la possibilité d'utiliser ce que l'on appelle des propriétés.

Quelles sont les fonctionnalités offertes par Nette\Object et, par extension, par Nette\SmartObject? En voici un aperçu. (Dans les exemples, la classe Nette\Object est utilisée, mais la plupart des fonctionnalités s'appliquent également au trait Nette\SmartObject ).

Erreurs de cohérence

PHP avait un comportement incohérent lors de l'accès aux membres non déclarés. L'état au moment de Nette\Object était le suivant :

echo $obj->undeclared; // E_NOTICE, later E_WARNING
$obj->undeclared = 1;  // passes silently without reporting
$obj->unknownMethod(); // Fatal error (not catchable by try/catch)

Une erreur fatale terminait l'application sans possibilité de réagir. L'écriture silencieuse dans des membres inexistants sans avertissement pouvait entraîner des erreurs graves difficiles à détecter. Nette\Object Tous ces cas ont été détectés et une exception MemberAccessException a été levée.

echo $obj->undeclared;   // throw Nette\MemberAccessException
$obj->undeclared = 1;    // throw Nette\MemberAccessException
$obj->unknownMethod();   // throw Nette\MemberAccessException

Depuis PHP 7.0, PHP ne provoque plus d'erreurs fatales non capturables, et l'accès à des membres non déclarés est un bug depuis PHP 8.2.

Voulez-vous dire ?

Si une erreur Nette\MemberAccessException était déclenchée, peut-être à cause d'une faute de frappe lors de l'accès à une variable d'objet ou de l'appel d'une méthode, Nette\Object tentait de donner un indice dans le message d'erreur sur la façon de corriger l'erreur, sous la forme de l'addendum emblématique „Did you mean ?“.

class Foo extends Nette\Object
{
	public static function from($var)
	{
	}
}

$foo = Foo::form($var);
// throw Nette\MemberAccessException
// "Call to undefined static method Foo::form(), did you mean from()?"

Bien que le PHP d'aujourd'hui n'ait pas de fonction „did you mean ?“, cette phrase peut être ajoutée aux erreurs par Tracy. Il peut même corriger automatiquement ces erreurs.

Méthodes d'extension

Inspiré par les méthodes d'extension de C#. Elles donnaient la possibilité d'ajouter de nouvelles méthodes à des classes existantes. Par exemple, vous pouvez ajouter la méthode addDateTime() à un formulaire pour ajouter votre propre DateTimePicker.

Form::extensionMethod(
	'addDateTime',
	fn(Form $form, string $name) => $form[$name] = new DateTimePicker,
);

$form = new Form;
$form->addDateTime('date');

Les méthodes d'extension se sont avérées peu pratiques car leurs noms n'étaient pas autocomplétés par les éditeurs, qui signalaient au contraire que la méthode n'existait pas. Par conséquent, leur prise en charge a été abandonnée.

Obtention du nom de la classe

$class = $obj->getClass(); // using Nette\Object
$class = $obj::class;      // since PHP 8.0

Accès à la réflexion et aux annotations

Nette\Object a proposé un accès à la réflexion et aux annotations en utilisant les méthodes getReflection() et getAnnotation():

/**
 * @author John Doe
 */
class Foo extends Nette\Object
{
}

$obj = new Foo;
$reflection = $obj->getReflection();
$reflection->getAnnotation('author'); // returns 'John Doe'

Depuis PHP 8.0, il est possible d'accéder aux méta-informations sous forme d'attributs :

#[Author('John Doe')]
class Foo
{
}

$obj = new Foo;
$reflection = new ReflectionObject($obj);
$reflection->getAttributes(Author::class)[0];

Les récupérateurs de méthodes

Nette\Object offraient un moyen élégant de traiter les méthodes comme s'il s'agissait de variables :

class Foo extends Nette\Object
{
	public function adder($a, $b)
	{
		return $a + $b;
	}
}

$obj = new Foo;
$method = $obj->adder;
echo $method(2, 3); // 5

Depuis PHP 8.1, vous pouvez utiliser la syntaxe:https://www.php.net/…lable_syntax dite first-class callable:

$obj = new Foo;
$method = $obj->adder(...);
echo $method(2, 3); // 5

Événements

Nette\Object a offert un sucre syntaxique pour déclencher l'événement:

class Circle extends Nette\Object
{
	public array $onChange = [];

	public function setRadius(float $radius): void
	{
		$this->onChange($this, $radius);
		$this->radius = $radius;
	}
}

Le code $this->onChange($this, $radius) est équivalent à ce qui suit :

foreach ($this->onChange as $callback) {
	$callback($this, $radius);
}

Pour des raisons de clarté, nous vous recommandons d'éviter la méthode magique $this->onChange(). Un bon substitut est Nette\Utils\Arrays::invoke:

Nette\Utils\Arrays::invoke($this->onChange, $this, $radius);